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:
@@ -0,0 +1,98 @@
|
||||
'use client'
|
||||
|
||||
import { useState } from 'react'
|
||||
import { useRouter } from 'next/navigation'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { FormRenderer } from '@/components/forms/form-renderer'
|
||||
import { submitForm } from '@/lib/actions'
|
||||
import type { FormField } from '@/lib/forms-schema'
|
||||
|
||||
// Authenticated member fill surface for members-only / in-app forms. The
|
||||
// member is already signed in, so submitForm's visibility check passes;
|
||||
// submission is by formId (slugs are per-space now).
|
||||
export function MemberFillClient({
|
||||
formId,
|
||||
description,
|
||||
kind,
|
||||
legalText,
|
||||
fields,
|
||||
}: {
|
||||
formId: string
|
||||
description: string | null
|
||||
kind: string
|
||||
legalText: string | null
|
||||
fields: FormField[]
|
||||
}) {
|
||||
const router = useRouter()
|
||||
const [values, setValues] = useState<Record<string, unknown>>({})
|
||||
const [consent, setConsent] = useState(false)
|
||||
const [submitting, setSubmitting] = useState(false)
|
||||
const [error, setError] = useState<string | null>(null)
|
||||
const [done, setDone] = useState(false)
|
||||
|
||||
const isWaiver = kind === 'waiver'
|
||||
|
||||
async function submit() {
|
||||
setError(null)
|
||||
setSubmitting(true)
|
||||
const res = await submitForm({
|
||||
formId,
|
||||
answers: values,
|
||||
consent: isWaiver ? consent : undefined,
|
||||
})
|
||||
setSubmitting(false)
|
||||
if ('error' in res && res.error) {
|
||||
setError(res.error)
|
||||
return
|
||||
}
|
||||
setDone(true)
|
||||
}
|
||||
|
||||
if (done) {
|
||||
return (
|
||||
<div className="space-y-4 rounded-lg border p-8 text-center">
|
||||
<h2 className="text-xl font-semibold">Thank you</h2>
|
||||
<p className="text-sm text-muted-foreground">Your response has been recorded.</p>
|
||||
<Button variant="secondary" onClick={() => router.push('/my-forms')}>
|
||||
Back to forms
|
||||
</Button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
{description && <p className="text-muted-foreground">{description}</p>}
|
||||
|
||||
{isWaiver && legalText && (
|
||||
<div className="max-h-72 overflow-y-auto whitespace-pre-wrap rounded-lg border bg-muted/30 p-4 text-sm">
|
||||
{legalText}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<FormRenderer
|
||||
fields={fields}
|
||||
values={values}
|
||||
onChange={(k, v) => setValues(p => ({ ...p, [k]: v }))}
|
||||
/>
|
||||
|
||||
{isWaiver && (
|
||||
<label className="flex items-start gap-2 text-sm">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={consent}
|
||||
onChange={e => setConsent(e.target.checked)}
|
||||
className="mt-1"
|
||||
/>
|
||||
I have read and agree to the terms above.
|
||||
</label>
|
||||
)}
|
||||
|
||||
{error && <p className="text-sm text-destructive">{error}</p>}
|
||||
|
||||
<Button onClick={submit} disabled={submitting}>
|
||||
{submitting ? 'Submitting…' : 'Submit'}
|
||||
</Button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user