151 lines
5.6 KiB
TypeScript
151 lines
5.6 KiB
TypeScript
"use client";
|
|
|
|
import { useRouter } from "next/navigation";
|
|
import { useState, type FormEvent } from "react";
|
|
|
|
type FormType = "start" | "ask";
|
|
|
|
type FieldErrors = Record<string, string[] | undefined>;
|
|
|
|
export function SubmissionForm({ type }: { type: FormType }) {
|
|
const router = useRouter();
|
|
const [errors, setErrors] = useState<FieldErrors>({});
|
|
const [status, setStatus] = useState<"idle" | "submitting" | "error">("idle");
|
|
|
|
async function handleSubmit(event: FormEvent<HTMLFormElement>) {
|
|
event.preventDefault();
|
|
setErrors({});
|
|
setStatus("submitting");
|
|
|
|
const form = new FormData(event.currentTarget);
|
|
const payload = Object.fromEntries(form.entries());
|
|
|
|
const response = await fetch("/api/submissions", {
|
|
method: "POST",
|
|
headers: { "Content-Type": "application/json" },
|
|
body: JSON.stringify({
|
|
...payload,
|
|
type,
|
|
consentAccepted: form.get("consentAccepted") === "on",
|
|
sourcePath: type === "start" ? "/start" : "/ask"
|
|
})
|
|
});
|
|
|
|
const result = await response.json().catch(() => ({}));
|
|
|
|
if (!response.ok) {
|
|
setErrors(result.issues || { form: [result.error || "Submission failed"] });
|
|
setStatus("error");
|
|
return;
|
|
}
|
|
|
|
router.push(`/thanks?submission_id=${encodeURIComponent(result.submission_id)}`);
|
|
}
|
|
|
|
return (
|
|
<form className="form-wrap" onSubmit={handleSubmit}>
|
|
<label className="field">
|
|
Name <span className="req">*</span>
|
|
<input className="field-control" name="name" autoComplete="name" required />
|
|
<FieldError errors={errors} name="name" />
|
|
</label>
|
|
<label className="field">
|
|
Email <span className="req">*</span>
|
|
<input className="field-control" name="email" type="email" autoComplete="email" required />
|
|
<FieldError errors={errors} name="email" />
|
|
</label>
|
|
<label className="field">
|
|
LinkedIn {type === "ask" ? <span>(optional)</span> : null}
|
|
<input className="field-control" name="linkedin" type="url" placeholder="https://linkedin.com/in/..." />
|
|
<FieldError errors={errors} name="linkedin" />
|
|
</label>
|
|
|
|
{type === "start" ? <StartFields errors={errors} /> : <AskFields errors={errors} />}
|
|
|
|
<label className="field large checkbox-field">
|
|
<input name="consentAccepted" type="checkbox" required />
|
|
<span>
|
|
I understand that submitting this form does not create an attorney-client relationship or
|
|
constitute legal advice. Paprika is not a law firm and does not provide legal services.
|
|
</span>
|
|
<FieldError errors={errors} name="consentAccepted" />
|
|
</label>
|
|
|
|
{errors.form?.length ? <div className="form-error">{errors.form[0]}</div> : null}
|
|
|
|
<button className="button primary" type="submit" disabled={status === "submitting"}>
|
|
{status === "submitting" ? "Submitting..." : type === "start" ? "Submit Profile" : "Send Question"}
|
|
</button>
|
|
<div className="form-note">
|
|
File upload is intentionally disabled until storage, size limits, access control, and privacy
|
|
handling are approved.
|
|
</div>
|
|
</form>
|
|
);
|
|
}
|
|
|
|
function StartFields({ errors }: { errors: FieldErrors }) {
|
|
return (
|
|
<>
|
|
<label className="field">
|
|
Company Website
|
|
<input className="field-control" name="companyWebsite" type="url" placeholder="https://..." />
|
|
<FieldError errors={errors} name="companyWebsite" />
|
|
</label>
|
|
<label className="field">
|
|
Current Role / Company
|
|
<input className="field-control" name="currentRoleCompany" />
|
|
</label>
|
|
<label className="field">
|
|
Field or Industry
|
|
<input className="field-control" name="fieldOrIndustry" />
|
|
</label>
|
|
<label className="field large start-question-field">
|
|
<strong>
|
|
What brings you to Paprika? <span className="req">*</span>
|
|
</strong>
|
|
<span>
|
|
Tell us where you are in your O-1 journey. Whether you have a specific goal, are building
|
|
evidence early, or are just exploring, a quick summary is enough.
|
|
</span>
|
|
<textarea className="field-control" name="reason" required />
|
|
<FieldError errors={errors} name="reason" />
|
|
</label>
|
|
<label className="field large start-question-field">
|
|
<strong>What recognition have you already built, if any?</strong>
|
|
<span>
|
|
Press, interviews, judging roles, awards, speaking, scholarly articles, patents, funding,
|
|
major projects, or other recognition.
|
|
</span>
|
|
<textarea className="field-control" name="existingRecognition" />
|
|
</label>
|
|
<label className="field large start-question-field">
|
|
<strong>Is there a timeline on your mind?</strong>
|
|
<span>
|
|
Whether it is urgent, months away, or just a future possibility, an approximate timeline
|
|
helps us recommend the right pace.
|
|
</span>
|
|
<textarea className="field-control" name="timeline" />
|
|
</label>
|
|
</>
|
|
);
|
|
}
|
|
|
|
function AskFields({ errors }: { errors: FieldErrors }) {
|
|
return (
|
|
<label className="field large question-field">
|
|
<strong>
|
|
Your question <span className="req">*</span>
|
|
</strong>
|
|
<span>Ask us anything about our services, your situation, or where to start.</span>
|
|
<textarea className="field-control" name="question" required />
|
|
<FieldError errors={errors} name="question" />
|
|
</label>
|
|
);
|
|
}
|
|
|
|
function FieldError({ errors, name }: { errors: FieldErrors; name: string }) {
|
|
const message = errors[name]?.[0];
|
|
return message ? <span className="form-error">{message}</span> : null;
|
|
}
|