this repo has no description
0
fork

Configure Feed

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

fix(public-form-runner): correct remaining step count

+84 -25
+27
components/form-builder-panels.tsx
··· 84 84 completionMessage: string; 85 85 completionLinkLabel: string; 86 86 completionLinkUrl: string; 87 + showProgress: boolean; 87 88 slug: string; 88 89 }; 89 90 ··· 313 314 </div> 314 315 </div> 315 316 </div> 317 + </div> 318 + 319 + <div className="space-y-5 border-t border-[color:var(--line)] pt-6"> 320 + <div> 321 + <p className="text-xs font-semibold uppercase text-[var(--accent)]"> 322 + {t("builder.appearance")} 323 + </p> 324 + <p className="mt-2 text-sm leading-6 text-[var(--muted)]"> 325 + {t("builder.appearanceDescription")} 326 + </p> 327 + </div> 328 + 329 + <label className="flex items-start gap-3 text-sm text-[var(--muted)]"> 330 + <Checkbox 331 + checked={metadataDraft.showProgress} 332 + onChange={(event) => 333 + setMetadataDraft((current) => ({ 334 + ...current, 335 + showProgress: event.target.checked, 336 + })) 337 + } 338 + /> 339 + <span className="font-medium text-[var(--ink)]"> 340 + {t("builder.showProgress")} 341 + </span> 342 + </label> 316 343 </div> 317 344 318 345 <div className="space-y-5 border-t border-[color:var(--line)] pt-6">
+3
components/form-builder.tsx
··· 258 258 completionMessage: initialForm.completionMessage, 259 259 completionLinkLabel: initialForm.completionLinkLabel ?? "", 260 260 completionLinkUrl: initialForm.completionLinkUrl ?? "", 261 + showProgress: initialForm.showProgress, 261 262 slug: initialForm.slug, 262 263 }); 263 264 ··· 309 310 completionMessage: form.completionMessage, 310 311 completionLinkLabel: form.completionLinkLabel ?? "", 311 312 completionLinkUrl: form.completionLinkUrl ?? "", 313 + showProgress: form.showProgress, 312 314 slug: form.slug, 313 315 }); 314 316 }, [ ··· 317 319 form.completionMessage, 318 320 form.completionLinkLabel, 319 321 form.completionLinkUrl, 322 + form.showProgress, 320 323 form.slug, 321 324 ]); 322 325
+39 -25
components/public-form-runner.tsx
··· 97 97 const nextBlockId = currentBlock 98 98 ? resolveNextBlockId(form.blocks, currentBlock.id, answers) 99 99 : null; 100 - const visibleTotal = Math.max( 101 - cursor + 1, 102 - currentBlock && cursor === history.length - 1 && nextBlockId 103 - ? history.length + 1 104 - : history.length || 1, 105 - ); 100 + const visibleTotal = useMemo(() => { 101 + if (!currentBlockId) { 102 + return 1; 103 + } 104 + 105 + let total = cursor + 1; 106 + let nextId = resolveNextBlockId(form.blocks, currentBlockId, answers); 107 + let safetyCounter = 0; 108 + 109 + while (nextId && safetyCounter < form.blocks.length) { 110 + total += 1; 111 + nextId = resolveNextBlockId(form.blocks, nextId, answers); 112 + safetyCounter += 1; 113 + } 114 + 115 + return Math.max(total, cursor + 1); 116 + }, [answers, currentBlockId, cursor, form.blocks]); 106 117 const progress = useMemo( 107 118 () => ((cursor + 1) / visibleTotal) * 100, 108 119 [cursor, visibleTotal], ··· 520 531 </h1> 521 532 </div> 522 533 523 - <div className="w-full max-w-xs shrink-0"> 524 - <div className="flex items-center justify-between gap-3"> 525 - <span className="inline-flex items-center rounded-full bg-[var(--accent-soft)] px-3 py-1 text-[11px] font-semibold uppercase tracking-[0.18em] text-[var(--accent-ink)]"> 526 - {t("publicRunner.step", { 527 - current: cursor + 1, 528 - total: visibleTotal, 529 - })} 530 - </span> 531 - <span className="text-xs font-medium uppercase tracking-[0.2em] text-[var(--muted)]"> 532 - {Math.round(progress)}% 533 - </span> 534 + {form.showProgress ? ( 535 + <div className="w-full max-w-xs shrink-0"> 536 + <div className="flex items-center justify-between gap-3"> 537 + <span className="inline-flex items-center rounded-full bg-[var(--accent-soft)] px-3 py-1 text-[11px] font-semibold uppercase tracking-[0.18em] text-[var(--accent-ink)]"> 538 + {t("publicRunner.step", { 539 + current: cursor + 1, 540 + total: visibleTotal, 541 + })} 542 + </span> 543 + <span className="text-xs font-medium uppercase tracking-[0.2em] text-[var(--muted)]"> 544 + {Math.round(progress)}% 545 + </span> 546 + </div> 547 + <div className="mt-3 h-2 overflow-hidden rounded-full bg-[var(--bg-strong)]"> 548 + <motion.div 549 + className="h-full rounded-full bg-[var(--accent)]" 550 + initial={{ width: 0 }} 551 + animate={{ width: `${progress}%` }} 552 + transition={{ type: "spring", stiffness: 130, damping: 20 }} 553 + /> 554 + </div> 534 555 </div> 535 - <div className="mt-3 h-2 overflow-hidden rounded-full bg-[var(--bg-strong)]"> 536 - <motion.div 537 - className="h-full rounded-full bg-[var(--accent)]" 538 - animate={{ width: `${progress}%` }} 539 - transition={{ type: "spring", stiffness: 130, damping: 20 }} 540 - /> 541 - </div> 542 - </div> 556 + ) : null} 543 557 </div> 544 558 </div> 545 559
+2
lib/form-types.ts
··· 44 44 completionMessage: string; 45 45 completionLinkLabel: string | null; 46 46 completionLinkUrl: string | null; 47 + showProgress: boolean; 47 48 slug: string; 48 49 status: FormStatus; 49 50 updatedAt: string; ··· 60 61 completionMessage: string; 61 62 completionLinkLabel: string | null; 62 63 completionLinkUrl: string | null; 64 + showProgress: boolean; 63 65 slug: string; 64 66 blocks: PublicBlock[]; 65 67 };
+3
lib/forms.ts
··· 164 164 completionMessage: form.completionMessage, 165 165 completionLinkLabel: form.completionLinkLabel, 166 166 completionLinkUrl: form.completionLinkUrl, 167 + showProgress: form.showProgress, 167 168 slug: form.slug, 168 169 status: form.status, 169 170 updatedAt: form.updatedAt.toISOString(), ··· 182 183 completionMessage: form.completionMessage, 183 184 completionLinkLabel: form.completionLinkLabel, 184 185 completionLinkUrl: form.completionLinkUrl, 186 + showProgress: form.showProgress, 185 187 slug: form.slug, 186 188 blocks: form.blocks.map(serializeBlock), 187 189 }; ··· 875 877 description: "", 876 878 completionTitle: copy.completionTitle, 877 879 completionMessage: copy.completionMessage, 880 + showProgress: true, 878 881 slug, 879 882 blocks: { 880 883 create: initialBlocks(locale),
+1
lib/validators.ts
··· 11 11 completionMessage: z.string().trim().max(600).default(""), 12 12 completionLinkLabel: z.string().trim().max(80).default(""), 13 13 completionLinkUrl: z.string().trim().max(2048).default(""), 14 + showProgress: z.boolean().default(true), 14 15 slug: z 15 16 .string() 16 17 .trim()
+3
locales/en.yml
··· 140 140 followUpUrlPlaceholder: https://example.com/next 141 141 publicRoute: Public route 142 142 publicRouteHelp: Published forms accept anonymous submissions. Changes to a published form go live immediately. 143 + appearance: Appearance 144 + appearanceDescription: Control what respondents see while moving through the form. 145 + showProgress: Show form progress 143 146 copyLink: Copy link 144 147 openRunner: Open runner 145 148 deleteForm: Delete form
+3
locales/ru.yml
··· 140 140 followUpUrlPlaceholder: https://example.com/next 141 141 publicRoute: Публичный доступ 142 142 publicRouteHelp: Опубликованные формы принимают анонимные ответы. Изменения в опубликованной форме становятся доступны сразу. 143 + appearance: Внешний вид 144 + appearanceDescription: Управляйте тем, что респонденты видят во время прохождения формы. 145 + showProgress: Показывать прогресс формы 143 146 copyLink: Скопировать ссылку 144 147 openRunner: Открыть раннер 145 148 deleteForm: Удалить форму
+2
prisma/migrations/20260414103000_add_form_progress_visibility/migration.sql
··· 1 + ALTER TABLE "Form" 2 + ADD COLUMN "showProgress" BOOLEAN NOT NULL DEFAULT true;
+1
prisma/schema.prisma
··· 95 95 completionMessage String @default("") 96 96 completionLinkLabel String? 97 97 completionLinkUrl String? 98 + showProgress Boolean @default(true) 98 99 slug String @unique 99 100 status FormStatus @default(DRAFT) 100 101 user User @relation(fields: [userId], references: [id], onDelete: Cascade)