Skip to content

#731 Display And Store Phone Numbers In One Format - #752

Merged
b-at-neu merged 3 commits into
devfrom
731-display-and-store-phone-numbers-in-one-format
Sep 25, 2026
Merged

b-at-neu merged 3 commits into
devfrom
731-display-and-store-phone-numbers-in-one-format

Conversation

@b-at-neu

Copy link
Copy Markdown
Collaborator

Closes #731

Summary

  • Phone numbers now display consistently everywhere via a shared, tolerant formatter, and new phone_number answers are stored normalized. No backfill — existing rows render unchanged through the tolerant path.
  • Hand-rolled digit-strip-and-mask, not libphonenumber-js — confirms the ticket's recommendation, since the validator is already deliberately loose and the masking rule is US-only by design.

Changes

  • lib/constants.ts — added normalizePhoneNumber, formatPhoneNumber, and the two format-dispatching helpers normalizeShortAnswerValue / formatShortAnswerValue, placed beside matchesShortAnswerFormat.
  • lib/types.ts — added format: ShortAnswerFormat | null to ApplicationReviewAnswer (AnswerQuestion already had it).
  • prisma/data/applications.ts — selects format through the live question relations (including for orphaned/soft-deleted questions) and maps it into all four branches of normalizeApplicationAnswers.
  • prisma/actions/profile.ts (updateGlobalAnswer) and prisma/actions/applications.ts (createOrUpdateApplicationAnswer) — the write path now runs each short-answer value through normalizeShortAnswerValue instead of a bare .trim(). Runs strictly after existing validation; the profile-copy branch for file_upload answers is untouched.
  • components/features/answer-display.tsx and components/features/application-answers-list.tsx — the single-value and chip-list short-answer branches render through formatShortAnswerValue, covering both the profile view and the reviewer/applicant answer list.
  • tests/unit/constants.test.ts — unit coverage for normalizePhoneNumber/formatPhoneNumber (10-digit, 11-digit leading 1, already-normalized, + international, 00 international, 7-digit local, a validator-passing value that fits no mask — each round-tripped normalize → format) and for the dispatchers' identity behavior on other formats.
  • tests/db/phone-number-normalization.test.ts (new) — confirms updateGlobalAnswer and createOrUpdateApplicationAnswer both store a digits-only value for a punctuated input.
  • docs/WORKFLOWS.md — one clause each in AP-2, AP-6, AP-11, PM-9 describing the storage/display rule.

Testing plan

  • Admin: set a global question's format to Phone number. As an applicant, answer it (617) 555-0100 on /profile, reload — the read-only card shows (617) 555-0100.
  • Confirm the stored value is 6175550100 (digits only) via the DB or the edit field.
  • Answer a second phone question +44 20 7123 4567 — stored +442071234567, displayed unchanged as +442071234567.
  • Manually insert/edit a legacy-shaped row (555-123-4567) directly in the DB — the profile card and the reviewer view both show (555) 123-4567, and the row itself is untouched.
  • Apply to a position with a phone-formatted position question; save an answer, submit, then open the application as a manager at /manage/applications/<id> — the answer renders masked in the answers list.
  • Delete that position question (soft delete), reload the reviewer view — the orphaned answer still renders masked, below the live questions.
  • Enter abc in a phone field — still Enter a valid phone number inline, nothing saved (unchanged behavior).
  • Check an email, url, and zip_code question and a long answer — rendering byte-identical to before this change.
  • A multi-value legacy short_answer row (more than one value, format phone_number) — each chip formats independently.

Automated checks

  • npm run prettier:check — pass
  • npm run eslint:check — pass
  • npm run tsc:check — pass
  • npm run test:unit — pass (77/77 in tests/unit/constants.test.ts; full unit suite 423/423, one pre-existing unrelated failure in tests/unit/email-delivery-events.test.ts due to no local DATABASE_URL)
  • tests/db/phone-number-normalization.test.ts added but not run locally — no Postgres available in this worktree (DATABASE_URL unset, and the local db:start port is already occupied by an unrelated container). Will run in CI's run-tests check.

Notes

  • The edit input (AnswerField) deliberately keeps showing the raw stored value while the read-only card shows the mask — formatting a controlled autosave input would desync the stepper's dirty/diff tracking for a cosmetic gain. type="tel" and the validator are unchanged.
  • findDivergingGlobalAnswers is string-exact, so re-saving an application phone answer can flag a purely cosmetic divergence against an untouched legacy profile value. Cosmetic only — "Revert to profile answers" still writes the correct profile value; not worth normalizing the copy path, which would be a backfill by the back door.
  • The 00 → + promotion is the one rule the ticket left implicit — it follows from the validator treating both as the same prefix, and is covered by a round-trip unit test.

@b-at-neu b-at-neu added the claude Will be worked on by Claude label Sep 21, 2026
@b-at-neu b-at-neu self-assigned this Sep 21, 2026
@vercel

vercel Bot commented Sep 21, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
aplio Ready Ready Preview Sep 25, 2026 2:19pm UTC

@b-at-neu b-at-neu added ready for review PR ready for review agent reviewing Review agent working (in-flight) and removed ready for review PR ready for review agent labels Sep 21, 2026

@b-at-neu b-at-neu left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review — Cycle 1 · needs revision

1 open — 1 🟡 Low

R1-L1 🟡 Low — AnswerMismatchNotice renders orphaned short_answer values raw (no formatShortAnswerValue), so a legacy multi-value phone_number row shows unmasked extra digits in the mismatch notice — the one render surface the ticket's "verified by grep" acceptance criterion missed. Fix: run values through formatShortAnswerValue/pass format down from AnswerDisplay.

