# Forms (The Multi-Step Quote Form)

## Forms

**Files:** `src/config/config.toml`, `src/content/sections/english/contact-section-two.md` · **Skill level:** Beginner

<div class="border border-dark">
  ![Ecozen contact page with the quote form](/images/ecozen/contact.jpg)
</div>

Ecozen's main form is the **quote request** on `/contact/`, beside a card with your contact details. Ecozen is a static site, so the form sends its answers to a service you choose, which emails them to you or stores them.

The short form in the contact band section and the quote card of the `form` hero send to the same address. The email sign-up in the `newsletter` footer has its own address, in `[settings.newsletter]` (see [Footer](/ecozen/customization/footer/)).

The form has three steps (property, energy use, contact). With JavaScript, visitors see one step at a time, with a progress bar, **Next** and **Back** buttons, and "Step 1 of 3". **Next** checks the required fields of the current step before it moves on. Without JavaScript, all steps show as one long form that still submits.

### Connect The Form
The theme ships with a placeholder address (`https://formsubmit.co/your@email.com`). Until you replace it, requests go nowhere. Pick a provider and set both lines in `config.toml`:

<Tabs>
  <TabItem label="Formsubmit (default)">

    Free, no account needed. Requests arrive by email, as a table of the answers.

    ```toml
    [settings]
      contactFormAction   = "https://formsubmit.co/you@your-domain.com"
      contactFormProvider = "formsubmit.co"
    ```

    The first request you send triggers an **activation email** from Formsubmit. Open it and confirm, or later requests won't be delivered.

  </TabItem>
  <TabItem label="Formspree">

    Create a form at [formspree.io](https://formspree.io/) and copy its endpoint. Requests appear in your Formspree dashboard, and Formspree can email them to you.

    ```toml
    [settings]
      contactFormAction   = "https://formspree.io/f/your-form-id"
      contactFormProvider = "formspree"
    ```

    If Formspree returns an error or doesn't answer within 5 seconds, the form shows `contactForm.fallbackTicketMessage` from `src/i18n/<language>.json`, so put your own email address there.

  </TabItem>
  <TabItem label="Netlify Forms">

    Only when the site is hosted on Netlify. Netlify detects the form at deploy time.

    ```toml
    [settings]
      contactFormAction   = "/"
      contactFormProvider = "netlify"
    ```

    Turn on form detection in the Netlify dashboard (**Forms**), deploy, and send a test request. Submissions appear in the dashboard and can be forwarded by email. Check that your test request arrives there before you rely on the form.

  </TabItem>
</Tabs>

After launch, send one real request from the live site to make sure it arrives.

<Aside type="caution">
  The form posts to the address in `contactFormAction` even while you work with
  `npm run dev`. Use your own address before you test it, and keep test
  requests short.
</Aside>

### Check The Setup
Ecozen checks both form addresses (`contactFormAction` with `contactFormProvider`, and `action` with `provider` in `[settings.newsletter]`) whenever it renders a page that shows that form (the newsletter form shows only in the `newsletter` footer). It warns when an address is empty, is still a placeholder, names a provider the theme doesn't know, or doesn't match the provider, for example a Formspree address with `contactFormProvider = "formsubmit.co"`.

- **In the terminal:** `npm run dev` and `npm run build` print a line that starts with a yellow `[forms]` tag, names the form (`contact` or `newsletter`) and the problem, and says which keys to set.
- **On the dev server:** after you submit the form, a "Dev only · form not connected" note appears inside it with the same reason. It never shows when the page loads, and never on a built or deployed site, so check the terminal before you deploy.

The check knows the shape of each provider's address: `https://formsubmit.co/...`, `https://formspree.io/f/...`, and a path such as `/` for Netlify. A newsletter with `provider = ""` can post to any address, so only a placeholder address is reported there. An empty newsletter `action` hides the sign-up form instead.

### Change The Questions
The form lives in `src/content/sections/english/contact-section-two.md` (and the other language folders), under `form`. The contact page file can override it with a `contactSection` block, but ships with `enable: true` only, so edit the section file.

**Steps.** `form.steps` lists the steps in order. Each field has a `step` number that puts it in that step:

```yaml
form:
  emailSubject: "New quote request from the website"   # subject of the email you receive (Formsubmit and Formspree)
  steps:
    - title: "Property"
      description: ""
    - title: "Energy use"
      description: ""
    - title: "Contact"
      description: ""
  stepNav:
    stepLabel: "Step"     # "Step 1 of 3"
    ofLabel: "of"
    nextButton:
      label: "Next"
      variant: "fill"
    backButton:
      label: "Back"
      variant: "outline"
  submitButton:
    enable: true
    label: "Send my request"
    variant: "fill"
```

To make a one-step form, remove `steps` (or leave one step) and the `step` numbers.

**Fields.** Each item of `inputs` is one field:

```yaml
  inputs:
    - label: "Full name"
      placeholder: "Your name"
      name: "Full name"          # the field name in the request you receive
      autocomplete: "name"
      required: true
      halfWidth: true            # two fields per row on large screens
      step: 3
    - label: "Message"
      tag: "textarea"
      rows: "4"
      name: "Message"
      step: 3
```

**Choices.** Radio buttons and checkboxes are one item per choice. Items with the same `group` are shown together, and the first one carries the `groupLabel` (the question) and `inline: true` (choices in a row):

```yaml
    - label: "Home"
      name: "Property type"
      value: "Home"
      checked: true              # selected by default
      required: true
      groupLabel: "Property type"
      group: "property"
      type: "radio"
      inline: true
      step: 1
    - label: "Business"
      name: "Property type"
      value: "Business"
      required: true
      group: "property"
      type: "radio"
      step: 1
```

| Field type     | How to write it                                                           |
| -------------- | ------------------------------------------------------------------------- |
| Text           | No `type`, or `type: "text"`                                              |
| Email, phone, number | `type: "email"`, `"tel"`, or `"number"`                             |
| Long text      | `tag: "textarea"` and `rows`                                              |
| Radio buttons  | `type: "radio"`, one item per choice, with a shared `group` and `name`    |
| Checkboxes     | `type: "checkbox"`, one item per choice, each with its own `name`         |
| Dropdown       | `dropdown:` with `type` (`select` or `search`) and `items`                |

Other options: `required`, `placeholder`, `autocomplete`, `defaultValue`, and `id`.

The privacy checkbox near the end (`id: "privacy-policy"`) links to your terms and privacy policy in its `label`. Keep it, and keep the links pointing at your real pages.

**Messages.** The last two items of `inputs` are not fields: the one with `note: success` holds the message shown after a request is sent, and the one with `note: deprecated` the message shown when sending fails. Edit their `content` and keep their `parentClass`:

```yaml
    - note: success
      parentClass: "hidden text-sm message success"
      content: "Thank you. We have your request and an energy advisor will contact you within one working day."
    - note: deprecated
      parentClass: "hidden text-sm message error"
      content: "Something went wrong. Please email [hello@example.com](mailto:hello@example.com) and we will follow up."
```

Replace `hello@example.com` there, and in `contactForm.fallbackTicketMessage` in each file in `src/i18n/`, with your own address.

The section file has no schema, so `npm run astro-check` doesn't report a mistyped option. Open `/contact/`, step through the form, and send a test request after each change.

### The Contact Details Card
The card beside the form lists `phone`, `email`, `address`, and `hours` from `[settings.contactInfo]` in `config.toml`, as plain lines with a small label above each value. Each item in `list` of `contact-section-two.md` picks one with `settingFieldName`; its `title` is the label (the `icon` field is kept in the file but not shown). Set `value` on an item to show other text instead, for example translated opening hours in the French file:

```yaml
list:
  - title: "Opening hours"
    icon: "Clock"
    settingFieldName: "hours"
    value: "Lun–Ven, 8h00–18h00"
```

### Do It With AI
```text
Connect the quote form to [Formsubmit with EMAIL | Formspree with form ID | Netlify Forms]: set contactFormAction and
contactFormProvider in src/config/config.toml following its comments. Then change the questions in
contact-section-two.md in every language folder of src/content/sections/ to: step 1 [QUESTIONS], step 2
[QUESTIONS], step 3 [CONTACT FIELDS]. Keep the steps structure, the privacy checkbox, and the two
message items, and replace hello@example.com with [EMAIL] in the messages and in
contactForm.fallbackTicketMessage in every file in src/i18n/.
```