=== Flexa Local Services ===
Contributors: flexatech
Tags: local business, contact form, leads, services, testimonials
Requires at least: 6.4
Tested up to: 7.1
Requires PHP: 8.1
Stable tag: 1.1.1
License: GPL-2.0-or-later
License URI: https://www.gnu.org/licenses/gpl-2.0.html

Service pages, areas, projects and testimonials for a local business, plus a lead form that records which page each enquiry came from.

== Description ==

A plumber's website usually ends up running three plugins: one for the contact form, one to lay out services and testimonials, one for local business schema. None of them knows about the others. So the alert that lands in the owner's inbox says a form was submitted, and leaves out the thing they actually want to know, which job the caller is asking about.

Flexa Local Services is those three jobs in one plugin, wired together. The form knows which page it was sitting on, so every lead arrives labelled "Emergency Plumbing" or "Drain Cleaning" rather than "Contact form". Hitting reply on the alert email goes to the customer, not to the website. Both of those are small things that decide whether the owner keeps the plugin after week one.

Everything lives under one **Flexa Local** menu: the content you publish, the leads that come in, the forms that collect them, and the settings they share.

= What you get on day one =

* A setup checklist on the dashboard that tracks five real steps and does not tick itself.
* A starter site for nine trades, so one click fills an empty plugin with example content you can edit.
* Leads listed newest first, with the page, the phone number and the email on the same row.
* Reply and Call buttons on each lead, so the follow-up happens from a phone.
* Email alerts that quote the visitor's own answers under the question they were asked.
* CSV export, with a UTF-8 marker so Excel reads accented names correctly, and spreadsheet formulas defused so an export cannot run anything on your machine.

= A starter site, if you want one =

An empty plugin is hard to judge. Pick a trade on the Tools screen, press Import, and you get a worked example of a whole site: home, services, pricing, service areas, recent work, reviews, about, FAQ, contact and a quote page, each one assembled from the blocks. Plumbing, electrical, HVAC, roofing and locksmith get an emergency call-out page as well, because those are the trades whose customers go looking for one at midnight.

Behind the pages sit six services with starting prices, six service areas, six testimonials, eight FAQs, three team members, three finished jobs, three offers and a quote form wired to the leads table.

Nine trades are there to pick from: plumbing, electrical, HVAC, roofing, locksmith, appliance repair, pest control, cleaning and landscaping. Everything is sample text about an invented company in Portland, written to be plausible rather than impressive, so edit the prices and the phone number before you launch. No images are bundled.

The pages arrive as drafts, so nothing shows up on your site until you publish them.

Publishing is one tick, and two more sit next to it: Home becomes the front page of the site, and a navigation menu is built from the page set and hung in the first free slot your theme offers. Both are on by default, and both need the pages published, so they sit out an import that comes in as drafts. The company name, phone, email and address are filled in from the sample data either way, but only where you have not set them yet. Nothing you already filled in is overwritten, and a menu your theme is already showing is left where it is. On a block theme the menu is still created, ready to drop into a Navigation block.

One button on the same screen takes the whole import back out again, by ID. The front page setting and the menu location go back the way they were, anything you edited in the meantime is kept, and no lead is ever touched.

= Content types =

Each one is a normal WordPress post type, so the editor, revisions, featured images and search all behave the way you expect.

* **Services** with a starting price, hero text, a call to action and optional JSON-LD schema
* **Service areas** with city, state, ZIP codes and coordinates, so visitors can tell whether you cover them
* **Projects** for finished work, with before and after images, cost, duration and the service and area they belong to
* **Testimonials** with a star rating, the customer's name, a photo and an optional video
* **Team** members with a role, years of experience, phone, email and photo
* **FAQs**, grouped by category and attachable to a service
* **Coupons** with a code, a fixed or percentage discount and an expiry date

= Blocks =

Twelve blocks render your content on the front end. They are server rendered, so a published page always shows the current data, not a copy frozen at the time you saved it.

Services Grid, Areas Grid, Projects, Testimonials, Team, FAQ, Coupons, Pricing, Hero, CTA, Contact Card and Form.

= Lead capture =

Build a form in the Forms screen, drop the Form block (or the `[flexa_local_form id="1"]` shortcode) on any page, and submissions land in the Leads screen.

* Each lead records the page it came from, which is the whole point of reusing one form across a site.
* Works with JavaScript switched off. With JavaScript on, it submits without reloading the page.
* A honeypot field and a timing check reject bots, so visitors never meet a CAPTCHA.
* Email alerts to one address or several, with Reply-To set to the customer.
* Mark a lead new, contacted, won or lost.

= For developers =

* REST API under `fxlc/v1` for content, forms, entries, leads and settings.
* Hooks for the pieces you are most likely to want to change: `fxlc_lead_notification`, `fxlc_form_spam_check`, `fxlc_form_public_submit`, `fxlc_settings_defaults`, `fxlc_admin_menu`, `fxlc_demo_packs`, `fxlc_demo_pages` and more.
* Leads and form submissions live in their own tables, so a large volume of them never slows down your posts list.
* No external services. Nothing is sent anywhere except the lead emails you configure, through WordPress's own mail function.

== Source code ==

