#355 Keep In-Progress Drafts Submittable After a Global Question Is Added - #420
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
b-at-neu
left a comment
There was a problem hiding this comment.
Code Review — Cycle 1 · needs revision
3 open — 1 🔴 Critical, 1 🟠 Medium, 1 🟡 Low
🔴 R1-C1 — Required check Vercel is failing: https://vercel.com/sga-operational-affairs-projects/aplio/ET6FahE8s5wrVxZseaVFTAoZNA1m — resolve the deployment failure before merge (prettier/eslint/tsc checks are green).
Other findings inline.
fa9137b to
50cce97
Compare
b-at-neu
left a comment
There was a problem hiding this comment.
Code Review — Cycle 2 · needs revision
1 open — 1 🔴 Critical
🔴 R2-C1 — Required check Vercel is failing on the latest commit: https://vercel.com/sga-operational-affairs-projects/aplio/57Mwupuag9WQRQAwDHFsFi3yyquD (deployment build/runtime error — npx vercel inspect dpl_57Mwupuag9WQRQAwDHFsFi3yyquD --logs). Fix: resolve the deployment failure before merge; prettier/eslint/tsc are green.
Pipeline EscalationRebasing 1.
|
50cce97 to
5acf6bd
Compare
b-at-neu
left a comment
There was a problem hiding this comment.
Code Review — Cycle 5 · approved
0 open — R4-C1 (Critical) resolved, checks green
the client's readiness check now reads snapshot ?? profile (same as the server), and submitApplication/reopenApplication backfill any missing snapshot rows from the profile inside a transaction before validating, so a question added after draft creation surfaces as an empty required field instead of an unexplained submit failure. Existing drafts also bypass the profile-completeness gate on the apply page, since that gate protects creating an application, not resuming one already in progress. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Fix client/server mismatch for deliberately-cleared global answers: initialGlobalValues and hasNewRequiredGlobals now branch on snapshot row presence, not value length, so a cleared (but existing) answer row no longer falls back to the profile value. Also memoize the per-render derived values (initialGlobalValues, initialPositionValues, hasGlobalRow, hasNewRequiredGlobals) since they only depend on props that don't change after mount. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Condense this PR's comments to the one-line-by-default bar from ENGINEERING.md §7 and drop the #398 provenance citation. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2195acb to
db01637
Compare
Revision — comment-discipline cleanupHuman-directed pass per ENGINEERING.md §7 (post-#421/#418), not a new review cycle — condensed 16 comments across the 4 touched files to one line, kept 1 as a rare 2-line exception (the |
b-at-neu
left a comment
There was a problem hiding this comment.
Code Review — Cycle 6 · approved
0 open — comment-discipline-only revision since the last approval, no regressions
Closes #355
Summary
GlobalApplicationAnswer(the snapshot) the single source of truth on both the client and server, so a required global question added after a draft was created no longer causes the client to say "you're done" while the server rejects the submit.submitApplicationandreopenApplicationnow backfill any snapshot row missing from the caller's current profile inside a transaction before validating — an answer the applicant can already see in their profile is never treated as "unanswered", but a deliberately cleared application-only answer is never resurrected.checkGlobalReadiness) is now shared by both Next and Submit, closes a gap where a position with no position-specific questions could submit from step 1 with no global check at all, and auto-enters Customize mode with the new question rendered as an editable empty required field.Changes
prisma/actions/applications.ts— adds module-privatesyncGlobalAnswersFromProfile(backfill + missing-required-label report) andformatMissingQuestions; rewritessubmitApplicationaround it inside a transaction (now throws'Application not found for caller'on an IDOR-style miss instead of returning{ error: 'Unauthorized' });reopenApplicationreuses the same backfill (position questions are intentionally not re-validated there — that's Design the Position Edit Policy #398's problem).prisma/data/applications.ts— addsgetDraftApplication(userId, positionId), scoped tostatus: 'draft', deletedAt: null.app/(main)/(auth)/positions/[id]/apply/page.tsx— fetches the existing draft first; only applies the profile-completeness gate when there is no draft yet.components/features/application-stepper.tsx— one readiness check (checkGlobalReadiness) driven by form values, shared by Next and Submit; auto-enters Customize when a required global answer is empty (hasNewRequiredGlobals, derived once from initial values, no effect); flushes in-flight autosaves and catches unexpected throws on submit; root error usesrole="alert".Testing plan
/profilestill shows the question unanswered (application-only answer)./profileinstead: return to the draft — the value is prefilled — and submit without touching any field. It succeeds, and aGlobalApplicationAnswerrow now exists for the new question.skipDuplicates).userId).Automated checks
npm run prettier:check— passnpm run eslint:check— passnpm run tsc:check— passNotes
submitApplicationis now a read-then-write, so it runs in a transaction;skipDuplicateson the backfill guards a race between two tabs.syncGlobalAnswersFromProfileon resurrect; it stays transaction-scoped and side-effect-free beyond the backfill.