Skip to content

UI Spec: Quản lý vật tư theo Subtask

Feature slug: subtask-materialVersion: 1.0 Ngày: 2026-03-26


B1) Screen Map

CustomerDetail (Tab "Công Việc")
└── TaskDetail (Chi tiết công việc cha)
    ├── SCR-03: SubtaskTable — Bảng subtask (fix button label + delete confirm)
    ├── SCR-04: AggregateView — Bảng VẬT TƯ DỰ KIẾN (aggregate)
    ├── SCR-05: LegacyMaterialView — Bảng VẬT TƯ ĐÃ XUẤT KHO (backward compat)
    └── TaskCreate Popup (isChild=true)
        ├── SCR-02: SubtaskForm — Form subtask (existing + thêm MaterialForm)
        └── SCR-01: MaterialForm — Section vật tư (NEW)

B2) Component Inventory

SCRComponentFileLoạiFR
SCR-01MaterialFormTaskForm/MaterialForm.tsxNewFR-001, FR-002, FR-003, FR-004, FR-008
SCR-02TaskForm (sửa)TaskForm/index.tsxSửaFR-001
SCR-03SubtaskTable (sửa)TaskDetail/SubtaskTable.tsxSửaFR-007
SCR-04General.tsx — aggregate sectionTaskDetail/General.tsxSửaFR-005, FR-006
SCR-05MaterialTable (giữ cũ)TaskDetail/MaterialTable.tsxSửa nhẹFR-006

B3) User Flows

Flow 1: Tạo subtask với vật tư

[SubtaskTable] Click "Thêm công việc"
    → [TaskCreate popup] mở (isChild=true, isCreateForm=true)
    → [SubtaskForm] nhập tên, assignees, due_date
    → [Chọn subtask definition]
        → Auto-fill vật tư mẫu vào MaterialForm
    → [MaterialForm] KTV chỉnh SL / xóa / search thêm
    → Click "Lưu"
        → Insert project_task (subtask)
        → Insert project_task_material[] (materials)
    → Popup đóng
    → [SubtaskTable] refresh
    → [AggregateView] refresh

Flow 2: Sửa vật tư trên subtask

[SubtaskTable] Click icon sửa subtask
    → [TaskCreate popup] mở (isChild=true, có taskId)
    → Load task_materials từ DB → hiển thị trong MaterialForm
    → [MaterialForm] KTV sửa SL / xóa / thêm
    → Click "Lưu"
        → Delete all materials by task_id → Re-insert
    → Popup đóng → refresh

Flow 3: Xóa subtask có materials

[SubtaskTable] Click icon xóa subtask
    → [Confirm dialog] "Bạn có chắc chắn muốn xóa công việc [tên]?
       (Sẽ xóa 3 vật tư đi kèm)"
    → Click "Xác nhận"
        → Delete project_task → CASCADE delete project_task_material
    → [SubtaskTable] refresh
    → [AggregateView] refresh (materials giảm)

B4) Notification Spec

Không có notification trong phase 1 (Ref: DEC-B04, Non-goals).


B5) Permission Matrix

Màn hìnhActionStaff (KTV)ManagerAdminCall Center
SCR-01 MaterialFormXem vật tư subtaskSubtask mình assign/tạoTất cả trong projectTất cảẨn
SCR-01 MaterialFormThêm vật tưSubtask mình assign/tạo, status ≠ done/canceledTất cả, status ≠ done/canceledTất cảẨn
SCR-01 MaterialFormSửa SL vật tưSubtask mình assign/tạo, status ≠ done/canceledTất cả, status ≠ done/canceledTất cảẨn
SCR-01 MaterialFormXóa vật tưSubtask mình assign/tạo, status ≠ done/canceledTất cả, status ≠ done/canceledTất cảẨn
SCR-04 AggregateViewXem tổng hợpTheo quyền xem task chaTất cảTất cảẨn

Permission reuse: theo useProjectTaskPermission hiện tại — Staff xem subtask mình assign/tạo, Manager xem tất cả trong project, Admin full.


