goodgrief/apps/submission/src/routes/SubmissionRoute.tsx
2026-04-08 10:06:54 -07:00

127 lines
5.3 KiB
TypeScript

import { useSubmissionForm } from "../features/submission/useSubmissionForm";
export const SubmissionRoute = () => {
const { state, progress, status, statusTone, submitting, updateField, submit } = useSubmissionForm();
return (
<main className="submission-shell">
<div className="submission-stage">
<section className="submission-hero">
<p className="submission-kicker">Good Grief</p>
<h1 className="submission-title">Offer a photo to tonight&apos;s memory field.</h1>
<p className="submission-copy">
Share one image that carries memory, witness, humor, or tenderness for you. The creative team will
review each submission. Not every image will appear, and none will be shown without moderation.
</p>
</section>
<section className="submission-card">
<div className="submission-grid">
<div className="submission-field">
<label htmlFor="file">Photo</label>
<div className="submission-file">
<input
id="file"
type="file"
accept="image/jpeg,image/png,image/heic,image/heif"
onChange={(event) => updateField("file", event.target.files?.[0] ?? null)}
/>
<p className="submission-status">
One image only. Common phone photos work best. Unsupported files will be declined.
</p>
</div>
</div>
<div className="submission-field">
<label htmlFor="displayName">Name or initials (optional)</label>
<input
id="displayName"
type="text"
value={state.displayName}
maxLength={80}
onChange={(event) => updateField("displayName", event.target.value)}
/>
</div>
<div className="submission-field">
<label htmlFor="caption">Caption or note (optional)</label>
<textarea
id="caption"
rows={3}
maxLength={180}
placeholder="A short caption, dedication, or line of context."
value={state.caption}
onChange={(event) => updateField("caption", event.target.value)}
/>
</div>
<div className="submission-field">
<label htmlFor="promptAnswer">Optional prompt</label>
<textarea
id="promptAnswer"
rows={4}
maxLength={240}
placeholder="What would you want this image to carry tonight?"
value={state.promptAnswer}
onChange={(event) => updateField("promptAnswer", event.target.value)}
/>
</div>
<div className="submission-checkboxes">
<p className="submission-label">Consent</p>
<label className="submission-checkbox">
<input
type="checkbox"
checked={state.hasRights}
onChange={(event) => updateField("hasRights", event.target.checked)}
/>
<span>I have the right to share this photo, and I understand it may be declined.</span>
</label>
<label className="submission-checkbox">
<input
type="checkbox"
checked={state.allowProjection}
onChange={(event) => updateField("allowProjection", event.target.checked)}
/>
<span>I consent to this image being used in a live theatrical performance.</span>
</label>
<label className="submission-checkbox">
<input
type="checkbox"
checked={state.acknowledgePublicPerformance}
onChange={(event) => updateField("acknowledgePublicPerformance", event.target.checked)}
/>
<span>I understand this is a public performance setting and projection is not guaranteed.</span>
</label>
<label className="submission-checkbox">
<input
type="checkbox"
checked={state.allowArchive}
onChange={(event) => updateField("allowArchive", event.target.checked)}
/>
<span>Optional: you may retain this image briefly after the show for archive review.</span>
</label>
</div>
{submitting ? (
<div className="submission-progress" aria-hidden="true">
<span style={{ width: `${progress}%` }} />
</div>
) : null}
<div className="submission-actions">
<p className="submission-status" data-tone={statusTone}>
{status ??
"This flow is intentionally simple: one image, clear consent, moderated review, no public gallery."}
</p>
<button className="submission-button" type="button" disabled={submitting} onClick={() => void submit()}>
{submitting ? "Uploading..." : "Submit Photo"}
</button>
</div>
</div>
</section>
</div>
</main>
);
};