One file in this plugin is compiled: the admin app bundle under
`assets/dist/`, built from `apps/admin/src/` (TypeScript and React). The
readable source ships inside the plugin, next to the build it produces, so
nothing has to be fetched to inspect or rebuild it.

Everything else is already readable as shipped. `assets/js/*.js` and
`assets/css/*.css` are hand-written, with no build step and no minified
variant, and all of the PHP is plain source under `app/`.

To rebuild the bundle from the shipped source, in the plugin folder:

1. Install Node 20 or newer and pnpm 10 (`npm install -g pnpm`).
2. `pnpm install`
3. `pnpm build`

That runs TypeScript against `apps/admin/tsconfig.json` and then Vite against
`apps/admin/vite.config.ts`, writing the bundle and its manifest back into
`assets/dist/`. The plugin reads `assets/dist/.vite/manifest.json` to find the
hashed filenames, so a rebuild needs no change in PHP. `pnpm type-check` and
`pnpm test` run the type checker and the unit tests on their own.

The build tools are pinned in the `package.json` and `pnpm-lock.yaml` that ship
with the plugin: Vite 6, TypeScript 5, React 18, Tailwind CSS 4.

== Installation ==

1. Upload the plugin through Plugins > Add New, or copy the folder to `wp-content/plugins/`.
2. Activate it. The database tables for forms, submissions and leads are created on activation.
3. Open **Flexa Local** in the admin menu and work down the dashboard checklist.
4. Fill in Settings with your business name and phone, then add your first service.
5. Create a form under Forms, add the Form block to a page, and send yourself a test submission.

== Frequently Asked Questions ==

= Do I need a specific theme? =

No. The blocks use your theme's colors and fonts where they can, and ship with enough styling of their own to look right in any block theme.

= Can I see what the plugin looks like with content in it? =

Yes. **Flexa Local > Tools** has a starter site for nine trades. Importing one creates example services, areas, testimonials, FAQs, team members, finished jobs, offers, a quote form and the ten or eleven pages that make up a service website, all editable like anything else you write. The same screen deletes them again, and only the items the import created.

= How does it know which page a lead came from? =

The form block writes the ID of the page it is rendered on into the submission. The Leads screen, the alert email and the CSV export all show that page, so one form reused across twenty service pages still tells you which service was being read.

= Who on my site can see the leads? =

Anyone who can edit other people's posts, which means editors and administrators. Authors and contributors can write and edit the service pages, areas and testimonials, but the Leads, Forms and Emails screens do not appear for them, and the REST routes behind those screens turn them away. If that is the wrong line for your site, the `fxlc_capabilities_can_manage_leads` filter moves it.

= Where do form submissions go? =

Into two custom database tables, readable in the Leads screen. They are not emailed anywhere unless you turn on notifications in Settings, and they are never sent to a third party service.

= Can I keep my existing contact form plugin? =

Yes. The content types and blocks work on their own, so skip the Forms and Leads screens. You lose the page attribution, which only works through the form in this plugin.

= Does the form work if a visitor has JavaScript disabled? =

Yes. The form is a real HTML form that posts to WordPress and redirects back to the page with a confirmation. JavaScript only upgrades it to submit in place.

= How do I stop spam without a CAPTCHA? =

Two checks run on every public submission: a hidden field that humans never fill in, and the time between the page rendering and the form arriving. Both are tunable with the `fxlc_form_spam_check` and `fxlc_form_spam_min_seconds` filters.

= What happens to my data if I delete the plugin? =

Settings are removed. The tables holding your leads and submissions are left alone, so deleting the plugin by accident does not destroy them. Drop those tables yourself if you want a clean slate.

= Is the admin area translatable? =

Yes, every string goes through the `flexa-local-services` text domain.

== Screenshots ==

1. The Leads screen. Every lead carries the page the visitor was reading when they got in touch.
2. One lead in full, with Reply and Call buttons and the answers under the questions you asked.
3. The dashboard: a five step checklist while you set up, then the leads still waiting for a reply.
4. The Form block on a page in the block editor, with the form, button and thank you message in the sidebar.
5. The form builder. Add a field, pick a type, map it to the name, phone or email column.
6. Settings: business details, where lead emails go, social links and brand colors.

== Changelog ==

= 1.1.1 =

* An email text block whose tokens are all empty is left out of the sent email.
* Post type screens say "No FAQs found." and keep the label's case.

= 1.1.0 =

* First release.
* Seven content types: services, service areas, projects, testimonials, team, FAQs, coupons.
* Twelve blocks to display them.
* Form builder, Form block and `[flexa_local_form]` shortcode, Leads screen, CSV export.
* Starter sites for nine trades, from plumbing to pest control, imported and removed from the Tools screen.
* Leads record the page they were submitted from, shown in the admin, the alert email and the export.
* Email alert on every lead, addressed so that a reply reaches the customer.
* Honeypot and timing spam checks, no CAPTCHA.
* Leads, forms and email templates sit behind a higher capability than the content, so an author cannot read the customer details.
* REST API under `fxlc/v1`.

== Upgrade Notice ==

= 1.1.1 =
Small email and label fixes.

= 1.1.0 =
First release.
