feat(forms): obvious publish flow, member Forms surface, templates (P8)
- builder: draft/published/closed status banners; creating a form now
lands on its edit page so Publish is immediately visible; a
members-only form shows a "no public link" note instead of a dead URL
- member-facing forms: /my-forms list + /my-forms/[id] fill
(MemberFillClient), "Forms" sidebar item for all members. Fixes the
long-standing gap where members-only forms had no fill surface
- starter templates: lib/form-templates.ts + a picker on /forms/new
(Liability waiver, Membership application, Event RSVP, Photo release,
Blank) that seeds builder state (no table)
- forms-client relabels members-only forms (was showing a misleading
/f/<space>/<slug> that always 404s for members-only)
- docs corrected per the audit pass: DATABASE_SCHEMA / DB_SCHEMA_MAP /
ARCHITECTURE / API_REFERENCE now reflect migrations 014-029, per-space
slug, /f/[space]/[slug], getPublicForm({space,slug}), invite roles;
HANDOFF pass-19 added with residual gaps
337 tests; build verified (Compiled successfully, exit 0). No deploy.
This commit is contained in:
@@ -135,8 +135,15 @@ export function FormBuilder({ initial }: { initial: BuilderForm }) {
|
||||
toast.error(res.error || 'Could not save')
|
||||
return
|
||||
}
|
||||
toast.success(isEdit ? 'Form saved' : 'Form created')
|
||||
router.push('/forms')
|
||||
toast.success(isEdit ? 'Form saved' : 'Form created — now publish it to make it live')
|
||||
if (isEdit) {
|
||||
router.push('/forms')
|
||||
} else {
|
||||
// Land on the new form's edit page so the Publish step is right there
|
||||
// (creating a form leaves it as a draft).
|
||||
const newId = 'data' in res && res.data ? res.data.id : undefined
|
||||
router.push(newId ? `/forms/${newId}/edit` : '/forms')
|
||||
}
|
||||
router.refresh()
|
||||
}
|
||||
|
||||
@@ -180,6 +187,28 @@ export function FormBuilder({ initial }: { initial: BuilderForm }) {
|
||||
<div className="grid gap-6 lg:grid-cols-2">
|
||||
{/* Editor */}
|
||||
<div className="space-y-6">
|
||||
{isEdit && initial.status === 'draft' && (
|
||||
<div className="rounded-lg border border-amber-500/40 bg-amber-500/10 p-3 text-sm">
|
||||
<strong>Draft.</strong> This form is not visible to anyone yet. Use{' '}
|
||||
<strong>Publish</strong> below to make it live.
|
||||
</div>
|
||||
)}
|
||||
{isEdit && initial.status === 'published' && (
|
||||
<div className="rounded-lg border border-emerald-500/40 bg-emerald-500/10 p-3 text-sm">
|
||||
<strong>Published.</strong> This form is live and accepting responses.
|
||||
</div>
|
||||
)}
|
||||
{isEdit && initial.status === 'closed' && (
|
||||
<div className="rounded-lg border bg-muted/40 p-3 text-sm">
|
||||
<strong>Closed.</strong> No longer accepting responses. Re-publish to reopen.
|
||||
</div>
|
||||
)}
|
||||
{!isEdit && (
|
||||
<div className="rounded-lg border bg-muted/40 p-3 text-sm text-muted-foreground">
|
||||
Build your form, then <strong>Create</strong>. It starts as a draft; you publish it on
|
||||
the next screen.
|
||||
</div>
|
||||
)}
|
||||
{isEdit && (
|
||||
<div className="flex flex-wrap items-center gap-2 rounded-lg border p-3">
|
||||
<Badge variant={initial.status === 'published' ? 'default' : 'secondary'}>
|
||||
@@ -221,6 +250,12 @@ export function FormBuilder({ initial }: { initial: BuilderForm }) {
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
{isEdit && initial.status === 'published' && visibility === 'members' && (
|
||||
<div className="rounded-lg border bg-muted/40 p-3 text-sm text-muted-foreground">
|
||||
Members-only: there is no public link. Members fill this from their{' '}
|
||||
<strong>Forms</strong> page in the app.
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="space-y-3">
|
||||
<SectionTitle>Form details</SectionTitle>
|
||||
|
||||
Reference in New Issue
Block a user