Skip to content

fix(document-viewer): harden manual-tag editor UX (validation, delete confirm, 401)#1035

Merged
BigSimmo merged 5 commits into
mainfrom
claude/tag-editor-hardening
Jul 21, 2026
Merged

fix(document-viewer): harden manual-tag editor UX (validation, delete confirm, 401)#1035
BigSimmo merged 5 commits into
mainfrom
claude/tag-editor-hardening

Conversation

@BigSimmo

@BigSimmo BigSimmo commented Jul 21, 2026

Copy link
Copy Markdown
Owner

Summary

Follow-up to #1025 — addresses the three pre-existing manual-tag-editor.tsx findings that
CodeRabbit surfaced there. They predate the decomposition (the editor was moved verbatim), so
folding them into that pure-move PR would have broken its no-behaviour-change contract; they
belong in a dedicated PR. Client-side only — the server already enforces the label
contract and auth; this closes the gap between the field and the API.

  • Length validation. The server requires label to be 2–64 chars
    (manualLabelSchema in src/app/api/documents/[id]/labels/route.ts). Add maxLength={64}
    to the add and edit inputs and disable submit/save until the trimmed value reaches the 2-char
    minimum, so a too-short/too-long tag no longer round-trips to a generic 400.
  • Delete confirmation. A manual-tag delete now needs an explicit second click — an inline
    two-click confirm (RemoveConfirm remove / Cancel), no native window.confirm (which
    the repo avoids) — so a single misclick can't remove a curated label.
  • 401 handling. On a 401, submitManualTag now returns after onUnauthorized() instead of
    falling through to the generic throw, so the unauthorized handoff no longer also raises a
    stale inline error banner underneath the parent's unauthorized UI.

Off the RAG-ranking lane; no server, schema, permission, or data-access behaviour changed.

Verification

  • npm run typecheck — 0 errors
  • npm run lint — 0 warnings
  • npm run test — 3,065 passing, incl. the new
    tests/manual-tag-editor.dom.test.tsx (React Testing Library) covering all three behaviours:
    Add stays disabled below the 2-char minimum + input caps at 64; delete requires the explicit
    confirm (and Cancel backs out) before any request; a 401 calls onUnauthorized without
    rendering the error banner. The only failure is the pre-existing container-only
    pdf-extraction-budget flake (Python OCR stack absent in this VM).

UI note: the manual-tag editor is admin-gated (canManage) and not reachable in the demo
Playwright flow, so the RTL component test — which renders the component and drives the exact
new interactions in jsdom — is the behavioural proof here rather than a Chromium spec.

Risk and rollout

  • Risk: low — small, well-scoped client-side UX changes with direct test coverage; no server,
    schema, or permission change.
  • Rollback: revert the single commit.
  • Provider or production effects: None.

Clinical Governance Preflight

Not applicable — client-side field validation and confirmation UX only. No ingestion, answer
generation, retrieval/ranking, source governance, document-access rules, privacy, schema, or
production behaviour changed; the server-side label contract, auth, and permissions are
untouched.

Notes

🤖 Generated with Claude Code


Generated by Claude Code

Summary by CodeRabbit

  • New Features

    • Added client-side minimum/maximum length validation for manual tag labels.
    • Introduced a two-step confirmation flow before removing tags.
  • Bug Fixes

    • “Add” and “Save” are now disabled until the label meets the required minimum after trimming.
    • Improved handling of unauthorized (401) responses to call the unauthorized handler and avoid duplicate/inline error banner messaging.
    • Removing tags now prevents unintended duplicate destructive actions during rapid clicks.
  • Tests

    • Expanded UI tests to cover validation, confirmation behavior, and unauthorized flows.

… confirm, 401)

Address the three pre-existing manual-tag-editor findings deferred from #1025 — they
predate the decomposition, so folding them into that pure-move PR would have broken its
no-behaviour-change contract. Client-side only; the server already enforces the label
contract and auth.

- Match the server's 2-64 char label rule (manualLabelSchema in
  src/app/api/documents/[id]/labels/route.ts) in the field: maxLength={64} on the add and
  edit inputs, and disable submit/save until the trimmed value reaches the 2-char minimum,
  so a too-short/too-long tag no longer round-trips to a generic 400.
- Require an explicit second click to confirm a manual-tag delete (inline two-click confirm
  with a Cancel, no native window.confirm) so a single misclick can't remove a curated label.
- Return after onUnauthorized() on a 401 instead of falling through to the generic throw, so
  the unauthorized handoff no longer also raises a stale inline error banner.

