feat(admin): refresh nursing review UX and annotation reliability - #46
Conversation
Rework dashboard/history annotation surfaces to use a clinical-first layout, consistent shared label/badge mappings, and clearer unannotated state handling so nursing review decisions are faster and less ambiguous. Co-authored-by: Cursor <cursoragent@cursor.com>
Repair local SQLite installations where annotations still require staff_user_id so staff annotation saves no longer fail with 500 after moving to reviewer_identity_id. Co-authored-by: Cursor <cursoragent@cursor.com>
There was a problem hiding this comment.
Summary
Solid nursing-workflow refresh: centralized 繁中 label mapping (staff-review-label-mapping.ts), clearer annotation modal sections (symptom chips, badge styling, explicit label selection), and a guarded legacy migration for SQLite snapshots that still carry staff_user_id.
Blocking — CI frontend unit tests (2 failures)
Frontend unit tests fails locally with the same two suites:
history-overview/__tests__/page.test.tsx—opens upload detail modal from grouped thumbnails…still expects開啟病患完整頁, but the annotation modal no longer renders that link after the UX refresh.review-fast/__tests__/page.test.tsx—shows only positive symptoms in modalstill asserts影像判讀/症狀綜合, but the modal now usesSTAFF_REVIEW_FIELD_LABELS(AI 判讀結果,症狀綜合判定).
Please update those tests (or restore the removed link if it was unintentional).
Nits (non-blocking)
suggestedHistoryUploadLabelinhistory-upload-review-helpers.tsis now unused after drafts default to existing annotation or""— consider removing or reusing for first-open suggestions.- Migration creates
reviewer_identity_idnullable while the SQLAlchemy model is non-nullable; fallback to an arbitrary staff identity for NULL legacy rows is a mild audit-trail concern. - Minor copy inconsistency: annotation
rejected→上傳不採用vs screeningrejected→影像不採用; fast-review modal still shows無症狀while the shared copy uses未回報症狀.
CI snapshot
| Check | Status |
|---|---|
| Lint and migration policy | pass |
| Backend tests | pass |
| Frontend build | pass |
| K8s manifest render | pass |
| Frontend unit tests | fail (above) |
Happy to re-review once the unit tests are aligned.
Sent by Cursor Automation: PD Care PR Reviewer
| sa.Column("id", sa.Integer(), primary_key=True, autoincrement=True), | ||
| sa.Column("patient_id", sa.Integer(), sa.ForeignKey("patients.id", ondelete="CASCADE"), nullable=False), | ||
| sa.Column("upload_id", sa.Integer(), sa.ForeignKey("uploads.id", ondelete="CASCADE"), nullable=False), | ||
| sa.Column("reviewer_identity_id", sa.Integer(), sa.ForeignKey("liff_identities.id", ondelete="CASCADE"), nullable=True), |
There was a problem hiding this comment.
This column is nullable=True in the rebuilt table, but Annotation.reviewer_identity_id in models.py is nullable=False. If any legacy rows still have NULL reviewer_identity_id after the COALESCE backfill (e.g. no identities exist yet), inserts succeed but the ORM write path will still require a reviewer on new annotations.
Consider matching the model (nullable=False) once backfill is guaranteed, or documenting that NULL legacy rows are acceptable read-only artifacts.
|
|
||
| return ( | ||
| <div className="fixed inset-0 z-[60] flex items-end justify-center bg-zinc-900/60 p-0 sm:items-center sm:p-4"> | ||
| <div className="h-[90vh] w-full overflow-auto rounded-t-2xl bg-white shadow-xl sm:h-auto sm:max-w-3xl sm:rounded-2xl"> |
There was a problem hiding this comment.
Removing the 開啟病患完整頁 link from this modal breaks history-overview/__tests__/page.test.tsx (opens upload detail modal from grouped thumbnails…). If the link removal is intentional UX, please update that test; otherwise consider keeping a patient-detail link in the modal header.
| <div className="flex flex-col gap-2 text-xs text-zinc-500"> | ||
| <div className="flex items-center justify-between gap-2"> | ||
| <span>影像判讀</span> | ||
| <span>{STAFF_REVIEW_FIELD_LABELS.aiResult}</span> |
There was a problem hiding this comment.
review-fast/__tests__/page.test.tsx still asserts the old labels 影像判讀 / 症狀綜合. With STAFF_REVIEW_FIELD_LABELS, the modal now shows AI 判讀結果 / 症狀綜合判定 — update the test expectations to match the new copy.
| normal: "正常", | ||
| suspected: "疑似感染", | ||
| confirmed_infection: "確認感染", | ||
| rejected: "上傳不採用", |
There was a problem hiding this comment.
Nit: annotation rejected is 上傳不採用 here, but screeningResultText('rejected') returns 影像不採用. Nurses may see different wording for the same verdict depending on context — worth aligning if intentional.
| return screeningResultText(upload.screening_result); | ||
| } | ||
|
|
||
| export function suggestedHistoryUploadLabel( |
There was a problem hiding this comment.
suggestedHistoryUploadLabel looks unused now that drafts initialize from annotation_label ?? "" instead of an AI suggestion. Safe cleanup candidate unless you plan to restore auto-suggest on first open.
Expose the latest annotation label in today-attention API responses and render the actual nursing verdict badge beside patient names so staff see actionable status at a glance. Update impacted unit tests to match the revised review modal copy and structure. Co-authored-by: Cursor <cursoragent@cursor.com>
Establish a contract-first workflow for browser feature demos and add a deterministic Playwright setup for local persona-based scenarios. Separate unit and e2e lanes so CI unit runs stay stable while keeping recorded demo artifacts local-only. Co-authored-by: Cursor <cursoragent@cursor.com>
Document and ship a systemd-driven recovery path that brings minikube, ingress bridge, and dependent workloads back in order after host reboot incidents. This reduces manual recovery steps and gives operators a repeatable health-gated startup sequence. Co-authored-by: Cursor <cursoragent@cursor.com>
Require migrated annotations to retain a valid reviewer identity instead of assigning an arbitrary account. This keeps the schema aligned with the non-null model contract and prevents audit attribution from being fabricated. Co-authored-by: Cursor <cursoragent@cursor.com>
Use one nursing-facing vocabulary for rejected images and absent symptom reports, while removing an obsolete suggested-verdict helper that no longer matches review draft behavior. Co-authored-by: Cursor <cursoragent@cursor.com>
Show every patient card's manual annotation status prominently and separate clinical risk badges from AI confidence. Keep symptom detail out of elevated-risk list cards so the dashboard remains scannable while preserving it in the selected patient preview and review flow. Co-authored-by: Cursor <cursoragent@cursor.com>
Drop the obsolete isTodaySelected prop from TodayPatientPool now that the detail panel no longer accepts it. This unblocks production builds by keeping the pool and panel interfaces aligned. Co-authored-by: Cursor <cursoragent@cursor.com>
|
@cursoragent review again |
Re-review summaryI re-checked PR #46 against the earlier review comments. The follow-up commits on Prior findings — resolved
Verification run locally
Remaining nit (non-blocking)
VerdictApprove from a code-review standpoint. The nursing label mapping is centralized in No additional code changes were needed on this pass. |
Use the shared 已標註 and 未標註 labels for every patient tier in the detail subtitle. This prevents high-risk patients from showing a conflicting review-workflow status beside the annotation badge. Co-authored-by: Cursor <cursoragent@cursor.com>



Summary
Test plan