<ul className="mt-1 flex flex-col gap-0.5">
{values.map((v, i) => (
<li key={i} className="font-medium">
{v}
</li>
))}
</ul>

@b-at-neu b-at-neu added needs revision Review found issues that need fixing revising Revise agent working (in-flight) and removed reviewing Review agent working (in-flight) needs revision Review found issues that need fixing labels Sep 21, 2026
@b-at-neu
b-at-neu force-pushed the 731-display-and-store-phone-numbers-in-one-format branch from 6b29bf0 to 2db1cd3 Compare September 21, 2026 04:07
@b-at-neu

Copy link
Copy Markdown
Collaborator Author

Revision — Cycle 1

fixed R1-L1 · 2db1cd3

@b-at-neu b-at-neu added ready for review PR ready for review agent reviewing Review agent working (in-flight) and removed revising Revise agent working (in-flight) ready for review PR ready for review agent labels Sep 21, 2026

@b-at-neu b-at-neu left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review — Cycle 2 · approved

1 open — 1 ⚪ Nit (see inline)

Comment thread lib/constants.ts Outdated
@b-at-neu b-at-neu added approved Review passed, ready to merge needs revision Review found issues that need fixing revising Revise agent working (in-flight) and removed reviewing Review agent working (in-flight) approved Review passed, ready to merge needs revision Review found issues that need fixing labels Sep 21, 2026
@b-at-neu
b-at-neu force-pushed the 731-display-and-store-phone-numbers-in-one-format branch from 2db1cd3 to 4964bc2 Compare September 23, 2026 17:38
@b-at-neu
b-at-neu force-pushed the 731-display-and-store-phone-numbers-in-one-format branch from 4964bc2 to 8ff9988 Compare September 23, 2026 17:40
@b-at-neu

Copy link
Copy Markdown
Collaborator Author

Revision — rebase

rebase: docs/WORKFLOWS.md (merged independent insertions), tests/unit/constants.test.ts (merged independent import additions) · 8ff9988

@b-at-neu b-at-neu added the revising Revise agent working (in-flight) label Sep 23, 2026
@b-at-neu
b-at-neu force-pushed the 731-display-and-store-phone-numbers-in-one-format branch from d77c17d to b015b01 Compare September 23, 2026 23:56
@b-at-neu

Copy link
Copy Markdown
Collaborator Author

Revision — Cycle 3

fixed R2-N1 · b015b01

R3-L1 investigated: no code-level cause found. The diff has no schema/migration/route changes, no env-dependent code, and nothing edge-runtime-incompatible. The failed deployment (dpl_8L29hqQgchXrAciyJaKx4NCA45WB, commit 8ff9988) predates this branch's merge from dev; the very next deployment on the same PR (merge commit d77c17d, functionally identical app code) completed successfully, and the Vercel build log itself is human-only / vercel CLI is deny-listed for agents. Given the identical code redeployed clean, this reads as a transient build/infra hiccup (likely in vercel.json's preview-only prisma migrate deploy step against the Neon preview branch) rather than something this PR introduced.

@b-at-neu b-at-neu added ready for review PR ready for review agent and removed revising Revise agent working (in-flight) labels Sep 23, 2026
@b-at-neu b-at-neu added reviewing Review agent working (in-flight) and removed ready for review PR ready for review agent labels Sep 23, 2026

@b-at-neu b-at-neu left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review — Cycle 4 · approved

0 open — clean

@b-at-neu b-at-neu added approved Review passed, ready to merge needs revision Review found issues that need fixing revising Revise agent working (in-flight) and removed reviewing Review agent working (in-flight) approved Review passed, ready to merge needs revision Review found issues that need fixing labels Sep 24, 2026
b-at-neu and others added 3 commits September 25, 2026 10:16
Store new phone_number answers as digits with an optional leading +,
and mask 10/11-digit US-shaped values on every short-answer renderer.
Unparseable and international values render exactly as stored, and
existing rows are never backfilled.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Run orphaned short_answer values through formatShortAnswerValue in
AnswerMismatchNotice so a legacy multi-value phone_number row masks
consistently in the mismatch notice.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@b-at-neu
b-at-neu force-pushed the 731-display-and-store-phone-numbers-in-one-format branch from b015b01 to e3c1561 Compare September 25, 2026 14:17
@b-at-neu

Copy link
Copy Markdown
Collaborator Author

Revision — Cycle 4

rebased onto origin/dev · e3c1561 · rebase: docs/WORKFLOWS.md (union merge — both sides added distinct non-overlapping sentences to the same AP-11/PM-9 paragraphs; kept both)

@b-at-neu b-at-neu added ready for review PR ready for review agent reviewing Review agent working (in-flight) and removed revising Revise agent working (in-flight) ready for review PR ready for review agent labels Sep 25, 2026

@b-at-neu b-at-neu left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review — Cycle 5 · approved

0 open — clean

@b-at-neu b-at-neu added approved Review passed, ready to merge and removed reviewing Review agent working (in-flight) labels Sep 25, 2026
@b-at-neu
b-at-neu merged commit 9ca9129 into dev Sep 25, 2026
14 of 19 checks passed
@b-at-neu
b-at-neu deleted the 731-display-and-store-phone-numbers-in-one-format branch September 25, 2026 14:26

This branch was successfully deployed

1 active deployment
Preview — e3c15619 Deployed Sep 25, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

approved Review passed, ready to merge claude Will be worked on by Claude

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Display And Store Phone Numbers In One Format

1 participant