# Submit a service request

Grouped fields, draft saving, and final validation.

Source: https://withastro-utils.github.io/docs/examples/government-form/

Complete the [Forms setup](https://withastro-utils.github.io/docs/guides/forms/getting-started/index.md) first, or use the configuration, middleware, and layout files in the sidebar. Install `@astro-utils/forms` and `@astrojs/node` in an existing Astro project.

## Try the interaction

Save an incomplete draft, then finish the required fields and submit. The preview confirms each action; it does not persist a draft after reload.

### src/layouts/Layout.astro

```astro title="src/layouts/Layout.astro"
---
import { WebForms } from '@astro-utils/forms/forms.js';
---
<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width" />
        <title><slot name="title" /></title>
    </head>
    <body>
        <WebForms>
            <slot />
        </WebForms>
    </body>
</html>
```

### src/pages/application.astro

```astro title="src/pages/application.astro"
---
import { BButton, BInput, BOption, BSelect, BTextarea, Bind, BindForm, FormErrors } from '@astro-utils/forms/forms.js';
import Layout from '../layouts/Layout.astro';

type Form = {
    application: { 
        legalName: string; 
        birthDate: string | Date; service: 
        string; details: string 
    }
};

const bind = Bind<Form>({application: Astro.locals.session.applicationDraft});
let status = '';

function saveDraft() {
    Astro.locals.session.applicationDraft = bind.application;
    status = 'Draft saved.';
}

async function submitApplication() {
    await sendData(bind.application);
    status = 'Application submitted.';

    Astro.locals.session.applicationDraft = undefined;
    bind.application = {}; // clear the form
}
---
<Layout>
    <span slot="title">Submit a service request</span>

    <BindForm {bind}>
        <FormErrors title="Check your application" />

        <fieldset>
            <legend>Applicant details</legend>

            <label for="legalName">Legal name</label>
            <BInput id="legalName" name="application.legalName" autocomplete="name" maxlength={60} required />

            <label for="birthDate">Date of birth</label>
            <BInput id="birthDate" name="application.birthDate" type="date" required />
        </fieldset>

        <fieldset>
            <legend>Request</legend>

            <label for="service">Service</label>
            <BSelect id="service" name="application.service">
                <BOption value="permit">Permit application</BOption>
                <BOption value="record">Public record request</BOption>
            </BSelect>

            <label for="details">Supporting details</label>
            <BTextarea id="details" name="application.details" maxlength={600} required />
        </fieldset>

        <BButton onClick={saveDraft}>Save draft</BButton>
        <BButton onClick={submitApplication} whenFormOK>Submit application</BButton>

        {status && <p role="status">{status}</p>}
    </BindForm>
</Layout>
```

### src/middleware.ts

```ts title="src/middleware.ts"
import forms from '@astro-utils/forms';

export const onRequest = forms();
```

### astro.config.mjs

```js title="astro.config.mjs"
import { defineConfig } from 'astro/config';
import node from '@astrojs/node';
import forms from '@astro-utils/forms/dist/integration.js';

export default defineConfig({
    output: 'server',
    adapter: node({ mode: 'standalone' }),
    integrations: [forms]
});
```

## How it works

In your project, **Save draft** writes a small object to the current session. Reloading the page restores it; submission clears it. This identifies the draft by browser session, not by an authenticated account. For a real service, authenticate the applicant and store drafts durably under their user ID. The demo creates no official request.

## Continue

Review [binding and validation](https://withastro-utils.github.io/docs/guides/forms/data-binding/index.md).