B6) State Matrix

Màn hìnhLoadingEmptyErrorNo PermissionPartial
SCR-01 MaterialFormSkeleton rows (3 dòng)Bảng trống + text "Chưa có vật tư. Tìm kiếm để thêm."Toast error "Không thể tải vật tư"Ẩn section hoàn toàn (Call Center)Hiển thị rows đã load, search disabled
SCR-01 MaterialForm (readonly)Skeleton rowsBảng trống + text "Không có vật tư"Toast errorẨn
SCR-04 AggregateViewSpinnerText "Chưa có vật tư dự kiến"Toast errorẨn sectionHiển thị partial data
SCR-05 LegacyMaterialViewSpinnerẨn section hoàn toànToast errorẨn

B7) Copy Text Dictionary

Key (i18n)Text (VI)Màn hìnhGhi chú
project.label.material_sectionVẬT TƯSCR-01Heading section trong form subtask
project.label.material_plannedVẬT TƯ DỰ KIẾNSCR-04Heading aggregate ở task cha
project.label.material_exportedVẬT TƯ (ĐÃ XUẤT KHO)SCR-05Heading backward compat
project.label.material_nameVật tưSCR-01, SCR-04Cột tên
project.label.material_skuMã vật tưSCR-01, SCR-04Cột SKU
project.label.material_unitĐVTSCR-01, SCR-04Cột đơn vị
project.label.material_qty_plannedSL dự kiếnSCR-04Cột số lượng aggregate
project.label.material_qtySLSCR-01Cột số lượng input
project.label.material_searchTìm kiếm thêm vật tư...SCR-01Placeholder search
project.label.material_emptyChưa có vật tư. Tìm kiếm để thêm.SCR-01Empty state
project.label.material_empty_aggregateChưa có vật tư dự kiếnSCR-04Empty state aggregate
project.label.material_disabledNgưng KDSCR-01Badge product disabled
project.label.add_taskThêm công việcSCR-03Button label (fix)
project.label.delete_task_with_materialsSẽ xóa {count} vật tư đi kèmSCR-03Delete confirm dialog
project.label.material_aggregate_titletổng hợp từ {count} công việc conSCR-04Subtitle aggregate

B8) Analytics Events

EventTriggerDataMục đích
subtask_material_addedKTV thêm vật tư vào subtask`{ subtask_id, product_id, quantity, source: "auto_fill""search" }`
subtask_material_deletedKTV xóa vật tư khỏi subtask{ subtask_id, product_id }Track vật tư bị bỏ thường xuyên

B9) Tooltip Dictionary

Màn hìnhField/IconTooltip TextĐiều kiện hiện
SCR-01SL (input)Số lượng vật tư dự kiến sử dụng cho công việc nàyHover label "SL"
SCR-01Icon 🗑Xóa vật tư khỏi danh sáchHover icon xóa
SCR-01Badge "Ngưng KD"Sản phẩm này đã ngưng kinh doanh. Vật tư vẫn được giữ để theo dõi.Hover badge
SCR-04SL dự kiếnTổng số lượng dự kiến từ tất cả công việc conHover label "SL dự kiến"
SCR-04"(tổng hợp từ N công việc con)"Dữ liệu được cộng dồn từ vật tư của các công việc conLuôn hiện (subtitle)

B-Desktop: Wireframes

Wireframe dùng dữ liệu thực tế ngành spa. Không placeholder.

SCR-01: MaterialForm (trong form subtask)

