Appearance
UI Spec — Báo cáo Đơn hàng Voucher/Quà tặng
Ref: PRD v1.0 | Date: 19/03/2026
B1) Screen Map
| SCR | Tên | Route | Mô tả |
|---|---|---|---|
| SCR-01 | Tab Đơn hàng Voucher/Quà tặng | /r/reports/revenue_report_group (tab mới) | Bảng + summary bar + filter riêng |
B2) SCR-01: Tab Đơn hàng Voucher/Quà tặng
Layout
┌─────────────────────────────────────────────────────────────────────────────────┐
│ Revenue Report Group │
│ ┌─────────┬─────────────┬──────────────┬──────────────┬────────────┬───────────┐│
│ │Tổng ĐH │ĐH Dịch vụ │GD Dịch vụ │ĐH Sản phẩm │ĐH Thẻ TT │ Voucher/ ││
│ │ │ │ │ │ │ Quà tặng ◄││
│ └─────────┴─────────────┴──────────────┴──────────────┴────────────┴───────────┘│
├──────────────┬──────────────────────────────────────────────────────────────────┤
│ │ │
│ FILTER │ SUMMARY BAR │
│ ┌────────┐ │ ┌──────────────┬──────────────┬──────────────┬──────────────┐ │
│ │Thời gian│ │ │ Tổng lượt │ Tổng giá trị │ Tổng thực thu│ Tỷ lệ giảm │ │
│ │01/01 - │ │ │ dùng │ giảm giá │ (đơn có VC) │ trung bình │ │
│ │19/03/26 │ │ │ 256 lượt │ 45.200.000đ │ 320.500.000đ │ 12.4% │ │
│ ├────────┤ │ │ (128 đơn) │ │ │ │ │
│ │Chi nhánh│ │ └──────────────┴──────────────┴──────────────┴──────────────┘ │
│ │[Tất cả]│ │ │
│ ├────────┤ │ ┌──────────────────────────────────────────────────────────┐ │
│ │Nguồn quà│ │ │ [Xuất Excel] │ │
│ │[Tất cả]│ │ └──────────────────────────────────────────────────────────┘ │
│ ├────────┤ │ │
│ │Chiến dịch│ │ TABLE │
│ │[Chọn ]│ │ ┌──────┬────────┬─────────┬──────┬──────────────┬─────┬───────┐ │
│ │ nguồn │ │ │Mã ĐH │Ngày tạo│Khách │CN │Voucher/Quà │Loại │Số tiền│ │
│ │ trước │ │ │ │ │hàng │ │ │giảm │giảm │ │
│ ├────────┤ │ ├──────┼────────┼─────────┼──────┼──────────────┼─────┼───────┤ │
│ │Loại giảm│ │ │DH-001│15/03/26│Nguyễn │CN │Giảm 20% DV │% giảm│200.000│ │
│ │[Tất cả]│ │ │ │10:30 │Thị Mai │Quận 1│VOC-ABC123 │ │ │ │
│ ├────────┤ │ │ │ │0901xxx │ │ │ │ │ │
│ │Trạng │ │ ├──────┼────────┼─────────┼──────┼──────────────┼─────┼───────┤ │
│ │thái đơn│ │ │ ↳ │ │ │ │Mua 1 tặng 1 │DV │500.000│ │
│ │[Trừ ĐH │ │ │ │ │ │ │VOC-DEF456 │ │ │ │
│ │ đã hủy]│ │ ├──────┼────────┼─────────┼──────┼──────────────┼─────┼───────┤ │
│ └────────┘ │ │DH-002│14/03/26│Trần │CN │Giảm 100K │VNĐ │100.000│ │
│ │ │ │14:20 │Văn Hùng │Q.7 │VOC-GHI789 │ │ │ │
│ │ │ │ │0912xxx │ │ │ │ │ │
│ │ └──────┴────────┴─────────┴──────┴──────────────┴─────┴───────┘ │
│ │ (tiếp: Nguồn quà | Campaign | Tổng ĐH | Thực thu | Trạng thái) │
│ │ │
│ │ ◄ 1 2 3 ... 10 ► Hiển thị 1-20 / 256 lượt │
└──────────────┴──────────────────────────────────────────────────────────────────┘Ghi chú wireframe:
- Dòng "↳" biểu thị dòng thứ 2 cùng đơn DH-001 — dim các cột order-level, chỉ hiển thị cột item-level.
- Layout khác các tab cũ (Ref: DEC-016): Summary bar 4 cards + Export button row riêng — khác pattern 1 BoxInfo + Export cùng row của các tab hiện có. Lý do: tab này cần 4 metrics.
- Cột STT — đánh liên tục theo order_item (1, 2, 3...), không reset khi group. Consistent với các tab khác.
- BoxInfo card 1 có sub-text "(128 đơn)" — component
BoxInfohiện tại chưa support sub-text. FE cần extend hoặc custom render slot.
Filter Bar
| # | Component | Type | Default | Behavior |
|---|---|---|---|---|
| 1 | Thời gian | DateRangePicker | Tháng hiện tại | Thay đổi giá trị, bấm "Áp dụng" để reload |
| 2 | Chi nhánh | QSelect multi | Tất cả (theo RBAC) | Staff: branch mình. Manager: branch quản lý. Admin: tất cả |
| 3 | Nguồn quà | QSelect multi | Tất cả | 5 giá trị. Chọn → enable + load dropdown Chiến dịch |
| 4 | Chiến dịch/Chương trình | QSelect multi | — | Disabled + "Chọn nguồn quà trước" khi #3 chưa chọn. Loading state khi đang fetch. Reset khi #3 thay đổi |
| 5 | Loại giảm | QSelect multi | Tất cả | 6 giá trị (từ master_data). WHERE: gift: { gift_type: { _in: [...] } } |
| 6 | Trạng thái đơn | QSelect single | Trừ ĐH đã hủy | Options: Tất cả / Đang xử lý / Hoàn tất / Đã hủy / Trừ ĐH đã hủy |
| — | Nút "Áp dụng" | XBtn | — | Click → reload data + summary bar + reset page 1. KHÔNG auto-reload khi thay đổi filter (Ref: DEC-015, consistent với RevenueReportFilter pattern) |
Table
| Cột | Width | Align | Format | Sticky |
|---|---|---|---|---|
| STT | 50px | Center | Số thứ tự liên tục (1, 2, 3...), không reset khi group | No |
| Mã đơn hàng | 100px | Left | Link (blue, underline on hover) | No |
| Ngày tạo | 130px | Left | DD/MM/YYYY HH:MM | No |
| Khách hàng | 150px | Left | Tên (dòng 1) + SĐT (dòng 2, grey) | No |
| Chi nhánh | 100px | Left | Text | No |
| Voucher/Quà | 200px | Left | Tên (dòng 1) + Mã voucher (dòng 2, grey). ellipsis + QTooltip | No |
| Loại giảm | 80px | Left | Text abbreviated: "% giảm", "VNĐ", "DV", "SP", "Lời chúc", "Khác" | No |
| Số tiền giảm | 120px | Right | formatCurrency(value) + "đ" | No |
| Nguồn quà | 90px | Left | Text abbreviated: "Lắc xì", "Vòng quay", "Voucher", "Bạn bè", "Thủ công" | No |
| Campaign | 180px | Left | Text. ellipsis + QTooltip. Logic render theo gifted_from | No |
| Tổng đơn hàng | 120px | Right | formatCurrency(value) + "đ" | No |
| Thực thu | 120px | Right | formatCurrency(value) + "đ" | No |
| Trạng thái | 90px | Left | Text | No |
Permission Matrix
| Role | Xem tab | Filter chi nhánh | Export Excel |
|---|---|---|---|
| Staff | Branch mình (nếu có quyền report) | Chỉ branch mình | ✅ |
| Manager | Branch quản lý | Branches quản lý | ✅ |
| Admin | ✅ Tất cả | Tất cả | ✅ |
| Không có quyền report | ❌ Ẩn tab | ❌ | ❌ |
State Matrix
| State | Hiển thị |
|---|---|
| Loading | Summary bar: 4 BoxInfo với loading={true} (QSkeleton). Table: XTable loading={true} spinner |
| Empty | "Không có đơn hàng voucher/quà tặng trong khoảng thời gian này" centered. Summary bar: 0 / 0đ / 0đ / — |
| Error | Error message từ getNoDataMessage(error) + nút Retry. Summary bar: hiển thị "—" |
| No Permission | Ẩn tab hoàn toàn (không disable) |
B3) User Flows
Flow 1: Happy path — Xem báo cáo
Vào Revenue Report Group → Click tab "Voucher/Quà tặng"
→ Filter tự load tháng hiện tại + branch theo RBAC
→ Summary bar hiển thị 4 metrics
→ Bảng hiển thị 20 items đầu tiên
→ Scroll/phân trang để xem thêmFlow 2: Drill-down theo nguồn quà → chiến dịch
Chọn Nguồn quà = "Lắc xì"
→ Dropdown Chiến dịch enable + loading
→ Danh sách chương trình lắc xì hiện ra
→ Chọn "Lắc xì Tết 2026"
→ Data reload: chỉ hiện đơn từ chương trình đó
→ Summary bar cập nhật theo filterFlow 3: Export Excel
Áp dụng filter → Bấm "Áp dụng" → Data load xong
→ Click "Xuất Excel"
→ Kiểm tra aggregate count từ summary bar (đã có sẵn, không query thêm):
→ Nếu > 10,000 dòng:
→ Hiện confirmation dialog:
"Dữ liệu có hơn 10,000 dòng, quá trình export có thể mất vài phút. Tiếp tục?"
→ [Tiếp tục]: bắt đầu fetch toàn bộ data → build Excel → download
→ [Hủy]: abort, không fetch
→ Nếu ≤ 10,000 dòng: fetch toàn bộ → build Excel → download ngay
→ Hiện loading spinner trên nút export trong suốt quá trình fetch + buildFlow 4: Xem đơn hủy (audit)
Chọn filter Trạng thái đơn = "Đã hủy"
→ Bảng hiển thị đơn đã hủy có voucher
→ Summary bar tính trên đơn hủy
→ PO kiểm tra voucher đã được restored chưaB4) Notification Spec
Không có notification — feature chỉ đọc dữ liệu (read-only report).
B5) Export Spec
| Column | Header text | Format | Width |
|---|---|---|---|
| Mã đơn hàng | Mã đơn hàng | text | auto |
| Ngày tạo | Ngày tạo | DD/MM/YYYY HH:MM | 18 |
| Khách hàng | Khách hàng | text | auto |
| SĐT | Số điện thoại | text | 15 |
| Chi nhánh | Chi nhánh | text | auto |
| Tên voucher/quà | Tên voucher/quà | text | auto |
| Mã voucher | Mã voucher | text | 15 |
| Loại giảm giá | Loại giảm giá | text | 15 |
| Số tiền giảm | Số tiền giảm (VNĐ) | number (#,##0) | 15 |
| Nguồn quà | Nguồn quà | text | 15 |
| Campaign/Chương trình | Campaign/Chương trình | text | auto |
| Tổng đơn hàng | Tổng đơn hàng (VNĐ) | number (#,##0) | 15 |
| Thực thu | Thực thu (VNĐ) | number (#,##0) | 15 |
| Trạng thái | Trạng thái | text | 12 |
- File name:
don-hang-voucher-qua-tang_DD-MM-YYYY.xlsx - Row cap: Không giới hạn (follow pattern các tab hiện có)
- Warning > 10,000 dòng: Hiển thị confirmation dialog "Dữ liệu có hơn 10,000 dòng, quá trình export có thể mất vài phút. Tiếp tục?" → [Tiếp tục] / [Hủy]
- Header style: Bold, background color (giống pattern hiện có)
B6) Copy Text Dictionary
| Key | Vietnamese | English (i18n) | Context |
|---|---|---|---|
tab_voucher_gift_order | Đơn hàng Voucher/Quà tặng | Voucher/Gift Orders | Tab label |
summary_total_usage | Tổng lượt dùng | Total Usage | Summary card 1 |
summary_total_discount | Tổng giá trị giảm giá | Total Discount Value | Summary card 2 |
summary_total_revenue | Tổng thực thu | Total Net Revenue | Summary card 3 |
summary_avg_rate | Tỷ lệ giảm trung bình | Average Discount Rate | Summary card 4 |
filter_gift_source | Nguồn quà | Gift Source | Filter label |
filter_campaign | Chiến dịch/Chương trình | Campaign/Program | Filter label |
filter_gift_type | Loại giảm | Discount Type | Filter label |
filter_order_status | Trạng thái đơn | Order Status | Filter label |
filter_campaign_placeholder | Chọn nguồn quà trước | Select gift source first | Disabled placeholder |
filter_campaign_na | Không áp dụng | Not applicable | When source has no campaign |
empty_text | Không có đơn hàng voucher/quà tặng trong khoảng thời gian này | No voucher/gift orders found | Empty state |
unit_usage | lượt | usage(s) | Count unit |
unit_order | đơn | order(s) | Count unit |
B7) Analytics Events
| Event | Trigger | Properties | KPI Link |
|---|---|---|---|
report_voucher_gift_tab_view | Click tab | branch_id, user_role | User adoption |
report_voucher_gift_filter | Apply filter | filter_type, filter_value | Most used filters |
report_voucher_gift_export | Click export | row_count, filter_applied | Export usage |
B8) Responsive Rules
| Breakpoint | Layout Behavior |
|---|---|
| Desktop (≥1024px) | Full layout: sidebar filter 350px + content area |
| Tablet (768-1023px) | Sidebar collapse thành drawer. Bảng horizontal scroll |
| Mobile (<768px) | Không hỗ trợ — admin report chỉ dùng trên desktop |
B9) Tooltip Dictionary
| Màn hình | Field/Icon | Tooltip Text | Điều kiện hiện |
|---|---|---|---|
| SCR-01 | Tổng lượt dùng | Số lượt sử dụng voucher/quà tặng trong đơn hàng. Một đơn có thể có nhiều lượt. | Hover icon ℹ️ |
| SCR-01 | Tỷ lệ giảm TB | Tổng giá trị giảm ÷ (Tổng thực thu + Tổng giá trị giảm) × 100%. Chỉ tính phần voucher/gift. | Hover icon ℹ️ |
| SCR-01 | Cột Voucher/Quà (ellipsis) | Hiển thị full tên + mã voucher | Hover cell bị cắt |
| SCR-01 | Cột Campaign (ellipsis) | Hiển thị full tên campaign/chương trình | Hover cell bị cắt |
| SCR-01 | Filter Chiến dịch (disabled) | Vui lòng chọn Nguồn quà trước để lọc theo chiến dịch | Hover khi disabled |
B-Edge Cases
| # | Case | Expected Behavior |
|---|---|---|
| 1 | 1 đơn có 5 order_item dùng voucher | 5 dòng, dim repeated order-level cells ở dòng 2-5. Pagination đếm 5 items |
| 2 | Voucher không còn liên kết campaign | Cột Campaign hiển thị "—" |
| 3 | Gift type "Lời chúc" | Hiển thị bình thường, Số tiền giảm = 0đ |
| 4 | Filter "Bạn bè" + "Thủ công" (không có campaign) | Dropdown Chiến dịch disabled + "Không áp dụng" |
| 5 | Chọn nhiều nguồn quà cùng lúc | Gộp tất cả campaigns của các nguồn đã chọn vào dropdown |
| 6 | Date range 1 năm → dữ liệu rất nhiều | Pagination bảo vệ. Summary bar có thể chậm → loading skeleton |
| 7 | Tất cả đơn trong range đều đã hủy | Bảng empty state. Summary bar: 0 / 0đ / 0đ / — |
| 8 | Export > 10,000 dòng | Check aggregate count TRƯỚC khi fetch. Hiện confirmation dialog. "Tiếp tục" → fetch all + export. "Hủy" → abort |
| 9 | Đổi nguồn quà khi đã chọn campaign | Reset campaign dropdown + clear selected values |
| 10 | User Staff không có quyền report | Ẩn toàn bộ tab (không disable) |
| 11 | Chọn nguồn hỗn hợp (có campaign + không campaign), VD: "Lắc xì" + "Bạn bè" | Dropdown Chiến dịch enable, load campaigns của Lắc xì. Items từ "Bạn bè" vẫn hiện trong bảng bất kể filter campaign |