# Workspace review

[Try the local example](/playground/content#workspace-review). This composition
uses the same source for the live view and the example below. Import the Flood
stylesheet once and render it inside FloodRoot. No network, upload, authorization
or persistent save is implemented: acceptance captures native FormData in memory.

## Choose the owner

Section supplies the heading and supporting content. Field owns the Input/Select
associations; Checkbox owns its label and native form value. Button supplies submit
and reset actions. StatusIndicator describes draft/snapshot state without a
handmade dot. Alert announces local acceptance, and DescriptionList shows the
accepted values separately from the current draft.

The rendered form keeps task labels and operation state concise. Example-only
storage and permission limits are documented above; they are not repeated as
badges, helper paragraphs and drawer descriptions in the product composition.

Drawer owns modal focus, scroll locking and dismissal. Its FileList/FileItem rows
reuse FileMeta and StatusIndicator. The English example supplies an explicit
locale for file metadata, keeping server and browser formatting consistent.
No parallel popup, field or file chip is
implemented in this recipe. Stack, Inline and Surface own the composition's gaps
and containment. An unstyled section wrapper provides the demonstration anchor.

## Apply relevant skills

Read [composition](/agent/references/flood-composition/SKILL.md),
[forms](/agent/references/flood-forms/SKILL.md),
[spacing](/agent/references/flood-spacing/SKILL.md),
[overlays](/agent/references/flood-overlays/SKILL.md) and
[feedback](/agent/references/flood-feedback/SKILL.md) as those tasks arise.
The [skills index](/docs/skills) routes other work without loading every guide.

Controlled values are reset explicitly after checking that native reset was not
cancelled. Browser validity blocks an empty or whitespace-only name before
submission. Unchecked updates are absent from FormData. Changing a draft keeps
the accepted snapshot intact and marks it stale. Closing the drawer never resets
the form. The status uses text as well as color.

## Source

```tsx
"use client";
import {useRef, useState, type FormEvent} from "react";
import {Button, Checkbox, DescriptionList, Drawer, Field, FileItem, FileList,
  Inline, Input, Link, Section, Select, Stack, StatusIndicator, Surface, Text} from "@flood-md/react";

const defaults = {name: "Design workspace", visibility: "private", updates: true};

/** Host state and native form semantics; no persistence or upload transport. */
export function WorkspaceReview() {
  const [draft, setDraft] = useState(defaults);
  const [saved, setSaved] = useState<Record<string, string> | null>(null);
  const [error, setError] = useState("");
  const [filesOpen, setFilesOpen] = useState(false);
  const nameRef = useRef<HTMLInputElement>(null);

  function submit(event: FormEvent<HTMLFormElement>) {
    event.preventDefault();
    const data = new FormData(event.currentTarget);
    setSaved({name: String(data.get("workspace")), visibility: String(data.get("visibility")),
      updates: data.has("updates") ? "On" : "Off"});
    setError("");
  }
  function reset(event: FormEvent<HTMLFormElement>) {
    const nativeEvent = event.nativeEvent;
    queueMicrotask(() => {
      if (nativeEvent.defaultPrevented) return;
      setDraft(defaults); setSaved(null); setError("");
    });
  }
  const dirty = saved !== null && (saved.name !== draft.name || saved.visibility !== draft.visibility
    || saved.updates !== (draft.updates ? "On" : "Off"));

  return <Surface padding="4">
    <Section title="Workspace settings">
      <Stack gap="6">
        <form aria-label="Workspace review settings" onSubmit={submit} onReset={reset}>
          <Stack gap="4">
            <Field label="Workspace name" required error={error || undefined}>
              {control => <Input {...control} ref={nameRef} name="workspace" required pattern={".*\\S.*"}
                value={draft.name} onChange={event => {setDraft({...draft, name: event.currentTarget.value}); setError("");}}
                onInvalid={() => setError("Enter a workspace name, including at least one non-space character.")} />}
            </Field>
            <Field label="Visibility">
              {control => <Select {...control} name="visibility" value={draft.visibility}
                onChange={event => setDraft({...draft, visibility: event.currentTarget.value})}>
                <option value="private">Only me</option><option value="team">Project team</option>
              </Select>}
            </Field>
            <Checkbox label="Product updates" name="updates" value="yes" checked={draft.updates}
              onChange={event => setDraft({...draft, updates: event.currentTarget.checked})} />
            <Inline gap="3">
              <Button type="submit">Apply changes</Button><Button type="reset" variant="subtle">Reset settings</Button>
              {saved && <StatusIndicator size="sm" tone={!dirty ? "success" : "neutral"}
                label={dirty ? "Unapplied changes" : "Applied"} />}
            </Inline>
          </Stack>
        </form>
        {saved && <Stack gap="3">
          <DescriptionList items={[
            {id:"name", term:"Accepted name", description:saved.name},
            {id:"visibility", term:"Accepted visibility", description:saved.visibility === "team" ? "Project team" : "Only me"},
            {id:"updates", term:"Accepted updates", description:saved.updates},
          ]} />
        </Stack>}
        <Drawer title="Files"
          open={filesOpen} onOpenChange={setFilesOpen} closeLabel="Close project files"
          trigger={props => <Button {...props} variant="subtle">Review files</Button>}
          footer={<Button onClick={() => setFilesOpen(false)}>Done reviewing</Button>}>
          <Stack gap="4"><FileList label="Sample project files">
            <FileItem lang="en" name="Research findings — international expansion and onboarding.pdf" format="PDF" size={2457600}
              description={<StatusIndicator label="Ready for review" tone="info" size="sm" />} />
            <FileItem lang="en" name="Brand guidelines.pdf" format="PDF" size={1048576}
              description={<StatusIndicator label="Reviewed" tone="success" size="sm" />} />
          </FileList></Stack>
        </Drawer>
        <Text as="p" size="helper" tone="subtle"><Link href="/docs/recipes/workspace-review">Read the composition and its checks</Link></Text>
      </Stack>
    </Section>
  </Surface>;
}
```

## Verify in the host

Exercise invalid submit and focus, corrected submit, unchecked values, dirty
drafts, normal/cancelled reset and the accepted snapshot. Open the drawer by
keyboard, dismiss it and verify focus returns to the trigger. Inspect its long
file names and metadata with enlarged text and RTL at a narrow width; local
scrolling must keep all content reachable without page overflow. These checks
support this recipe; they do not certify every possible host composition.
