Appearance
Design Doc: Cải thiện UX cài đặt Mã Công Việc / Tiền Tour
| Field | Value |
|---|---|
| Feature | Settings UX — Subtask Config + Tour Fee |
| Version | 1.1 |
| Date | 2026-03-26 |
| Status | Draft |
| Audience | IT Leader, IT Staff, Admin, PO |
1. Bối cảnh & Vấn đề
Hiện trạng
Hệ thống có 3 trang cài đặt riêng biệt để cấu hình công việc và tiền tour:
| Trang | Route | DB | Mục đích |
|---|---|---|---|
| Công Việc (subtask config) | /s/internal-settings/subtask | ecommerce.subtask | 275+ mã công việc (SUB280, SUB279...) |
| Tiền Tour (task tag) | /s/internal-settings/task-tag | project.project_task_tag | Tag tiền tour theo cấp bậc |
| Nhóm Tiền Tour (group) | /s/internal-settings/group-task-tag | project.project_task_group_tag | Nhóm chứa tags |
Quan hệ dữ liệu:
project_task_group_tag (nhóm: "Tiền tour 2024")
└── project_task_tag (tag: T26 "100k", T15 "Laser CO2")
└── tag_tour_moneys (Cấp độ 1: 100,000đ, Cấp độ 2: 100,000đ...)
subtask (mã: SUB280 "Tạo hình tai vểnh")
└── subtask_tag (junction) → project_task_tagPain points
| # | Vấn đề | Mức độ | Ảnh hưởng |
|---|---|---|---|
| P1 | Workflow phân mảnh: Phải navigate 3 trang để cấu hình 1 luồng nghiệp vụ. Tạo group → sang trang khác tạo tag → sang trang khác gán vào subtask | Nặng nhất | IT Staff |
| P2 | Form subtask vô nghĩa: Field "Danh sách tag" hiện chip tag chỉ có tên (VD: "Ok", "100k") — không biết nhóm nào, tiền tour bao nhiêu | Nặng | IT Staff, Admin/PO |
| P3 | Ngôn từ lẫn lộn: Sidebar gọi "Công Việc" (trùng với tab Công Việc ở chi tiết khách hàng), code gọi "Task Tag" nhưng UI gọi "Tiền Tour" | Trung bình | Tất cả |
| P4 | List subtask khó tìm: 275+ items, chỉ filter theo loại + trạng thái, không filter/hiện tag tiền tour | Trung bình | IT Staff |
| P5 | Tên tag = giá trị tiền: IT Staff đặt tên tag = "100k", "Ok" vì UI không cung cấp đủ context → naming convention bị phá | Nhẹ | IT Staff |
| P6 | Cột bảng subtask bị swap label: Cột 3 label "Loại công việc" nhưng hiển thị type, cột 4 label "Tên công việc" nhưng hiển thị name/description | Nhẹ | IT Staff |
Đối tượng sử dụng
| Role | Hành vi | Tần suất |
|---|---|---|
| IT Leader / IT Staff | Cấu hình: tạo/sửa/xóa subtask config, tag, group | Vài lần/tháng |
| Admin / PO | Xem tham khảo: kiểm tra subtask nào dùng tag nào, tiền tour bao nhiêu | Thỉnh thoảng |
2. Quyết định thiết kế
D1: Gộp Group + Tag thành 1 trang
- Quyết định: Gộp 2 trang "Nhóm Tiền Tour" + "Tiền Tour" thành 1 trang "Cấu Hình Tiền Tour"
- Lý do: Group và Tag có quan hệ cha-con chặt, cùng project DB, cùng mục đích (tiền tour). Tách riêng buộc user navigate qua lại không cần thiết.
- Trade-off: Component phức tạp hơn (accordion + nested table) nhưng giảm 3 trang → 2 trang.
D2: Giữ trang Subtask riêng
- Quyết định: Không gộp subtask vào trang tiền tour
- Lý do: Subtask config thuộc ecommerce DB (khác domain), 275+ items, có fields riêng (loại, mô tả). Gộp sẽ overload trang.
- Trade-off: Vẫn 2 trang nhưng mỗi trang có mục đích rõ ràng.
D3: Tag chip hiện số tiền thay vì tên
- Quyết định: Trong tất cả nơi hiện tag chip/text, hiện
[Mã — Range tiền]thay vì chỉ tên tag. - Lý do: Tên tag hiện tại vô nghĩa ("Ok", "100k"). Số tiền là thông tin mọi người thực sự cần.
- Trade-off: Chip dài hơn nhưng có ý nghĩa.
- Blast radius: TaskTagSelect dùng ở 2 nơi (settings SubTaskForm + projects TaskForm), TaskTagDisplay dùng ở 3 nơi (SubtaskDetail + OrderTaskLimitForm + ServiceSubtaskItem). Tất cả đều hưởng lợi từ enriched display.
D4: Không inline-create tag trong form subtask
- Quyết định: Không cho tạo tag mới ngay trong form subtask. Chỉ cung cấp link "Xem chi tiết ↗" mở tab mới.
- Lý do: Tạo tag cần cấu hình tiền tour theo 5 cấp bậc (form phức tạp). Tần suất tạo tag thấp (vài lần/tháng). Link mở tab mới đủ tiện.
D5: Rename sidebar + labels
- Quyết định: "Công Việc" → "Mã Công Việc", gộp "Nhóm Tiền Tour" + "Tiền Tour" → "Cấu Hình Tiền Tour"
- Lý do: "Công Việc" trùng với tab Công Việc ở chi tiết khách hàng. "Mã Công Việc" khớp với cột "MÃ CÔNG VIỆC" trong bảng.
D6: Giữ route name cũ hoạt động
- Quyết định:
ROUTE_SETTING_TASK_TAG_DETAILvẫn resolve được (redirect sang trang mới). Không cần sửa 2 file ecommerce. - Lý do: OrderTaskLimitForm và ServiceSubtaskItem dùng route name này để mở link. Redirect ở router level là đủ, không cần sửa từng consumer.
3. Thiết kế chi tiết
3.1 Trang "Cấu Hình Tiền Tour" (gộp Group + Tag)
Route: /s/internal-settings/tour-fee-config
Thay thế 2 trang:
/s/internal-settings/group-task-tag(xóa)/s/internal-settings/task-tag(xóa)
Layout:
┌─── CẤU HÌNH TIỀN TOUR ────────────────────────────────────────────┐
│ │
│ [🔍 Tìm theo mã, tên, số tiền...] [Nhóm: Tất cả ▼] [+ Nhóm] │
│ │
│ ┌─── Tiền tour 2024 ──────────────────────── [Sửa nhóm] [▼] ──┐ │
│ │ │ │
│ │ ┌────┬───────┬─────────┬───────────────────┬───────┬─────┐ │ │
│ │ │ Mã │ Tên │ Range │ Chi tiết │T.thái │ │ │ │
│ │ ├────┼───────┼─────────┼───────────────────┼───────┼─────┤ │ │
│ │ │T26 │ 100k │100,000đ │ CĐ1-5: đều 100k │ ✅ │[Sửa]│ │ │
│ │ │T15 │LaserX │50k-120k │ CĐ1:50k CĐ5:120k │ ✅ │[Sửa]│ │ │
│ │ │T08 │ Ok │ 80,000đ │ CĐ1-5: đều 80k │ ❌ │[Sửa]│ │ │
│ │ └────┴───────┴─────────┴───────────────────┴───────┴─────┘ │ │
│ │ │ │
│ │ [+ Thêm tag tiền tour] │ │
│ │ │ │
│ │ Đang dùng bởi: 45 mã công việc [Xem ↗] │ │
│ └────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─── Tiền tour 2023 ──────────────────────── [Sửa nhóm] [▶] ──┐ │
│ │ 12 tags · 8 đang hoạt động │ │
│ └────────────────────────────────────────────────────────────────┘ │
│ │
└──────────────────────────────────────────────────────────────────────┘Hành vi:
| Action | Behavior |
|---|---|
| Click ▶/▼ nhóm | Toggle expand/collapse |
| Search | Tìm cả tên nhóm lẫn tên/mã tag |
| [+ Nhóm] | Dialog tạo nhóm (code + name) |
| [+ Thêm tag tiền tour] | Side dialog tạo tag, nhóm pre-fill |
| [Sửa] tag | Side dialog sửa tag (giữ nguyên form hiện tại) |
| [Sửa nhóm] | Inline edit (code readonly, name editable) — group chỉ có 2 fields, không cần dialog |
| [Xem ↗] | Expand danh sách subtask configs đang dùng tags trong nhóm |
Expand "Đang dùng bởi":
Đang dùng bởi: 45 mã công việc [Thu gọn ▲]
┌──────────┬──────────────────────────┬──────────────┐
│ Mã │ Tên công việc │ Tags │
├──────────┼──────────────────────────┼──────────────┤
│ SUB280 │ Tạo hình tai vểnh │ T08 (Ok) │
│ SUB156 │ Laser Q-switch │ T26, T15 │
│ SUB089 │ Chăm sóc cơ bản │ T26 │
└──────────┴──────────────────────────┴──────────────┘Cột "Range" logic:
- Flat-rate (tất cả cấp bậc = nhau): "100,000đ"
- Multi-rate: "50k — 120k" (min — max)
- Chưa cấu hình: "—"Cột "Chi tiết" logic:
- Flat-rate: "CĐ1-5: đều 100k"
- Multi-rate: "CĐ1:50k CĐ2:60k ... CĐ5:120k"
- Chưa cấu hình: "Chưa có tiền tour"3.2 Cải thiện form "Chỉnh sửa Mã Công Việc" (Settings)
File: settings/components/subtask/SubTaskForm.tsx
A. Label rename:
| Hiện tại | Sau |
|---|---|
| Title: "CHỈNH SỬA CÔNG VIỆC" | "CHỈNH SỬA MÃ CÔNG VIỆC" |
| Field: "Danh sách tag" | "Tag tiền tour" |
B. Tag chip enriched:
Hiện tại: [Ok ×] [100k ×]
Đề xuất: [T08 — 80,000đ ×] [T26 — 100,000đ ×]
↑ mã ↑ flat ↑ mã ↑ flat
[T15 — 50k~120k ×]
↑ mã ↑ multi-rate rangeC. Hover chip → tooltip:
┌──────────────────────────────┐
│ T26 — 100k │
│ Nhóm: Tiền tour 2024 │
│ │
│ Cấp độ 1 100,000đ │
│ Cấp độ 2 100,000đ │
│ Cấp độ 3 100,000đ │
│ Cấp độ 4 100,000đ │
│ Cấp độ 5 100,000đ │
│ │
│ [Xem chi tiết ↗] │
└──────────────────────────────┘- "Xem chi tiết ↗" → mở
/internal-settings/tour-fee-config?tag={id}(trang mới) focus vào tag đó
D. Dropdown search cải thiện (TaskTagSelect):
┌───────────────────────────────────────────────┐
│ 🔍 Tìm tag tiền tour... │
├───────────────────────────────────────────────┤
│ T26 100k 100,000đ (Tiền tour 2024)│
│ T15 Laser CO2 50k~120k (Tiền tour 2024)│
│ T08 Ok 80,000đ (Tiền tour 2024)│
│ T03 Massage 30k~60k (CHĂM SÓC DA) │
└───────────────────────────────────────────────┘
↑ mã ↑ tên ↑ range ↑ nhóm3.3 Cải thiện bảng list Mã Công Việc (Settings)
File: settings/components/subtask/SubTaskTable.tsx
Thêm cột + filter + fix label swap:
TRƯỚC (hiện tại):
┌────┬────────┬──────────┬─────────────────────┬────────┐
│ # │ Mã CVC │ Loại CVC │ Tên công việc │ T.thái │ ← không có cột tag
└────┴────────┴──────────┴─────────────────────┴────────┘
↑ label sai (hiện type nhưng ghi "Loại công việc")
SAU:
┌────┬────────┬──────────┬─────────────────────┬─────────────────────┬────────┐
│ # │ Mã │ Loại │ Tên công việc │ Tag tiền tour │ T.thái │
├────┼────────┼──────────┼─────────────────────┼─────────────────────┼────────┤
│ 1 │ SUB280 │ Dịch vụ │ Tạo hình tai vểnh │ T08: 80,000đ │ ✅ │
│ 2 │ SUB279 │ Dịch vụ │ BS- Meso da chất... │ T26: 100k, T15: 50k~120k│ ✅ │
│ 3 │ SUB089 │ Thủ công │ Chăm sóc cơ bản │ — │ ✅ │
└────┴────────┴──────────┴─────────────────────┴─────────────────────┴────────┘
↑ CỘT MỚIFilter bar mở rộng:
TRƯỚC: [🔍 Tìm kiếm...] [Bộ lọc 🔽] (chỉ loại + trạng thái)
SAU: [🔍 Tìm kiếm...] [Loại ▼] [Nhóm tiền tour ▼] [Tag tiền tour ▼] [Trạng thái ▼]3.4 Rename tổng hợp
| Vị trí | Hiện tại | Sau | Lý do |
|---|---|---|---|
| Sidebar menu | Công Việc | Mã Công Việc | Phân biệt với "Công Việc" ở tab khách hàng |
| Sidebar menu | Nhóm Tiền Tour | (xóa) | Gộp |
| Sidebar menu | Tiền Tour | Cấu Hình Tiền Tour | 1 trang thay 2 |
| Breadcrumb | Quản Lý Công Việc | Giữ nguyên | Parent section vẫn đúng |
| Form title (settings) | CHỈNH SỬA CÔNG VIỆC | CHỈNH SỬA MÃ CÔNG VIỆC | Rõ nghĩa |
| Form field (settings) | Danh sách tag | Tag tiền tour | Rõ nghĩa |
| Table header (settings) | (không có cột tag) | Tag tiền tour | Thêm mới |
4. Ảnh hưởng hiển thị ngoài Settings (Blast Radius)
TaskTagSelect và TaskTagDisplay được dùng ở 3 nơi ngoài settings module. Tất cả đều hiển thị tag tiền tour và đều hưởng lợi từ enriched display.
4.1 TaskForm — Tạo/sửa subtask trong Projects
File: projects/components/TaskForm/index.tsx:2108-2131Context: Form tạo/sửa subtask trong tab Công Việc ở chi tiết khách hàng. KTV chọn "Loại tiền tour" cho subtask. Component: TaskTagSelect (single select, useBox=false)
TRƯỚC:
┌─── TẠO CÔNG VIỆC CON ───────────────────────────────┐
│ │
│ Loại tiền tour * │
│ ┌──────────────────────────────────────────────┐ │
│ │ Ok ▼ │ │
│ └──────────────────────────────────────────────┘ │
│ ↑ Chỉ hiện tên "Ok" — KTV không biết tiền bao nhiêu │
│ │
└────────────────────────────────────────────────────────┘
SAU:
┌─── TẠO CÔNG VIỆC CON ───────────────────────────────┐
│ │
│ Loại tiền tour * │
│ ┌──────────────────────────────────────────────┐ │
│ │ T08 — Ok — 80,000đ (Tiền tour 2024) ▼ │ │
│ └──────────────────────────────────────────────┘ │
│ ↑ Hiện mã + tên + range tiền + nhóm │
│ │
│ Dropdown khi mở: │
│ ┌──────────────────────────────────────────────┐ │
│ │ T26 100k 100,000đ (Tiền tour 2024) │ │
│ │ T15 Laser CO2 50k~120k (Tiền tour 2024) │ │
│ │ T08 Ok 80,000đ (Tiền tour 2024) │ │
│ └──────────────────────────────────────────────┘ │
│ │
└────────────────────────────────────────────────────────┘Lợi ích: KTV chọn đúng tag tiền tour dựa trên số tiền thực tế, không phải đoán từ tên vô nghĩa. Label: Giữ nguyên "Loại tiền tour" (đã đúng nghĩa ở context này). Thay đổi: Chỉ ở component TaskTagSelect — hiện enriched option format.
4.2 OrderTaskLimitForm — Hiển thị tag trong form đơn hàng
File: ecommerce/components/order/OrderForm/OrderTaskLimitForm.tsx:186-228Context: Hiển thị danh sách tag tiền tour của subtask trong form giới hạn công việc của đơn hàng. Component: Custom render (không dùng TaskTagSelect, render QText trực tiếp)
TRƯỚC:
┌─── Giới hạn công việc ──────────────────────────────┐
│ │
│ Tag: Ok, 100k │
│ ↑ text xanh (link cho IT), đen (role khác) │
│ │
└────────────────────────────────────────────────────────┘
SAU:
┌─── Giới hạn công việc ──────────────────────────────┐
│ │
│ Tag: T08 (80,000đ), T26 (100,000đ) │
│ ↑ hiện mã + range tiền, vẫn giữ link cho IT │
│ │
└────────────────────────────────────────────────────────┘Lợi ích: Manager/PO xem đơn hàng thấy ngay tiền tour, không cần click vào từng tag. Thay đổi: Sửa render logic — thay tag.name bằng ${tag.code} (${range}). Route link: Vẫn dùng ROUTE_SETTING_TASK_TAG_DETAIL → redirect sang /tour-fee-config?tag={id}.
4.3 ServiceSubtaskItem — Hiển thị tag trong cài đặt dịch vụ
File: ecommerce/components/service/ServiceForm/ServiceSubtaskItem.tsx:110-140Context: Hiển thị tag tiền tour gắn với subtask trong form cài đặt dịch vụ (Ecommerce module). Component: Custom render (QText trực tiếp, tương tự OrderTaskLimitForm)
TRƯỚC:
┌─── Subtask: BS - Laser sắc tố nám ─────────────────┐
│ │
│ Tag: Ok │
│ ↑ chỉ hiện tên, link xanh cho IT │
│ │
└────────────────────────────────────────────────────────┘
SAU:
┌─── Subtask: BS - Laser sắc tố nám ─────────────────┐
│ │
│ Tag: T08 (80,000đ) │
│ ↑ mã + range tiền, link xanh cho IT │
│ │
└────────────────────────────────────────────────────────┘Lợi ích: Khi cấu hình dịch vụ, IT Staff thấy ngay subtask dùng tag tiền tour bao nhiêu. Thay đổi: Sửa render logic — thay tag.name bằng ${tag.code} (${range}). Route link: Vẫn dùng ROUTE_SETTING_TASK_TAG_DETAIL → redirect.
4.4 TaskTagDisplay — Component hiển thị tag dùng chung
File: settings/components/task-tag/TaskTagDisplay.tsxContext: Component reusable, hiện dùng ở SubtaskDetail (settings). Cũng bị ảnh hưởng gián tiếp nếu OrderTaskLimitForm và ServiceSubtaskItem chuyển sang dùng component này.
TRƯỚC:
Ok, 100k (hoặc "--" nếu không có tag)
SAU:
T08 (80,000đ), T26 (100,000đ) (hoặc "--" nếu không có tag)Thay đổi: Sửa display format trong TaskTagDisplay — cần truyền thêm tag_tour_moneys data.
4.5 Tổng hợp tất cả nơi bị ảnh hưởng
| # | Nơi | Module | Component | Loại thay đổi | Risk |
|---|---|---|---|---|---|
| 1 | Trang Cấu Hình Tiền Tour | settings | NEW TourFeeConfig | Trang mới | Low — không đụng gì cũ |
| 2 | Form Chỉnh sửa Mã Công Việc | settings | SubTaskForm | Rename label + enriched chip | Low |
| 3 | Bảng list Mã Công Việc | settings | SubTaskTable | Thêm cột + filter + fix label | Low |
| 4 | Form Tạo/sửa subtask | projects | TaskForm via TaskTagSelect | Enriched dropdown option | Medium — KTV sẽ thấy format mới |
| 5 | Form đơn hàng — giới hạn CV | ecommerce | OrderTaskLimitForm | Tag text hiện mã + range | Medium — Manager thấy format mới |
| 6 | Cài đặt dịch vụ — subtask | ecommerce | ServiceSubtaskItem | Tag text hiện mã + range | Medium — IT Staff thấy format mới |
| 7 | Chi tiết Mã Công Việc | settings | SubtaskDetail via TaskTagDisplay | Tag text hiện mã + range | Low |
| 8 | Sidebar menu | settings | module.ts | Rename + gộp menu items | Low |
| 9 | Route redirects | settings | module.ts | Redirect routes cũ → mới | Low — transparent |
5. Impact Map
Frontend — Settings module
| File | Thay đổi | Effort |
|---|---|---|
settings/module.ts | Thêm route /tour-fee-config, redirect routes cũ, rename sidebar | 0.5d |
settings/types.ts | Thêm route constants mới | 0.1d |
settings/pages/TourFeeConfig.tsx | NEW — Trang gộp Group + Tag accordion | 2d |
settings/components/tour-fee/TourFeeGroupAccordion.tsx | NEW — Accordion nhóm chứa tags | 1.5d |
settings/components/tour-fee/TourFeeTagTable.tsx | NEW — Bảng tags trong nhóm (range, chi tiết, trạng thái) | 1d |
settings/components/tour-fee/TourFeeUsageExpand.tsx | NEW — "Đang dùng bởi N mã công việc" expandable | 0.5d |
settings/components/task-tag/TaskTagSelect.tsx | Enriched dropdown: hiện mã + range + nhóm, enriched chip | 1d |
settings/components/task-tag/TaskTagDisplay.tsx | Enriched display: code (range) thay vì name | 0.25d |
settings/components/task-tag/TaskTagForm.tsx | Giữ nguyên logic, reuse trong trang mới | 0.25d |
settings/components/subtask/SubTaskForm.tsx | Rename label "Danh sách tag" → "Tag tiền tour" | 0.1d |
settings/components/subtask/SubTaskTable.tsx | Thêm cột "Tag tiền tour" + filter + fix label swap | 1d |
settings/pages/SubTask.tsx | Rename title | 0.1d |
Frontend — Ngoài settings (blast radius)
| File | Thay đổi | Effort |
|---|---|---|
ecommerce/components/order/OrderForm/OrderTaskLimitForm.tsx | Sửa tag display format: tag.name → ${tag.code} (${range}) | 0.25d |
ecommerce/components/service/ServiceForm/ServiceSubtaskItem.tsx | Sửa tag display format: tag.name → ${tag.code} (${range}) | 0.25d |
projects/components/TaskForm/index.tsx | Không sửa — tự động thay đổi qua TaskTagSelect | 0d |
GraphQL
| Query | Thay đổi |
|---|---|
Fragment TaskTag (task-tag.graphql) | Thêm tag_tour_moneys { seniority, tour_money } |
Fragment Subtask (subtask.graphql) | Thêm subtask_tags { tag { code, tag_tour_moneys { tour_money } } } |
GetTaskTagGroup | Thêm nested project_task_tags { code, tag_tour_moneys { tour_money } } |
Backend / Hasura
ZERO thay đổi — chỉ FE restructure. Tất cả data đã có sẵn qua existing relationships.
6. Data Flow
Trang Cấu Hình Tiền Tour
TourFeeConfig page
├── GetTaskTagGroup (list nhóm + nested tags + tour_moneys)
├── Accordion render
│ ├── TourFeeGroupAccordion (per group)
│ │ ├── TourFeeTagTable (tags trong nhóm)
│ │ │ ├── Range column: computed từ tag_tour_moneys (min/max hoặc flat)
│ │ │ └── Chi tiết column: formatted tour_moneys by seniority
│ │ └── TourFeeUsageExpand
│ │ └── GetSubtaskConfig WHERE subtask_tags.tag_id IN group's tag IDs
│ └── [Sửa tag] → TaskTagForm (reuse component hiện tại)
└── Search: filter cả group name + tag name/codeForm Subtask (Settings) — Tag Chip Enriched
SubTaskForm
├── TaskTagSelect (sửa)
│ ├── Query GetTaskTag + tag_tour_moneys (MỚI)
│ ├── Dropdown item: render [Mã] [Tên] [Range] [Nhóm]
│ └── Selected chip: render [Mã — Range ×]
├── Tooltip on chip hover
│ ├── Show: tên, nhóm, bảng tiền tour 5 cấp
│ └── Link "Xem chi tiết ↗" → /tour-fee-config?tag={tagId}
└── Label: "Tag tiền tour"TaskForm (Projects) — Tự động thay đổi
TaskForm (projects module)
├── TaskTagSelect (CÙNG component — tự động enriched)
│ ├── Dropdown: [Mã] [Tên] [Range] [Nhóm]
│ └── Selected: hiện enriched format
└── Label: "Loại tiền tour" (GIỮU NGUYÊN)OrderTaskLimitForm + ServiceSubtaskItem — Display enriched
OrderTaskLimitForm / ServiceSubtaskItem
├── Tag data: tag.code + tag.tag_tour_moneys (CẦN thêm vào data source)
├── Display: "${tag.code} (${computeRange(tag.tag_tour_moneys)})"
└── Link: ROUTE_SETTING_TASK_TAG_DETAIL → redirect → /tour-fee-config?tag={id}7. Migration Plan (URL redirects)
Để không break bookmarks, links, và external references:
Route path redirects
| Route cũ | Redirect đến |
|---|---|
/s/internal-settings/group-task-tag | /s/internal-settings/tour-fee-config |
/s/internal-settings/group-task-tag/:id | /s/internal-settings/tour-fee-config?group={id} |
/s/internal-settings/task-tag | /s/internal-settings/tour-fee-config |
/s/internal-settings/task-tag/:id | /s/internal-settings/tour-fee-config?tag={id} |
/s/internal-settings/task-tag/:id/edit | /s/internal-settings/tour-fee-config?tag={id}&edit=true |
Route name mapping (QUAN TRỌNG)
| Route name cũ | Behavior |
|---|---|
ROUTE_SETTING_TASK_TAG_DETAIL | Redirect sang /tour-fee-config?tag={id} — PHẢI giữ vì OrderTaskLimitForm + ServiceSubtaskItem dùng |
ROUTE_SETTING_TASK_TAG | Redirect sang /tour-fee-config |
ROUTE_SETTING_TASK_TAG_GROUP | Redirect sang /tour-fee-config |
8. Effort Estimate
| Nhóm | Tasks | Estimate |
|---|---|---|
| Trang Cấu Hình Tiền Tour (gộp) | Page + Accordion + TagTable + UsageExpand | 5d |
| TaskTagSelect enriched | Dropdown + chip + tooltip + GraphQL | 1.5d |
| TaskTagDisplay enriched | Display format + data | 0.25d |
| Cải thiện form Subtask (settings) | Rename label | 0.1d |
| Cải thiện bảng Subtask (settings) | Cột tag + filters + fix label swap | 1d |
| Blast radius — ecommerce | OrderTaskLimitForm + ServiceSubtaskItem display format | 0.5d |
| Rename + redirect | Sidebar + titles + route redirects + route name mapping | 0.5d |
| GraphQL adjustments | Fragment mở rộng + codegen | 0.5d |
| Testing + polish | Cross-module test, responsive, edge cases | 1.5d |
| Tổng | ~10.85d |
9. Risks
| # | Risk | Impact | Mitigation |
|---|---|---|---|
| R1 | Accordion performance nếu nhiều groups × nhiều tags | Medium | Lazy load tags khi expand group, không load all upfront |
| R2 | "Đang dùng bởi" query chậm (cross-DB) | Medium | Query chỉ khi user click "Xem ↗", không auto-load |
| R3 | Tooltip hiện bảng tiền tour cần thêm query | Low | Prefetch tag_tour_moneys trong fragment GetTaskTag |
| R4 | Route redirects bị miss — 2 file ecommerce dùng route NAME | High | Giữ ROUTE_SETTING_TASK_TAG_DETAIL name mapping trong router (D6) |
| R5 | SubTaskTable 275+ rows thêm nested tag_tour_moneys → payload lớn | Medium | Chỉ query tag.code + tag_tour_moneys { tour_money } (không cần seniority/name cho list). Hoặc compute range server-side |
| R6 | TaskTagSelect enriched chip quá dài khi subtask có 3+ tags | Low | Max-width chip + ellipsis, hover để xem full. Test responsive |
| R7 | TaskForm (projects) tự động đổi format — KTV chưa quen | Low | Format mới rõ nghĩa hơn — chỉ cần thông báo thay đổi |
10. Phạm vi KHÔNG thay đổi
- Form tạo/sửa tag tiền tour (TaskTagForm): giữ nguyên — chỉ đổi nơi render
- Form tạo/sửa nhóm (TaskTagGroupForm): giữ nguyên
- Database schema: ZERO thay đổi
- Hasura metadata / permissions: ZERO thay đổi
- Backend Go services: ZERO thay đổi
- Event triggers (assignee insert/update, task_log): ZERO thay đổi
- Logic tính tiền tour: ZERO coupling — snapshot tại event trigger, không đọc từ UI
- Logic tính lương: ZERO coupling — dùng ecommerce_task_log.tour_fee đã snapshot
- Trang cài đặt khác: ZERO ảnh hưởng
11. Verification — Tiền tour runtime KHÔNG bị ảnh hưởng
[Settings UI — CÁI CHÚNG TA SỬA]
project_task_tag + tag_tour_money → chỉ thay đổi cách HIỂN THỊ
↓
(data DB giữ nguyên)
↓
[Event Trigger — KHÔNG SỬA]
project_task_assignee_insert.go:176-200
→ Query tag_tour_moneys BY seniority → SET tour_money (1 lần)
↓
[Salary — KHÔNG SỬA]
project_task_assignee_update.go:260-447
→ ecommerce_task_log.tour_fee (snapshot)
→ INCREMENT user_salary.tour_money3 lớp bảo vệ:
- Snapshot: tour_money SET 1 lần khi assign, salary dùng giá trị đã lưu
- Server-side: Event triggers chạy trong Go, không qua UI
- FE-only changes: Design chỉ sửa Vue components + GraphQL SELECT fields