#740 Show Draft Completion Progress Rings - #756
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Pipeline Escalation
|
|
Resolution: show the progress ring and the deadline/closing-soon indicator side by side in the shared trailing cell (both features coexist, neither replaces the other). |
4d16c09 to
90c1c77
Compare
Revision — rebase resolutionRebased onto
|
b-at-neu
left a comment
There was a problem hiding this comment.
Code Review — Cycle 1 · approved
0 open — clean
b-at-neu
left a comment
There was a problem hiding this comment.
Code Review — Cycle 4 · approved
0 open — clean (delta-scoped: verified the docs/WORKFLOWS.md rebase merge against current code — Progress column placement, ProgressRing ordering on all four surfaces, draft-row ⋯ removal — no regressions)
f37a4b9 to
846c9dd
Compare
Revision — Cycle 4rebase only (Cycle 4 review already approved, 0 open) · 846c9dd · rebase: components/features/position-card.tsx (dual independent imports, merged both) · rebase: components/features/applications-table-skeleton.tsx (dropped — file deleted by dev's #720 skeleton refactor, unreferenced by any surface; our commit's 2-line edit to it was dead code with no live equivalent to port) |
b-at-neu
left a comment
There was a problem hiding this comment.
Code Review — Cycle 5 · needs revision
1 open — 1 🟠 Medium (off-diff, see below)
R5-M1 🟠 Medium — ApplicationsResultsSkeleton's columns array (isDraftView branch) still has only 4 entries (Applicant/Position/badge/date) — the shape of the drafts table before this PR added Progress. DRAFT_COLUMNS in applications-table.tsx now renders 5 columns (Applicant, Position, Progress, Started, Last updated), so the /manage/applications?status=draft loading skeleton is missing a column and its widths no longer line up with the real table — a layout shift on resolve (ENGINEERING.md §4). The PR's own plan carried a matching fix (an extra bar in the now-deleted applications-table-skeleton.tsx); it wasn't carried over when the last rebase (846c9dd, dropping that file for the shared DataTableSkeleton) landed. Fix: add a Progress column entry to the isDraftView branch (and drop the stray badge shape there, which belongs to the merged view's Status column, not this dedicated drafts view).
aplio/components/features/applications-results.tsx
Lines 227 to 242 in 846c9dd
Pipeline EscalationThis PR has hit the 5-review cycle cap with the latest cycle still producing a Medium finding, so the pipeline is escalating for human attention rather than dispatching another automated revision. Cycle 5 finding (R5-M1, Medium): Context on why this cycled so many times: most of the prior cycles (2–4) were rebase-only dispatches responding to Recommendation: straightforward fix — add the missing Progress column placeholder to |
Revision — Cycle 5fixed R5-M1 · 2f5ea22 |
b-at-neu
left a comment
There was a problem hiding this comment.
Code Review — Cycle 6 · approved
0 open — clean (delta-scoped: verified R5-M1's fix — Progress column added to the drafts-view loading skeleton with the stray badge shape dropped, matching DRAFT_COLUMNS's 5-column order — no regressions)
Adds a server-side aggregate (getApplicationCompletion) that counts required-question completion for draft applications, reusing isAnswered and resolveGlobalAnswerValues so the ring can never disagree with whether Submit is enabled. A new ProgressRing server primitive renders it on all four surfaces: the reviewer drafts table, the merged applications table, the applicant's own applications table/widget, and the position card. Only three integers ever cross to a client; answer content never does. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Widen ProgressRing's left spacing, reorder the my-applications-widget draft/withdrawn trailing slot (badge -> ring -> deadline), and drop the draft-row status-actions menu on the manage applications table. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add missing Progress-column skeleton to the drafts view so its column count and widths match DRAFT_COLUMNS's 5 real columns. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Revision — Cycle 63b0a084 · rebase: applications-table.tsx (unioned |
b-at-neu
left a comment
There was a problem hiding this comment.
Code Review — Cycle 7 · approved
0 open — clean (delta-scoped: rebase onto updated dev; verified no regressions — R5-M1's skeleton fix survived intact, relocated into the shared DataTableSkeleton column defs by the unrelated #720 refactor; force-withdraw (#755) and skeleton-refactor (#720) diffs are base content, not this PR's own changes)
Closes #740
Summary
getApplicationCompletioninprisma/data/applications.ts) that reusesisAnsweredandresolveGlobalAnswerValues— the same rulessubmitApplicationand the apply-page stepper already use — so the ring can never disagree with whether Submit is enabled.ProgressRingprimitive (components/ui/progress-ring.tsx) and renders it on all four surfaces named in the issue: the reviewer drafts table and the merged "All statuses" table (applications-table.tsx), the applicant's own applications table (my-applications-table.tsx), the home dashboard widget (my-applications-widget.tsx), and the position card (position-card.tsx).answeredCount,requiredCount,percent) ever cross to a client — no answer content, no per-question breakdown. Updates the privacy-contract comment ongetDraftApplications/DraftApplicationListItemto describe the new sibling aggregate, and rewords the reviewer drafts-view privacy line to disclose that a reviewer can see how far along a draft is.docs/WORKFLOWS.mdupdated at AP-1, AP-10, AP-17, and PM-8 to describe the ring on each surface and the reworded privacy line.Changes
lib/types.ts—ApplicationCompletion;completionadded toMyPositionApplication; updated theDraftApplicationListItemcontract comment.lib/utils.ts—calculateAnswerCompletion(questions, values): pure, unit-testable required-question completion count. Zero required → 100% (neverNaN); 100%/0% only at exactly full/no answers, otherwise clamped to 1–99 so rounding can never falsely claim "done" or "nothing started."prisma/data/applications.ts—getApplicationCompletion(applications): five flat queries (never per-row) producingRecord<applicationId, ApplicationCompletion>; resolves globals throughresolveGlobalAnswerValuesso a profile-prefilled draft isn't under-reported. Wired intogetMyApplicationsByPosition; updated thegetDraftApplicationscontract comment.components/ui/progress-ring.tsx— new. Two-circle SVG (stroke-primaryat every value — colour never carries meaning), sized by prop,role="img"with one accessible name ("N% complete"); no'use client', no hooks.components/features/applications-results.tsx— computes completion for the rows actually rendered (both the drafts-only branch and the merged "All statuses" branch, over the sliced/paginated rows only); reworded the privacy line.components/features/applications-table.tsx—completionprop; a Progress column in the drafts view (nosortAccessor— progress isn't inApplicationSortField); ring beside the Draft badge in the merged view's desktop and mobile cards. A row with no completion entry renders nothing, never0%.components/features/applications-table-skeleton.tsx— one extra skeleton bar in the drafts-view header/rows so the new column doesn't shift layout on resolve.app/(main)/(auth)/applications/page.tsx— fetches completion for the caller's own drafts.components/features/my-applications-table.tsx—completionprop; ring beside the Draft badge (desktop + mobile).components/features/my-applications-widget.tsx— a follow-upgetApplicationCompletioncall over the widget's draft rows only (skipped when there are none); the draft row's—becomes the ring.components/features/position-card.tsx— ring beside Continue application, readingmyApplication.completion(now carried onMyPositionApplication).tests/unit/utils.test.ts—calculateAnswerCompletion: none/all answered, rounding + both 1–99 clamps, zero required,file_uploadcounts as answered, an optional question not affecting the denominator, an empty-string value not counting, and a global answered only through the profile fallback.tests/db/draft-completion.test.ts— new. Scope (one entry per draft in the caller's scope, none outside it), zero-required rendering 100% with noNaN, a required position question moving 0% → 100% once answered, and a required global answered only on the applicant's profile reporting 100%.docs/WORKFLOWS.md— AP-1, AP-10, AP-17, PM-8.Testing plan
/applicationsshows the ring beside the Draft badge, the home dashboard widget shows it on that row, and/positionsshows it beside Continue application — all three the same number./manage/applications?status=draftshows the same number in the Progress column, and the "All statuses" view shows it beside the Draft badge on the same row./profile, start a fresh draft and answer only the position questions — the ring counts the profile-backed globals (the trap this ticket calls out).NaN, no blank cell.file_uploadquestion — it counts as answered; remove it and the count drops./manage/applications?status=drafton a full page of drafts and confirm the query count stays flat (5 extra queries regardless of row count).Automated checks
npm run prettier:check— passnpm run eslint:check— passnpm run tsc:check— passnpm run test:unit— 420/420 passing (one pre-existing, unrelated failure:tests/unit/email-delivery-events.test.tsfails to import in this worktree becauseDATABASE_URLisn't set — confirmed viagit diffthat this file and its dependencies are untouched by this change)tests/db/draft-completion.test.tscould not be executed in this sandboxed worktree (no isolated Postgres instance available without risking a shared database used by other concurrent work) — added following the same patterns astests/db/draft-visibility.test.tsandtests/db/application-transitions.test.ts, and reviewed carefully by hand. Please runnpm run testlocally against a dedicated Postgres instance before merging.Notes
isAnsweredrather than forking the rule: the ticket asks that a stored['']not count as answered, butisAnswereddoes count it (viapartitionAnswerValue) for short/long answers. The UI never produces that row (answer-field.tsxwrites[], never['']), andsubmitApplicationuses the same helper — forking here would make the ring disagree with whether Submit is enabled, which is the worse bug. The unit tests pin[]/no-row as unanswered and don't attempt to special-case[''].isAnsweredinspects the value against the question's current shape, so nogroupBy/_countcan produce this number directly. The count is constant in the number of rows (never per-row), which is the property the ticket's cost section actually asks for.#682's status circles (position-stat-circles.tsx) don't exist in this codebase yet, so there was nothing to cross-check against —ProgressRingis the onlyui/circular indicator today.