┌─── VẬT TƯ ──────────────────────────────────────────────┐
│                                                           │
│  ┌───┬──────────────────────┬────────┬───────┬─────┬───┐ │
│  │ # │ Vật tư               │ Mã     │ ĐVT   │ SL  │   │ │
│  ├───┼──────────────────────┼────────┼───────┼─────┼───┤ │
│  │ 1 │ [img] Serum Laser X  │ SP001  │ ml    │ [2] │ 🗑│ │
│  │ 2 │ [img] Gel làm mát    │ SP042  │ tube  │ [1] │ 🗑│ │
│  │ 3 │ [img] Mask dưỡng da  │ SP018  │ miếng │ [1] │ 🗑│ │
│  └───┴──────────────────────┴────────┴───────┴─────┴───┘ │
│                                                           │
│  ┌─────────────────────────────────────────────────────┐  │
│  │ 🔍 Tìm kiếm thêm vật tư...                        │  │
│  └─────────────────────────────────────────────────────┘  │
│                                                           │
└───────────────────────────────────────────────────────────┘

SCR-01: MaterialForm — Readonly (subtask done)

┌─── VẬT TƯ ──────────────────────────────────────────────┐
│                                                           │
│  ┌───┬──────────────────────┬────────┬───────┬─────────┐ │
│  │ # │ Vật tư               │ Mã     │ ĐVT   │ SL      │ │
│  ├───┼──────────────────────┼────────┼───────┼─────────┤ │
│  │ 1 │ [img] Serum Laser X  │ SP001  │ ml    │ 2       │ │
│  │ 2 │ [img] Gel làm mát    │ SP042  │ tube  │ 1       │ │
│  └───┴──────────────────────┴────────┴───────┴─────────┘ │
│                                                           │
│  (Không cho sửa — công việc đã hoàn thành)                │
│                                                           │
└───────────────────────────────────────────────────────────┘

SCR-01: MaterialForm — Product disabled

│ 1 │ [img] Serum Laser X [Ngưng KD] │ SP001  │ ml    │ 2 │ 🗑│

SCR-04: AggregateView (task cha)

┌─── VẬT TƯ DỰ KIẾN (tổng hợp từ 2 công việc con) ───────┐
│                                                           │
│  ┌───┬──────────────────────┬────────┬───────┬──────────┐ │
│  │ # │ Vật tư               │ Mã     │ ĐVT   │ SL dự   │ │
│  │   │                      │        │       │ kiến    │ │
│  ├───┼──────────────────────┼────────┼───────┼──────────┤ │
│  │ 1 │ Serum Laser X        │ SP001  │ ml    │ 4        │ │
│  │ 2 │ Gel làm mát          │ SP042  │ tube  │ 1        │ │
│  │ 3 │ Mask dưỡng da        │ SP018  │ miếng │ 2        │ │
│  └───┴──────────────────────┴────────┴───────┴──────────┘ │
│                                                           │
└───────────────────────────────────────────────────────────┘

SCR-03: Delete Confirm Dialog (có materials)

┌─────────────────────────────────────────┐
│  Xóa công việc                          │
│                                         │
│  Bạn có chắc chắn muốn xóa công việc   │
│  "BS - Laser sắc tố nám, đồi mồi"?    │
│  (Sẽ xóa 3 vật tư đi kèm)             │
│                                         │
│              [Hủy]  [Xác nhận]          │
└─────────────────────────────────────────┘

B-Edge Cases

#Tình huốngBehaviorSCR
EC-01Auto-fill trả về 0 resultsBảng trống + text "Chưa có vật tư. Tìm kiếm để thêm."SCR-01
EC-02Search không tìm thấy productDropdown trống + text "Không tìm thấy"SCR-01
EC-03SL nhập = 0 hoặc âmValidate: SL phải > 0, hiện error inlineSCR-01
EC-04Thêm product đã có trong bảngToast warning "Vật tư đã có trong danh sách" — không thêm duplicateSCR-01
EC-05Task cha không có subtask nào có materialsẨn section "VẬT TƯ DỰ KIẾN" hoàn toànSCR-04
EC-06Task cha có order_materials cũ nhưng không có project_task_material mớiChỉ hiện bảng "VẬT TƯ (ĐÃ XUẤT KHO)"SCR-05
EC-07Manual board subtask (automate=false)MaterialForm hiển thị, auto-fill không chạy, KTV tự searchSCR-01