Skip to content

Login: offer "Create" for new accounts, confirm the PIN, widen the panel - #98

Merged
IanMayo merged 2 commits into
mainfrom
claude/dormant-project-consolidation-q5qpr9
Sep 5, 2026
Merged

IanMayo merged 2 commits into
mainfrom
claude/dormant-project-consolidation-q5qpr9

Conversation

@IanMayo

@IanMayo IanMayo commented Sep 5, 2026 •

Copy link
Copy Markdown
Contributor

Two changes to the login form, both from first-time-user feedback on the demo.

"Create" vs "Login", with PIN confirmation

A first-time user had no way to tell that logging in would create an account, or that the PIN they typed would become their PIN permanently.

  • The submit button reads "Create" when the entered service ID has no account for this release, and "Login" when it does.
  • Pressing "Create" takes the PIN a second time before storing it, so a typo cannot silently become the stored PIN and lock a student out of their own record.
  • A mismatch warns and returns to the create state. A match creates the account and logs in.

Registration is resolved on submit as well as on a debounced keystroke, so a fast typist who clicks before the lookup returns still gets the confirm step rather than a silently different flow. The lookup never throws: a storage problem resolves to "unknown" and the button falls back to its neutral "Login" label.

Latent bug fixed along the way: logout cleared every other form field but left the previous user's registration state behind.

Wider panel, so the PIN field states its rule

In #97 I kept the PIN field narrow because widening it wrapped the Instructor button. I blamed the header. That was wrong: the constraint was the panel's own max-width: 480px. At 560px the field carries a PIN (4 digits) placeholder and everything still sits on one row.

Viewport Rows Panel height
1540px one 75px
1280px one 75px
1024px wraps 105px

75px is shorter than the 81px this panel occupied before any of this work, and 1024px and below behave exactly as they did.

E2E: 50 call sites moved behind one helper

The second step breaks every test that logged in by clicking submit once. 77 of 111 E2E tests failed on the first run. Rather than patch each spec, the 50 student-login call sites across 16 files now route through a new tests/e2e/helpers.ts.

Three bugs in that helper, each found by a failing test rather than assumed:

  1. It hard-coded PIN 1234, but specs pass their own. It now reads the PIN already in the field.
  2. Its :has-text("Confirm") matched a button inside the migration dialog. Locators are now scoped to form.login-form.
  3. Its race resolved the same value on success and timeout, so "nothing happened" was read as "confirm requested". Only a real outcome now decides: confirm requested, logged in, or login rejected.

Three new E2E tests cover the create, mismatch and returning-user paths directly.

Trade-off recorded

The lookup makes account existence observable to anyone at the keyboard. Accepted and written up in docs/SECURITY.md: this is an offline single-machine tool, and anyone who can type into the form can already read the whole database in DevTools.

Verification

Check Result
typecheck, lint, format pass
unit + coverage 893 tests, thresholds pass
integration + coverage 88 tests, thresholds pass
E2E default 108 passed, 6 skipped, 0 failed
E2E ENCRYPT_STORAGE=true 113 passed, 1 skipped, 0 failed
Storybook build pass
test-gap ratchet pass
bundle size 39.77 KB gzip, limit 40 KB

Headroom is now 0.23 KB. The integration coverage ratchet also caught this change dipping below its floor, which is what prompted the four new integration tests rather than a lowered threshold. The next feature will need to trim before it can add.

🤖 Generated with Claude Code

https://claude.ai/code/session_015u1mfVebscuZA3YDc6hc87

Follow-up to the login affordances. I had kept the PIN field narrow
because widening it wrapped the Instructor button onto a second row, and
I attributed that to the header being short of space. That was wrong:
the header has plenty of room. The constraint was the panel's own
`max-width: 480px`.

Raising it to 560px lets the PIN field carry a "PIN (4 digits)"
placeholder, so all three fields now show worked examples the way Name
and Service ID already did, and every control still sits on one row.

Measured across viewports: 1540px and 1280px are a single row 75px tall,
which is shorter than the 81px this panel occupied before any of these
changes. At 1024px and below the panel shrinks under its max-width and
wraps, exactly as it did beforehand, so nothing narrow regresses.

Bundle 39.41 KB gzip against the 40 KB limit.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015u1mfVebscuZA3YDc6hc87
github-actions Bot added a commit that referenced this pull request Sep 5, 2026
@github-actions

github-actions Bot commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

🚀 PR Preview Deployment

✅ Your changes are being built and deployed to a preview environment.

📋 Live PR Preview Links:

ℹ️ About PR Previews:

📦 Production Demo (main branch):

🧪 Testing Locally:

npm install
npm run build
# Open demo/quiz-index.html in your browser (use file:// or http-server)

This comment will be automatically updated when you push new commits.

A first-time user had no way to tell that logging in would create an
account, or that the PIN they typed would become their PIN permanently.

The submit button now reads "Create" when the entered service ID has no
account for this release, and "Login" when it does. Pressing "Create"
takes the PIN a second time before storing it, so a typo cannot silently
become the stored PIN and lock a student out of their own record. A
mismatch warns and returns to the create state; a match creates the
account and logs in.

Registration is resolved on submit as well as on a debounced keystroke,
so a fast typist who clicks before the lookup returns still gets the
confirm step rather than a silently different flow. The lookup never
throws: a storage problem resolves to "unknown" and the form falls back
to its neutral "Login" label.

Also fixes a latent bug found while testing: logout cleared every other
form field but left the previous user's registration state behind.

E2E: 50 login call sites across 16 specs clicked submit directly and now
break on the second step, so they route through one new helper,
tests/e2e/helpers.ts. The helper reads the PIN already typed, scopes its
locators to the login form so dialog buttons cannot match, and waits for
a real outcome (confirm requested, logged in, or login rejected) instead
of a fixed delay. Three new E2E tests cover the create, mismatch and
returning-user paths directly.

The service-ID lookup makes account existence observable to anyone at the
keyboard. That trade-off is recorded in docs/SECURITY.md.

Bundle 39.77 KB gzip against the 40 KB limit, 0.23 KB remaining.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015u1mfVebscuZA3YDc6hc87
@IanMayo IanMayo changed the title Widen the login panel so the PIN field states its own rule Login: offer "Create" for new accounts, confirm the PIN, widen the panel Sep 5, 2026
github-actions Bot added a commit that referenced this pull request Sep 5, 2026
@IanMayo
IanMayo merged commit 85a7203 into main Sep 5, 2026
9 checks passed
@IanMayo
IanMayo deleted the claude/dormant-project-consolidation-q5qpr9 branch September 5, 2026 20:02
github-actions Bot added a commit that referenced this pull request Sep 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants