Page Design: DISPUTED Workflow Screens¶
Follows MASTER.md — Dialog + List patterns with destructive/warning theming. Applies to:
SCR-MAP-40,SCR-MAP-41,SCR-MAP-42,SCR-MAP-43Users: Data Owner (mark DISPUTED), Manager (mark DISPUTED), Approver (resolve + approve)
SCR-MAP-40: Popup Đánh dấu DISPUTED¶
Trigger: Data Owner/Manager clicks "Đánh dấu tranh chấp" on matrix cell (SCR-MAP-10)
Layout¶
+------------------------------------------+
| ⚠️ "Đánh dấu tranh chấp" |
| Cell: [DE name] × [Unit name] |
+------------------------------------------+
| Trạng thái hiện tại: [DATABASE] ● |
| |
| Lý do tranh chấp: * |
| [Textarea — min 20 ký tự] |
| "0/20 ký tự tối thiểu" |
+------------------------------------------+
| [Hủy] [Đánh dấu DISPUTED] |
+------------------------------------------+
Components¶
/* Dialog — max-w-lg, warning-themed header */
bg-card rounded-3xl border border-border shadow-xl max-w-lg
/* Header — warning/destructive accent */
px-8 py-6 border-b border-border bg-destructive/5
div.flex.items-center.gap-3
AlertTriangle.h-5.w-5.text-destructive
h2.text-xl.font-bold.text-destructive "Đánh dấu tranh chấp"
div.flex.items-center.gap-2.mt-2
Badge(variant="outline") /* DE name */
X.h-3.w-3.text-muted-foreground
Badge(variant="outline") /* Unit name */
/* Current status display */
flex items-center gap-3 p-4 bg-muted/50 rounded-2xl
/* Colored dot */ + /* Status label */
/* Reason textarea — REQUIRED, min 20 chars */
Textarea.rounded-2xl.min-h-[120px]
placeholder="Nhập lý do tranh chấp (tối thiểu 20 ký tự)..."
/* Character counter */
text-xs.font-medium.mt-1
/* < 20 chars: text-destructive */ "12/20 ký tự tối thiểu"
/* >= 20 chars: text-success */ "25 ký tự ✓"
/* Footer */
px-8 py-4 border-t border-border flex justify-end gap-3
Button(variant="outline") "Hủy"
Button(variant="destructive", disabled={charCount < 20})
Flag.h-4.w-4.mr-2 "Đánh dấu DISPUTED"
Business Rules¶
- Data Owner: Only cells in own unit column (org_id match)
- Manager: Any cell
- Min 20 chars: Button disabled until textarea >= 20 chars
- Save: Cell status → DISPUTED, color → red, toast "Ô đã được đánh dấu tranh chấp"
- Audit: Logged with user, timestamp, reason
SCR-MAP-41: Danh sách ô DISPUTED¶
Route:
/[locale]/current-state-map/disputedUsers: Approver (primary), Manager (view)
Layout¶
+-----------------------------------------------------------+
| "Danh sách ô tranh chấp (DISPUTED)" |
| Badge: "3 ô cần giải quyết" |
+-----------------------------------------------------------+
| Table |
| ┌──────────┬──────────┬────────────┬──────────┬──────────┐|
| │Data Elem │ Đơn vị │ Người đánh │ Lý do │ Ngày │|
| │ │ │ dấu │ │ │|
| ├──────────┼──────────┼────────────┼──────────┼──────────┤|
| │ Số CCCD │ Sở TC │ Chị Lan │ "Đơn vị │ 24/03/26 │|
| │ │ │ (Data Owner)│ không..."│ │|
| └──────────┴──────────┴────────────┴──────────┴──────────┘|
+-----------------------------------------------------------+
Components¶
/* Page header */
flex items-center gap-4 mb-6
h1.text-2xl.font-bold.text-foreground
Badge.bg-destructive.text-destructive-foreground.text-sm.font-black
"3 ô cần giải quyết"
/* DISPUTED table — MASTER.md table pattern */
bg-card rounded-3xl border border-border overflow-hidden shadow-sm
/* Columns */
th: Data Element | Đơn vị | Người đánh dấu | Lý do | Ngày | Thao tác
/* Row — each DISPUTED cell */
hover:bg-accent/50 transition-colors
/* DE column */ font-bold + code badge below
/* Unit column */ font-semibold
/* Person column */ name + role badge
/* Reason column */ text-sm italic, truncated with tooltip
/* Date column */ text-xs text-muted-foreground
/* Action column */
Button(variant="outline", size="sm")
Scale.h-4.w-4.mr-2 "Giải quyết"
/* Click → open SCR-MAP-42 */
Empty State¶
/* When 0 DISPUTED cells */
flex flex-col items-center justify-center py-16 text-center
CheckCircle.h-12.w-12.text-success/30
p.text-lg.font-bold.text-muted-foreground.mt-4
"Không còn ô tranh chấp"
p.text-sm.text-muted-foreground.mt-2
"Tất cả tranh chấp đã được giải quyết."
SCR-MAP-42: Popup Giải quyết DISPUTED¶
Trigger: Approver clicks "Giải quyết" on a row in SCR-MAP-41
Layout¶
+------------------------------------------+
| "Giải quyết tranh chấp" |
| [DE name] × [Unit name] |
+------------------------------------------+
| Thông tin tranh chấp: |
| Người đánh dấu: Chị Lan (Data Owner) |
| Ngày: 24/03/2026 |
| Lý do: "Đơn vị không lưu trữ trường |
| dữ liệu này trực tiếp..." |
| ───────────────────────────────────── |
| Quyết định của Approver: |
| (●) Xác nhận ánh xạ gốc → CONFIRMED |
| (○) Bác bỏ hoàn toàn → REJECTED |
| (○) Điều chỉnh sang DE khác |
| [Select DE — shown when selected] |
| |
| Lý do quyết định: * |
| [Textarea — required] |
+------------------------------------------+
| [Hủy] [Xác nhận quyết định] |
+------------------------------------------+
Components¶
/* Dialog — max-w-xl */
/* Dispute info card */
bg-destructive/5 border border-destructive/20 rounded-2xl p-4
grid grid-cols-2 gap-3 text-sm
/* Người đánh dấu, Ngày, Lý do (full width, italic) */
/* Decision radio group */
RadioGroup.space-y-3.mt-6
/* Option A: Xác nhận ánh xạ gốc */
label.flex.items-start.gap-3.p-4.rounded-2xl.border.border-border
.hover:bg-green-50.dark:hover:bg-green-950.cursor-pointer.transition-all
.has-[:checked]:bg-green-50.has-[:checked]:border-green-300
RadioGroupItem
div
p.font-bold.text-success "Xác nhận ánh xạ gốc"
p.text-xs.text-muted-foreground "Ô chuyển trạng thái CONFIRMED"
/* Option B: Bác bỏ hoàn toàn */
label...has-[:checked]:bg-red-50.has-[:checked]:border-red-300
p.font-bold.text-destructive "Bác bỏ hoàn toàn"
p.text-xs.text-muted-foreground "Ô chuyển trạng thái REJECTED"
/* Option C: Điều chỉnh sang DE khác */
label...has-[:checked]:bg-blue-50.has-[:checked]:border-blue-300
p.font-bold.text-primary "Điều chỉnh sang Data Element khác"
p.text-xs.text-muted-foreground "Chọn DE đúng, ô chuyển DATABASE"
/* Conditional: shown when option C selected */
Combobox.mt-3 /* Search & select DE */
/* Reason textarea — REQUIRED */
Label.mt-4 "Lý do quyết định *"
Textarea.rounded-2xl placeholder="Nhập lý do quyết định..."
/* Footer */
Button(variant="outline") "Hủy"
Button(variant="default") "Xác nhận quyết định"
Business Rules¶
- Only Approver can resolve DISPUTED
- Reason is required
- Option A → cell CONFIRMED (green)
- Option B → cell REJECTED (strikethrough)
- Option C → cell DATABASE with new DE, opens Combobox to search DE
- Toast: "Tranh chấp đã được giải quyết"
- Data Owner receives notification of result
SCR-MAP-43: Popup Phê duyệt Bảng C Tổng thể¶
Trigger: Approver clicks "Phê duyệt Bảng C" button on SCR-MAP-10
Layout¶
+------------------------------------------+
| "Phê duyệt Bảng C tổng thể" |
+------------------------------------------+
| Tổng quan trạng thái: |
| ┌────────────────────────────────────┐ |
| │ ✅ Sở đã xác nhận: 18/24 (75%) │ |
| │ ⏳ Sở chưa xác nhận: 6 │ |
| │ 🔴 Ô DISPUTED: 0 │ |
| └────────────────────────────────────┘ |
| |
| [Guard check: ✅ > 70% confirmed] |
| [Guard check: ✅ 0 DISPUTED] |
| |
| ⚠️ Sở chưa xác nhận quá deadline: |
| - Sở Xây dựng (quá 5 ngày) |
| - Sở Giao thông (quá 3 ngày) |
| [Chốt đơn phương cho các Sở trên?] ☐ |
+------------------------------------------+
| [Hủy] [Phê duyệt Bảng C] |
+------------------------------------------+
Components¶
/* Dialog — max-w-xl */
/* Status summary card */
bg-muted/50 rounded-2xl p-6 space-y-3
/* Each stat line */
flex items-center gap-3 text-sm
/* Confirmed */ CheckCircle.h-5.w-5.text-success + span.font-bold
/* Pending */ Clock.h-5.w-5.text-warning + span.font-bold
/* DISPUTED */ AlertTriangle.h-5.w-5.text-destructive + span.font-bold
/* Progress bar for % confirmed */
div.h-3.bg-muted.rounded-full.overflow-hidden.mt-2
div.h-full.bg-success.rounded-full /* width = % */
/* Guard condition checks */
space-y-2.mt-4
/* Each guard — pass or fail */
flex items-center gap-2 text-sm
/* Pass */ CheckCircle.h-4.w-4.text-success span.text-success.font-semibold
/* Fail */ XCircle.h-4.w-4.text-destructive span.text-destructive.font-semibold
/* Overdue units list (if any) */
bg-warning/10 border border-warning/20 rounded-2xl p-4 mt-4
h4.text-sm.font-bold.text-warning "Sở chưa xác nhận quá deadline:"
ul.mt-2.space-y-1.text-sm.text-muted-foreground
/* Each: unit name + days overdue */
/* Checkbox: "Chốt đơn phương" */
label.flex.items-center.gap-2.mt-3.text-sm.font-semibold
Checkbox
"Chốt đơn phương cho các Sở trên (đánh dấu 'Chốt bởi Ban Quản trị')"
/* Footer */
Button(variant="outline") "Hủy"
Button(variant="default", disabled={!guardsPass})
CheckCircle.h-4.w-4.mr-2 "Phê duyệt Bảng C"
/* Button disabled state: opacity-50, tooltip "Chưa đạt điều kiện phê duyệt" */
Business Rules¶
- Guard 1: > 70% units confirmed → green check
- Guard 2: 0 DISPUTED remaining → green check
- Both guards must pass for approval button to be enabled
- Overdue units: Approver can check "Chốt đơn phương" checkbox
- After approval: Bảng C → "Đã chốt", ready for EP-04 (Quy hoạch)
- AlertDialog confirm before final approval (irreversible action)
Approval Confirmation (AlertDialog, after clicking "Phê duyệt")¶
AlertDialog
title: "Xác nhận phê duyệt Bảng C?"
description: "Sau khi phê duyệt, Bảng C sẽ được chốt chính thức.
Bước tiếp theo là quy hoạch chủ quản (Bảng D).
Hành động này không thể hoàn tác."
cancel: "Quay lại"
action: Button(variant="default") "Phê duyệt chính thức"
Accessibility (all DISPUTED screens)¶
- SCR-MAP-40: Character counter announced via
aria-live="polite" - SCR-MAP-41: Table with
aria-label="Danh sách ô tranh chấp" - SCR-MAP-42: Radio group
aria-label="Quyết định giải quyết tranh chấp" - SCR-MAP-43: Guard conditions
role="status", button disabled statearia-disabled="true"with tooltip - All dialogs: focus trap, Escape to close, auto-focus first interactive element
MASTER.md Overrides¶
| What | MASTER.md | This Page | Reason |
|---|---|---|---|
| SCR-MAP-40 header bg | border-b border-border |
bg-destructive/5 |
Visual warning for destructive-adjacent action |
| SCR-MAP-42 dialog width | max-w-lg |
max-w-xl |
Need room for 3 radio options with descriptions + conditional combobox |