Adds tests/manual-tag-editor.dom.test.tsx covering all three behaviours.

Verified: typecheck, lint, and the full unit suite (3,065 passing incl. the 3 new tests;
only the pre-existing container-only pdf-extraction-budget flake fails).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019Jc1ZYHFjXjn6mE6U6riVU
@supabase

supabase Bot commented Jul 21, 2026

Copy link
Copy Markdown

This pull request has been ignored for the connected project sjrfecxgysukkwxsowpy because there are no changes detected in supabase directory. You can change this behaviour in Project Integrations Settings ↗︎.


Preview Branches by Supabase.
Learn more about Supabase Branching ↗︎.

@coderabbitai

coderabbitai Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: e46ce905-ec91-4735-8c73-8af4a5b0a2af

📥 Commits

Reviewing files that changed from the base of the PR and between e2de334 and eba4436.

📒 Files selected for processing (2)
  • src/components/document-viewer/manual-tag-editor.tsx
  • tests/manual-tag-editor.dom.test.tsx
🚧 Files skipped from review as they are similar to previous changes (2)
  • tests/manual-tag-editor.dom.test.tsx
  • src/components/document-viewer/manual-tag-editor.tsx

📝 Walkthrough

Walkthrough

The manual tag editor enforces label length limits, handles HTTP 401 responses through onUnauthorized, and requires confirmation before deletion. DOM tests cover validation, deletion confirmation, double-click protection, and unauthorized submission behavior.

Changes

Manual tag editor behavior

Layer / File(s) Summary
Label validation and unauthorized submission
src/components/document-viewer/manual-tag-editor.tsx, tests/manual-tag-editor.dom.test.tsx
Add and edit inputs enforce a 64-character maximum, action buttons require at least two trimmed characters, and HTTP 401 responses call onUnauthorized without rendering an inline error.
Two-step manual tag deletion
src/components/document-viewer/manual-tag-editor.tsx, tests/manual-tag-editor.dom.test.tsx
Removal first displays per-tag confirmation controls; cancellation avoids requests, confirmation clears the state and issues the DELETE request, and double-clicking the initial control does not trigger deletion.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 16.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main manual-tag editor UX hardening changes.
Description check ✅ Passed The description includes summary, verification, risk, governance, and notes sections with the key requested details.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/tag-editor-hardening

Comment @coderabbitai help to get the list of available commands.

@BigSimmo
BigSimmo marked this pull request as ready for review July 21, 2026 02:31
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/components/document-viewer/manual-tag-editor.tsx`:
- Around line 242-260: The confirmingDeleteId branch in ManualTagEditor must not
replace the original Remove target with a destructive action button, since rapid
repeated clicks can invoke deleteManualTag. Render a non-action confirmation
prompt there and provide a visibly labeled Confirm remove control in a separate
location; update tests/manual-tag-editor.dom.test.tsx at lines 61-72 to rapidly
click the initial Remove control and verify no DELETE request is issued.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 0434f048-76f3-4bc2-93c2-69c58e8d9f07

📥 Commits

Reviewing files that changed from the base of the PR and between 366eff9 and e2de334.

📒 Files selected for processing (2)
  • src/components/document-viewer/manual-tag-editor.tsx
  • tests/manual-tag-editor.dom.test.tsx

Comment thread src/components/document-viewer/manual-tag-editor.tsx
claude and others added 3 commits July 21, 2026 02:42
…e (CodeRabbit)

The first version rendered the "Confirm remove" button at the same fragment index as
the "Remove" button, so React reused the Remove button's DOM node for the destructive
confirm — a rapid double-click on Remove could land its second click on the in-place
delete control. Fix it two ways:

- Give the Rename/Remove and confirm/cancel controls distinct `key`s so React never
  reuses the Remove node for the destructive confirm — the control the user pressed is
  never turned into "delete" in place.
- Render a non-action "Remove this tag?" prompt in the position the Remove button was
  clicked from, shifting the confirm control off that hit target, and label the confirm
  button "Confirm remove".

Adds a regression test: after one Remove click the original Remove control is gone
(replaced by the prompt) and re-clicking it issues no DELETE.

Verified: typecheck, lint, the manual-tag-editor dom suite (4 passing), and the full
unit suite (3,066 passing; only the pre-existing pdf-extraction-budget flake fails).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019Jc1ZYHFjXjn6mE6U6riVU
@BigSimmo
BigSimmo merged commit 0052733 into main Jul 21, 2026
17 checks passed
@BigSimmo
BigSimmo deleted the claude/tag-editor-hardening branch July 21, 2026 06:20
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