this repo has no description
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat: refresh branding and builder UI

+114 -65
+1 -1
app/(creator)/dashboard/page.tsx
··· 52 52 <Card key={form.id} className="p-6"> 53 53 <div className="flex items-start justify-between gap-4"> 54 54 <div> 55 - <Badge className={form.status === "PUBLISHED" ? "bg-[var(--accent-soft)] text-[#2f5d35]" : "bg-black/5"}> 55 + <Badge className={form.status === "PUBLISHED" ? "rounded-full bg-[var(--accent-soft)] text-[#2f5d35]" : "rounded-full bg-black/5"}> 56 56 {form.status === "PUBLISHED" ? "Published" : "Draft"} 57 57 </Badge> 58 58 <h2 className="mt-4 font-display text-3xl text-[var(--ink)]">{form.title}</h2>
+2 -2
app/(creator)/forms/[id]/responses/[responseId]/page.tsx
··· 34 34 <section className="flex flex-col gap-4 border-b border-black/8 pb-5 lg:flex-row lg:items-end lg:justify-between"> 35 35 <div> 36 36 <p className="text-xs font-semibold uppercase tracking-[0.28em] text-[var(--accent)]">Submission replay</p> 37 - <h1 className="mt-3 font-display text-4xl text-[var(--ink)]">{formatDate(response.submittedAt)}</h1> 37 + <h1 className="mt-3 font-display text-4xl text-[var(--ink)]">Submission #{response.submissionNumber} · {formatDate(response.submittedAt)}</h1> 38 38 <p className="mt-2 text-sm leading-6 text-[var(--muted)]">Review submitted answers in form order.</p> 39 39 </div> 40 40 <Link href={`/forms/${id}/responses`}> ··· 65 65 </div> 66 66 <h2 className="mt-4 font-display text-3xl text-[var(--ink)]">{block.title}</h2> 67 67 {block.description ? <p className="mt-3 text-sm leading-7 text-[var(--muted)]">{block.description}</p> : null} 68 - <div className="mt-6 rounded-[24px] border border-black/8 bg-[var(--bg-strong)] px-5 py-4 text-[var(--ink)]"> 68 + <div className="mt-6 rounded-[18px] border border-black/8 bg-[var(--bg-strong)] px-5 py-4 text-[var(--ink)]"> 69 69 {Array.isArray(answer) ? ( 70 70 <div className="flex flex-wrap gap-2"> 71 71 {answer.map((value) => (
+1 -1
app/(creator)/forms/[id]/responses/page.tsx
··· 64 64 <div key={response.id} className="flex flex-col gap-3 px-5 py-4 sm:flex-row sm:items-center sm:justify-between sm:gap-4"> 65 65 <div className="min-w-0"> 66 66 <div className="flex flex-col gap-1 sm:flex-row sm:items-center sm:gap-3"> 67 - <h2 className="font-display text-2xl text-[var(--ink)]">{formatDate(response.submittedAt)}</h2> 67 + <h2 className="font-display text-2xl text-[var(--ink)]">Submission #{response.submissionNumber} · {formatDate(response.submittedAt)}</h2> 68 68 <p className="text-sm text-[var(--muted)]">{response.answerCount} answers</p> 69 69 </div> 70 70 </div>
+22 -2
app/(creator)/layout.tsx
··· 1 + import Image from "next/image"; 1 2 import Link from "next/link"; 2 3 import { redirect } from "next/navigation"; 3 4 ··· 11 12 redirect("/"); 12 13 } 13 14 15 + const firstName = session.user.name?.trim().split(/\s+/)[0] ?? session.user.email?.split("@")[0] ?? "Creator"; 16 + const greetings = [ 17 + `Lovely day, ${firstName}.`, 18 + `Ask away, ${firstName}.`, 19 + `Welcome back, ${firstName}.`, 20 + `How are you, ${firstName}?`, 21 + `Something good starts here, ${firstName}.`, 22 + ]; 23 + const greeting = greetings[Math.floor(Math.random() * greetings.length)]; 24 + 14 25 return ( 15 26 <main className="mx-auto min-h-screen w-full max-w-7xl px-6 py-8 lg:px-10"> 16 27 <header className="mb-8 flex flex-col gap-4 border-b border-black/8 pb-5 lg:flex-row lg:items-center lg:justify-between"> 17 - <div> 18 - <p className="font-display text-2xl text-[var(--ink)]">Hi, {session.user.name ?? session.user.email ?? "Creator"}</p> 28 + <div className="flex items-center gap-4"> 29 + <Link 30 + href="/dashboard" 31 + aria-label="Go to dashboard" 32 + className="shrink-0 transition-transform hover:scale-[1.02]" 33 + > 34 + <Image src="/sproute.png" alt="Lively Forms" width={48} height={48} priority className="size-12" /> 35 + </Link> 36 + <div> 37 + <p className="font-display text-2xl text-[var(--ink)]">{greeting}</p> 38 + </div> 19 39 </div> 20 40 <div className="flex items-center gap-5"> 21 41 <Link href="/dashboard" className="text-sm font-medium text-[var(--muted)] transition hover:text-[var(--ink)]">
app/favicon.ico

This is a binary file and will not be displayed.

+1 -1
app/globals.css
··· 16 16 --color-background: var(--bg); 17 17 --color-foreground: var(--ink); 18 18 --font-sans: var(--font-manrope); 19 - --font-display: var(--font-fraunces); 19 + --font-display: var(--font-kurale); 20 20 } 21 21 22 22 * {
app/icon.png

This is a binary file and will not be displayed.

+5 -4
app/layout.tsx
··· 1 1 import type { Metadata } from "next"; 2 - import { Fraunces, Manrope } from "next/font/google"; 2 + import { Kurale, Manrope } from "next/font/google"; 3 3 import "./globals.css"; 4 4 5 - const fraunces = Fraunces({ 6 - variable: "--font-fraunces", 5 + const kurale = Kurale({ 6 + variable: "--font-kurale", 7 + weight: "400", 7 8 subsets: ["latin"], 8 9 }); 9 10 ··· 24 25 }>) { 25 26 return ( 26 27 <html lang="en"> 27 - <body className={`${fraunces.variable} ${manrope.variable}`}>{children}</body> 28 + <body className={`${kurale.variable} ${manrope.variable}`}>{children}</body> 28 29 </html> 29 30 ); 30 31 }
+8 -10
app/page.tsx
··· 1 + import Image from "next/image"; 1 2 import Link from "next/link"; 2 3 import { ArrowRight } from "lucide-react"; 3 4 ··· 14 15 <section className="flex flex-1 items-center py-16 lg:py-24"> 15 16 <Card className="w-full p-8 lg:p-10"> 16 17 <div className="max-w-2xl"> 17 - <p className="text-xs font-semibold uppercase tracking-[0.28em] text-[var(--accent)]">Lively Forms</p> 18 - <h1 className="mt-4 font-display text-5xl leading-tight text-[var(--ink)] sm:text-6xl"> 19 - Create, publish, and review forms. 20 - </h1> 21 - <p className="mt-5 text-base leading-7 text-[var(--muted)] sm:text-lg"> 22 - Lively Forms is a focused tool for building conversational forms, sharing them publicly, and reviewing 23 - anonymous responses. 18 + <div className="flex items-center gap-4"> 19 + <Image src="/sproute.png" alt="Lively Forms" width={72} height={72} priority className="size-18" /> 20 + <h1 className="font-display text-5xl leading-none text-[var(--ink)] sm:text-6xl">Lively Forms</h1> 21 + </div> 22 + <p className="mt-6 max-w-xl text-base leading-7 text-[var(--muted)] sm:text-lg"> 23 + Like a seed in good soil, every thoughtful question has the power to make something grow. Build forms 24 + that feel alive—gentle invitations to reflect, respond, and uncover what matters most. 24 25 </p> 25 26 26 27 <div className="mt-8 flex flex-col gap-3 sm:flex-row"> ··· 36 37 )} 37 38 </div> 38 39 39 - <div className="mt-10 border-t border-black/8 pt-6 text-sm text-[var(--muted)]"> 40 - <p>Core workflow: create a form, publish it, and review responses.</p> 41 - </div> 42 40 </div> 43 41 </Card> 44 42 </section>
+26 -19
components/form-builder.tsx
··· 147 147 <> 148 148 {dragHandle} 149 149 <button className="min-w-0 flex-1 text-left" type="button" onClick={() => onSelect(block.id)}> 150 - <div className="flex items-center gap-2 text-[var(--muted)]"> 151 - <Icon className="size-4" /> 152 - <span className="text-xs font-semibold uppercase tracking-[0.22em]">{blockLabels[block.type]}</span> 150 + <div className="flex items-center justify-between gap-2 text-[var(--muted)]"> 151 + <div className="flex min-w-0 items-center gap-1.5"> 152 + <Icon className="size-3.5 shrink-0" /> 153 + <span className="truncate text-[10px] font-semibold uppercase tracking-[0.18em]">{blockLabels[block.type]}</span> 154 + </div> 155 + <span className="inline-flex size-5 shrink-0 items-center justify-center rounded-full bg-[var(--bg-strong)] text-[10px] font-semibold text-[var(--ink)]"> 156 + {block.position + 1} 157 + </span> 153 158 </div> 154 - <p className="mt-2 truncate font-medium text-[var(--ink)]">{getBlockPreview(block)}</p> 155 - <p className="mt-1 truncate text-xs text-[var(--muted)]">{block.description || "No helper copy yet"}</p> 159 + <p className="mt-1 truncate text-sm font-medium text-[var(--ink)]">{getBlockPreview(block)}</p> 156 160 </button> 157 161 </> 158 162 ); ··· 177 181 transition, 178 182 }} 179 183 className={cn( 180 - "group flex items-start gap-3 rounded-[22px] border px-4 py-4 transition", 184 + "group flex items-center gap-2.5 rounded-[16px] border px-3 py-3 transition", 181 185 selected ? "border-[var(--accent)] bg-white shadow-[0_18px_44px_rgba(15,23,42,0.08)]" : "border-black/8 bg-white/65 hover:bg-white", 182 186 )} 183 187 > ··· 186 190 onSelect={onSelect} 187 191 dragHandle={ 188 192 <button 189 - className="mt-1 rounded-full border border-black/8 p-2 text-[var(--muted)] transition hover:bg-black/5" 193 + className="rounded-full border border-black/8 p-1.5 text-[var(--muted)] transition hover:bg-black/5" 190 194 type="button" 191 195 {...attributes} 192 196 {...listeners} 193 197 > 194 - <GripVertical className="size-4" /> 198 + <GripVertical className="size-3.5" /> 195 199 </button> 196 200 } 197 201 /> ··· 211 215 return ( 212 216 <div 213 217 className={cn( 214 - "group flex items-start gap-3 rounded-[22px] border px-4 py-4 transition", 218 + "group flex items-center gap-2.5 rounded-[16px] border px-3 py-3 transition", 215 219 selected ? "border-[var(--accent)] bg-white shadow-[0_18px_44px_rgba(15,23,42,0.08)]" : "border-black/8 bg-white/65 hover:bg-white", 216 220 )} 217 221 > ··· 219 223 block={block} 220 224 onSelect={onSelect} 221 225 dragHandle={ 222 - <div className="mt-1 rounded-full border border-black/8 p-2 text-[var(--muted)]"> 223 - <GripVertical className="size-4" /> 226 + <div className="rounded-full border border-black/8 p-1.5 text-[var(--muted)]"> 227 + <GripVertical className="size-3.5" /> 224 228 </div> 225 229 } 226 230 /> ··· 250 254 ); 251 255 252 256 const [blockDraft, setBlockDraft] = useState<BuilderBlock | null>(selectedBlock); 257 + const SelectedBlockIcon = blockDraft ? blockIcons[blockDraft.type] : null; 253 258 254 259 const sensors = useSensors(useSensor(PointerSensor, { activationConstraint: { distance: 4 } })); 255 260 ··· 446 451 <p className="text-xs font-semibold uppercase tracking-[0.24em] text-[var(--accent)]">Builder</p> 447 452 <div className="mt-3 flex flex-wrap items-center gap-3"> 448 453 <h1 className="font-display text-4xl text-[var(--ink)]">{form.title}</h1> 449 - <Badge className={cn(form.status === "PUBLISHED" && "bg-[var(--accent-soft)] text-[#2f5d35]")}>{form.status}</Badge> 454 + <Badge className={cn("rounded-full", form.status === "PUBLISHED" && "bg-[var(--accent-soft)] text-[#2f5d35]")}>{form.status}</Badge> 450 455 </div> 451 456 <p className="mt-2 text-sm leading-6 text-[var(--muted)]">Edit blocks, update settings, and review responses.</p> 452 457 </div> ··· 552 557 </label> 553 558 </div> 554 559 555 - <div className="grid gap-4 rounded-[28px] border border-black/8 bg-[var(--bg-strong)] p-5 lg:grid-cols-[1fr_auto_auto] lg:items-center"> 560 + <div className="grid gap-4 rounded-[20px] border border-black/8 bg-[var(--bg-strong)] p-5 lg:grid-cols-[1fr_auto_auto] lg:items-center"> 556 561 <div> 557 562 <p className="text-xs font-semibold uppercase tracking-[0.22em] text-[var(--accent)]">Public route</p> 558 563 <p className="mt-2 truncate text-sm text-[var(--ink)]">{shareHref}</p> ··· 586 591 ) : blockDraft ? ( 587 592 <div className="space-y-6"> 588 593 <div className="flex items-start justify-between gap-4"> 589 - <div> 590 - <p className="text-xs font-semibold uppercase tracking-[0.24em] text-[var(--accent)]">Selected block</p> 591 - <h2 className="mt-3 font-display text-4xl text-[var(--ink)]">{blockLabels[blockDraft.type]}</h2> 594 + <div className="flex items-center gap-3"> 595 + {SelectedBlockIcon ? <SelectedBlockIcon className="size-6 text-[var(--ink)]" /> : null} 596 + <h2 className="font-display text-4xl text-[var(--ink)]">{blockLabels[blockDraft.type]}</h2> 592 597 </div> 593 - <Badge>{blockDraft.position + 1}</Badge> 598 + <span className="inline-flex size-8 items-center justify-center rounded-full bg-[var(--bg-strong)] text-sm font-semibold text-[var(--ink)]"> 599 + {blockDraft.position + 1} 600 + </span> 594 601 </div> 595 602 596 603 <div className="grid gap-5"> ··· 663 670 )} 664 671 665 672 {isQuestion(blockDraft.type) ? ( 666 - <label className="flex items-center gap-3 rounded-2xl border border-black/8 bg-[var(--bg-strong)] px-4 py-3 text-sm text-[var(--ink)]"> 673 + <label className="flex items-center gap-3 rounded-xl border border-black/8 bg-[var(--bg-strong)] px-4 py-3 text-sm text-[var(--ink)]"> 667 674 <input 668 675 checked={blockDraft.required} 669 676 className="size-4 rounded border-black/15" ··· 689 696 </div> 690 697 </div> 691 698 ) : ( 692 - <div className="flex min-h-[420px] items-center justify-center rounded-[28px] border border-dashed border-black/10 bg-[var(--bg-strong)] p-10 text-center"> 699 + <div className="flex min-h-[420px] items-center justify-center rounded-[20px] border border-dashed border-black/10 bg-[var(--bg-strong)] p-10 text-center"> 693 700 <div> 694 701 <p className="text-xs font-semibold uppercase tracking-[0.24em] text-[var(--accent)]">Nothing selected</p> 695 702 <h2 className="mt-4 font-display text-4xl text-[var(--ink)]">Select a block or open form settings.</h2>
+2 -2
components/loading-shell.tsx
··· 4 4 <div className="h-4 w-24 animate-pulse rounded-full bg-black/10" /> 5 5 <div className="h-12 w-64 animate-pulse rounded-full bg-black/10" /> 6 6 <div className="grid gap-6 lg:grid-cols-[320px_minmax(0,1fr)]"> 7 - <div className="h-[420px] animate-pulse rounded-[28px] bg-white/70 shadow-[0_22px_60px_rgba(15,23,42,0.08)]" /> 8 - <div className="h-[420px] animate-pulse rounded-[28px] bg-white/70 shadow-[0_22px_60px_rgba(15,23,42,0.08)]" /> 7 + <div className="h-[420px] animate-pulse rounded-[20px] bg-white/70 shadow-[0_22px_60px_rgba(15,23,42,0.08)]" /> 8 + <div className="h-[420px] animate-pulse rounded-[20px] bg-white/70 shadow-[0_22px_60px_rgba(15,23,42,0.08)]" /> 9 9 </div> 10 10 <p className="text-sm text-[var(--muted)]">Loading {title}…</p> 11 11 </div>
+5 -5
components/public-form-runner.tsx
··· 153 153 154 154 return ( 155 155 <Card className="w-full max-w-4xl overflow-hidden bg-[#16120f] p-4 text-white shadow-[0_36px_120px_rgba(22,18,15,0.32)] sm:p-6 lg:p-8"> 156 - <div className="rounded-[28px] border border-white/10 bg-white/[0.03] p-6 sm:p-8"> 156 + <div className="rounded-[20px] border border-white/10 bg-white/[0.03] p-6 sm:p-8"> 157 157 <div className="mb-8 flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between"> 158 158 <div> 159 159 <p className="text-xs font-semibold uppercase tracking-[0.3em] text-[#b9dfb9]">{form.title}</p> ··· 198 198 <div className="mt-10 space-y-4"> 199 199 {currentBlock.type === "SHORT_TEXT" ? ( 200 200 <Input 201 - className="h-14 rounded-[24px] border-white/10 bg-white/6 text-lg text-white placeholder:text-white/28" 201 + className="h-14 rounded-[18px] border-white/10 bg-white/6 text-lg text-white placeholder:text-white/28" 202 202 placeholder={(currentBlock.config as { placeholder: string }).placeholder} 203 203 value={typeof answers[currentBlock.id] === "string" ? answers[currentBlock.id] : ""} 204 204 onChange={(event) => setAnswer(currentBlock.id, event.target.value)} ··· 207 207 208 208 {currentBlock.type === "LONG_TEXT" ? ( 209 209 <Textarea 210 - className="min-h-[180px] rounded-[28px] border-white/10 bg-white/6 text-lg text-white placeholder:text-white/28" 210 + className="min-h-[180px] rounded-[20px] border-white/10 bg-white/6 text-lg text-white placeholder:text-white/28" 211 211 placeholder={(currentBlock.config as { placeholder: string }).placeholder} 212 212 value={typeof answers[currentBlock.id] === "string" ? answers[currentBlock.id] : ""} 213 213 onChange={(event) => setAnswer(currentBlock.id, event.target.value)} ··· 224 224 key={option} 225 225 type="button" 226 226 className={cn( 227 - "flex items-center justify-between rounded-[22px] border px-5 py-4 text-left transition", 227 + "flex items-center justify-between rounded-[16px] border px-5 py-4 text-left transition", 228 228 selected 229 229 ? "border-[#9fd7a4] bg-[#9fd7a4]/16 text-white" 230 230 : "border-white/10 bg-white/4 text-white/78 hover:bg-white/8", ··· 251 251 key={option} 252 252 type="button" 253 253 className={cn( 254 - "flex items-center justify-between rounded-[22px] border px-5 py-4 text-left transition", 254 + "flex items-center justify-between rounded-[16px] border px-5 py-4 text-left transition", 255 255 selected 256 256 ? "border-[#9fd7a4] bg-[#9fd7a4]/16 text-white" 257 257 : "border-white/10 bg-white/4 text-white/78 hover:bg-white/8",
+1 -1
components/ui/badge.tsx
··· 6 6 return ( 7 7 <span 8 8 className={cn( 9 - "inline-flex items-center rounded-full border border-black/8 bg-black/5 px-3 py-1 text-[11px] font-semibold uppercase tracking-[0.18em] text-[var(--muted)]", 9 + "inline-flex items-center rounded-lg border border-black/8 bg-black/5 px-3 py-1 text-[11px] font-semibold uppercase tracking-[0.18em] text-[var(--muted)]", 10 10 className, 11 11 )} 12 12 {...props}
+2 -2
components/ui/button.tsx
··· 4 4 import { cn } from "@/lib/utils"; 5 5 6 6 const buttonVariants = cva( 7 - "inline-flex items-center justify-center gap-2 rounded-full text-sm font-semibold transition-all duration-200 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 ring-offset-[var(--bg)]", 7 + "inline-flex items-center justify-center gap-2 rounded-xl text-sm font-semibold transition-all duration-200 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 ring-offset-[var(--bg)]", 8 8 { 9 9 variants: { 10 10 variant: { ··· 21 21 default: "h-11", 22 22 sm: "h-9 px-4 text-xs", 23 23 lg: "h-12 px-6 text-base", 24 - icon: "h-10 w-10 rounded-full", 24 + icon: "h-10 w-10 rounded-xl", 25 25 }, 26 26 }, 27 27 defaultVariants: {
+1 -1
components/ui/card.tsx
··· 6 6 return ( 7 7 <div 8 8 className={cn( 9 - "rounded-[28px] border border-black/8 bg-white/85 shadow-[0_22px_60px_rgba(15,23,42,0.08)] backdrop-blur-sm", 9 + "rounded-[20px] border border-black/8 bg-white/85 shadow-[0_22px_60px_rgba(15,23,42,0.08)] backdrop-blur-sm", 10 10 className, 11 11 )} 12 12 {...props}
+1 -1
components/ui/input.tsx
··· 8 8 <input 9 9 ref={ref} 10 10 className={cn( 11 - "flex h-11 w-full rounded-2xl border border-black/10 bg-white px-4 py-3 text-sm text-[var(--ink)] shadow-[inset_0_1px_0_rgba(255,255,255,0.5)] outline-none transition focus:border-black/20 focus:ring-2 focus:ring-black/5", 11 + "flex h-11 w-full rounded-xl border border-black/10 bg-white px-4 py-3 text-sm text-[var(--ink)] shadow-[inset_0_1px_0_rgba(255,255,255,0.5)] outline-none transition focus:border-black/20 focus:ring-2 focus:ring-black/5", 12 12 className, 13 13 )} 14 14 {...props}
+1 -1
components/ui/textarea.tsx
··· 8 8 <textarea 9 9 ref={ref} 10 10 className={cn( 11 - "flex min-h-[120px] w-full rounded-3xl border border-black/10 bg-white px-4 py-3 text-sm text-[var(--ink)] shadow-[inset_0_1px_0_rgba(255,255,255,0.5)] outline-none transition focus:border-black/20 focus:ring-2 focus:ring-black/5", 11 + "flex min-h-[120px] w-full rounded-xl border border-black/10 bg-white px-4 py-3 text-sm text-[var(--ink)] shadow-[inset_0_1px_0_rgba(255,255,255,0.5)] outline-none transition focus:border-black/20 focus:ring-2 focus:ring-black/5", 12 12 className, 13 13 )} 14 14 {...props}
+1 -1
components/ui/toast.tsx
··· 38 38 exit={{ opacity: 0, x: 24, y: -8, scale: 0.98 }} 39 39 transition={{ duration: 0.18, ease: "easeOut" }} 40 40 className={cn( 41 - "pointer-events-auto flex items-start gap-3 rounded-2xl border bg-white px-4 py-3 shadow-[0_18px_50px_rgba(15,23,42,0.12)]", 41 + "pointer-events-auto flex items-start gap-3 rounded-xl border bg-white px-4 py-3 shadow-[0_18px_50px_rgba(15,23,42,0.12)]", 42 42 toast.variant === "error" 43 43 ? "border-rose-200 text-rose-700" 44 44 : "border-[#cfe9d0] text-[#2f5d35]",
+34 -11
lib/forms.ts
··· 100 100 id: string; 101 101 submittedAt: string; 102 102 answerCount: number; 103 + submissionNumber: number; 103 104 }; 104 105 105 106 export type ResponseDetail = { 106 107 id: string; 107 108 submittedAt: string; 109 + submissionNumber: number; 108 110 answers: Record<string, string | string[]>; 109 111 blocks: SerializedBlock[]; 110 112 }; ··· 601 603 where: { 602 604 formId, 603 605 }, 604 - orderBy: { 605 - submittedAt: "desc", 606 - }, 606 + orderBy: [{ submittedAt: "asc" }, { id: "asc" }], 607 607 }); 608 608 609 609 return { 610 610 form, 611 - responses: responses.map((response) => ({ 612 - id: response.id, 613 - submittedAt: response.submittedAt.toISOString(), 614 - answerCount: 615 - typeof response.answersJson === "object" && response.answersJson && !Array.isArray(response.answersJson) 616 - ? Object.keys(response.answersJson as Record<string, unknown>).length 617 - : 0, 618 - })) satisfies ResponseListItem[], 611 + responses: responses 612 + .map((response, index) => ({ 613 + id: response.id, 614 + submittedAt: response.submittedAt.toISOString(), 615 + answerCount: 616 + typeof response.answersJson === "object" && response.answersJson && !Array.isArray(response.answersJson) 617 + ? Object.keys(response.answersJson as Record<string, unknown>).length 618 + : 0, 619 + submissionNumber: index + 1, 620 + })) 621 + .reverse() satisfies ResponseListItem[], 619 622 }; 620 623 } 621 624 ··· 647 650 throw new AppError("Response not found.", 404); 648 651 } 649 652 653 + const submissionNumber = await db.response.count({ 654 + where: { 655 + formId, 656 + OR: [ 657 + { 658 + submittedAt: { 659 + lt: response.submittedAt, 660 + }, 661 + }, 662 + { 663 + submittedAt: response.submittedAt, 664 + id: { 665 + lte: response.id, 666 + }, 667 + }, 668 + ], 669 + }, 670 + }); 671 + 650 672 return { 651 673 id: response.id, 652 674 submittedAt: response.submittedAt.toISOString(), 675 + submissionNumber, 653 676 answers: 654 677 typeof response.answersJson === "object" && response.answersJson && !Array.isArray(response.answersJson) 655 678 ? (response.answersJson as Record<string, string | string[]>)
public/sproute.png

This is a binary file and will not be displayed.

sproute.png

This is a binary file and will not be displayed.