Appearance
Đặc tả UI — Tab Chu kỳ khách ghé không mua
Version: 2.8
Date: 11/05/2026
Canonical Inputs: SOURCE_OF_TRUTH.md, EVIDENCE_PACK.md, prd.md.
v2.8 — 11/05/2026
| Thay đổi | Section | Ảnh hưởng |
|---|---|---|
| Thêm UI-009 info strip vào inventory | B0.1) Inventory | FE |
| Thêm 9 row delta NEW (sub-text + column rename T6) | B0.2) Field delta | FE |
| Wireframe B2 redrawn — info strip + sub-text overview/matrix + column rename T6 | B2) SCR-01 | FE, UI/UX |
| Wireframe B3 popup updated — ℹ️ icon cạnh 6 column header | B3) SCR-02 | FE, UI/UX |
| Thêm 9 i18n keys mới vào dictionary | B6) Từ điển | FE |
| B9 tooltip rewrite gộp 19 required (bỏ phân tách Phase 1 + Backlog) | B9) Tooltip | FE, QA |
| Thêm 4 row B-PITFALLS phòng tránh confusion v2.8 | B-PITFALLS | All |
| Thêm B0.6 style guideline (info strip + sub-text + tooltip icon) cho designer + FE | B0.6) Style guideline v2.8 | Designer, FE |
File này mô tả màn hình và delta UI. SOURCE_OF_TRUTH.md thắng khi có conflict về scope; PRD thắng khi có conflict về FR/công thức.
B-PRE) Discovery checklist
| Hạng mục | Kết quả |
|---|---|
| Route/component hiện có | /r/reports/customer_cycle_report_group, CustomerCycleReport.tsx |
| Tab hiện có | Tổng quan, Chu kỳ mua hàng, Chu kỳ khách hàng |
| Filter hiện có | Loại chu kỳ, Bước chu kỳ, thời gian, Chi nhánh, Đặt lại |
| Matrix hiện có | Header 2 tầng, row hardcode, click mở popup |
| Popup hiện có | Search + filter dịch vụ + filter nhóm dịch vụ + export + table purchase |
| Permission | Theo report group hiện tại; action role user; branch scope từ account branches |
| Export | Deferred phase 1 |
| Mobile/responsive | Matrix có scroll; popup cần width responsive |
| Copy review | Bắt buộc Vietnamese-first, không dùng wording purchase |
B0) As-Is UI Inventory và Delta Contract
B0.1) Inventory đầy đủ
| UI ID | Màn | Thành phần hiện tại | Evidence | Delta Status | Target |
|---|---|---|---|---|---|
| UI-001 | SCR-01 | Tab bar 3 tab | CustomerCycleReport.tsx | UPDATE | Thêm tab Chu kỳ khách ghé không mua |
| UI-002 | SCR-01 | localStorage.currentTab whitelist 3 key | CustomerCycleReport.tsx | UPDATE | Thêm key mới, rà key CUSTOMER_CYCLE_CUSTOMER |
| UI-003 | SCR-01 | Filter bar purchase | CustomerCyclePurchaseFilterReport.tsx | KEEP | Reuse visible controls |
| UI-004 | SCR-01 | Overview purchase copy | CustomerCyclePurchaseViewChart.tsx | UPDATE | Copy visit-based |
| UI-005 | SCR-01 | Matrix purchase copy | CustomerCyclePurchaseTable.tsx | UPDATE | Header/row visit-based |
| UI-006 | SCR-02 | Popup toolbar có service/group/export | CustomerCycleDetailPopup.tsx | UPDATE | Chỉ search, bỏ export/filter nâng cao |
| UI-007 | SCR-02 | Popup columns purchase | CustomerCycleDetailPopup.tsx | UPDATE | Columns chăm sóc visit |
| UI-008 | SCR-02 | Click row mở hồ sơ khách | CustomerCycleDetailPopup.tsx | KEEP | Giữ behavior |
| UI-009 | SCR-01 | (none — new component) | - | NEW | Info strip permanent above filter bar (DEC-028) |
B0.2) Field delta
| Field / Copy | Surface | Status | Target behavior |
|---|---|---|---|
Chu kỳ khách ghé không mua | Tab label (≥768px) | NEW | Tab thứ 4 trong report group, viewport desktop/tablet lớn |
Khách ghé không mua | Tab label (<768px) | NEW (DEC-022) | Short form cho mobile, tab key + URL không đổi |
Số lượng khách có lượt ghé không mua | Card label | NEW | Thay Số lượng khách bắt đầu mua |
Cơ cấu khách: đã/chưa quay lại ghé | Donut title | NEW | Thay wording tần suất mua; nêu thẳng 2 nhóm |
Tỉ lệ KH theo thời gian chưa ghé lại | Donut title | NEW | Thay wording chưa mua lại |
KH chưa quay lại ghé | Matrix row | NEW | Thay row Số khách có khả năng rời bỏ |
Lượt ghé gần nhất | Popup column | NEW | Date từ latest qualified visit |
Loại ghé | Popup column | NEW | Map source: consultant→Tư vấn, do_service→Làm dịch vụ. Multi-source: Tư vấn + Làm dịch vụ (DEC-017) |
Số lượt ghé không mua trong kỳ | Popup column | NEW (rename T4) | Đếm qualified visits của khách trong kỳ filter |
Đơn mua gần nhất | Popup column | NEW (rename T1) | Order hợp lệ gần nhất tới to_date |
Chưa từng mua | Popup empty value | NEW (rename T2) | Hiển thị khi closest_order_date null |
Cùng ngày | Matrix row label | NEW (rename T3) | Thay row 0 ngày; khách có nhiều lượt qualified cùng 1 ngày |
Tab hiển thị khách đã ghé tư vấn / làm dịch vụ nhưng chưa phát sinh doanh số. Đã loại khách đã mua sau lượt ghé. | Info strip copy | NEW (DEC-028) | Permanent strip trên đầu tab, không dismiss |
Đã loại khách chuyển đổi sau ghé | Card 1 sub-text | NEW (DEC-029) | Sub-text dưới label card 1 |
Trên tổng {total} KH | Donut 1 sub-text | NEW (DEC-029) | Sub-text dưới donut 1, runtime interpolate |
Trên tổng {total} KH | Donut 2 sub-text | NEW (DEC-029) | Sub-text dưới donut 2 |
Khoảng cách trung bình giữa các lượt ghé | Matrix header dọc sub-text | NEW (DEC-029) | Sub-text dưới header Chu kỳ khách ghé |
Tính tới ngày kết thúc kỳ | Matrix header ngang sub-text | NEW (DEC-029) | Sub-text dưới header Số ngày/tháng chưa ghé lại |
Nhiều lượt ghé qualified trong 1 ngày | Matrix row Cùng ngày sub-text | NEW (DEC-029) | Sub-text dưới row label |
Chỉ có 1 lượt ghé qualified trong kỳ | Matrix row KH chưa quay lại ghé sub-text | NEW (DEC-029) | Sub-text dưới row label |
% trên tổng | Matrix cột cuối — column header | RENAME T6 (DEC-029) | Đổi tên cột từ Tỷ lệ → % trên tổng; logic data binding không đổi |
B0.4) Field x Surface matrix
| Trường | Tab/card | Matrix | Popup | Search | Filter | Xuất dữ liệu | Quyền | Mobile | Tooltip | Kiểm tra |
|---|---|---|---|---|---|---|---|---|---|---|
total_customer | Có | Tổng row | Không | Không | Theo filter | Không | Report scope | Fit | Có | 0 -> empty |
visit_cycle | Không | Có | Không | Không | Theo duration | Không | Report scope | Scroll | Có | 1 visit -> no cycle |
days_since_last_visit | Donut 2 | Column bucket | Có | Không | Theo to_date | Không | Report scope | Fit | Có | Clamp to now |
branch_name | Không | Không | Có | Không | Theo branch | Không | Branch scope | Fit | Không | Multi-branch -> Nhiều chi nhánh |
closest_order_date | Không | Không | Có | Không | Theo to_date | Không | Report scope | Fit | Có | No order -> Chưa từng mua (rename T2) |
B0.5) State x Screen matrix
| State | SCR-01 | SCR-02 |
|---|---|---|
| Default | Filter default + loading vùng data | Không mở |
| Đang tải | Spinner/skeleton giống tab purchase, copy Đang tải dữ liệu... | Table loading |
| Rỗng | Chưa có khách ghé không mua trong điều kiện lọc này. Vui lòng đổi thời gian hoặc chi nhánh. | Chưa có khách hàng trong nhóm này. |
| Lỗi hệ thống | Không thể tải báo cáo. Vui lòng thử lại. + nút Thử lại nếu có | Không thể tải danh sách khách. Vui lòng thử lại. |
| Không có quyền | Ẩn tab | Không mở |
| Một phần | Chart có data nhưng bucket rỗng vẫn render matrix | Popup rỗng với title đúng bucket |
| Info strip | Visible always (không phụ thuộc state khác — DEC-028) | Không hiển thị |
B0.6) Style guideline v2.8 (DEC-028, DEC-029)
Style spec dưới đây dành cho designer + FE cùng đọc — không phải đợi dev-spec.md C6. Giúp designer build mockup / FE build component không lệch nhau.
| Element | Style spec |
|---|---|
| Info strip container (DEC-028) | Background bg-amber-50 (Quasar palette); border-radius nhẹ; padding 12-16px; font-size body; không bold; full container width |
| Info strip icon | q-icon name="info", color amber-9, size 18-20px, đặt left |
| Info strip text | Color amber-9 (đậm hơn background); 1-2 dòng max; mobile <768px auto-wrap |
| Sub-text dưới label (DEC-029) | Font-size 12px (text-caption Quasar); color text-grey-7 hoặc text-secondary; margin-top 4px; line-height tight; không italic (italic gây rối với dấu tiếng Việt) |
| Sub-text wrap | Mobile <768px: auto-wrap 2 dòng max, không cắt chữ; donut sub-text có thể trim còn Trên tổng nếu Chart.js label clutter |
| Popup column tooltip icon (DEC-029, DEC-030) | q-icon name="info"; size 14px; color grey-6; class q-ml-xs cursor-help; inline cạnh column header text |
| Tooltip popover | Quasar q-tooltip default style; mobile tap toggle; max-width responsive, không cắt chữ |
| KHÔNG có | Animation, dismiss button, localStorage cho info strip; italic cho sub-text; chip/badge cho tooltip icon |
Reference component cho FE:
VisitNoPurchaseInfoStrip.tsx(info strip, static)LabelWithSubText.tsx(sub-text wrapper, props:label,subtext,tooltip,level: 'card' | 'header' | 'row')
Chi tiết implementation FE: xem dev-spec.md C6.
B1) Bản đồ màn hình
| SCR | Tên | Route | Mục tiêu |
|---|---|---|---|
| SCR-01 | Tab Chu kỳ khách ghé không mua | /r/reports/customer_cycle_report_group | Xem phân bố khách đã ghé nhưng chưa mua |
| SCR-02 | Popup chi tiết bucket | Dialog từ SCR-01 | Xem danh sách khách để chăm sóc tiếp |
B2) SCR-01 — Tab Chu kỳ khách ghé không mua
Mục tiêu màn hình
User vào màn để xác định nhóm khách đã có tín hiệu quay lại chi nhánh nhưng chưa tạo doanh số. Primary action là click bucket trong matrix để mở danh sách khách; secondary action là đổi filter hoặc đặt lại.
Demo gắn vào UI hiện tại
text
Route: /r/reports/customer_cycle_report_group
[Tổng quan] [Chu kỳ mua hàng] [Chu kỳ khách hàng] [Chu kỳ khách ghé không mua]
--------------------------------------------------------------------------------
ℹ️ Tab hiển thị khách đã ghé tư vấn / làm dịch vụ nhưng chưa phát sinh
doanh số. Đã loại khách đã mua sau lượt ghé. (DEC-028)
--------------------------------------------------------------------------------
[Chu kỳ theo ngày v] [30 ngày i] [01/02/2026 - 30/04/2026] [Chi nhánh v] [Đặt lại]
Tổng quan
+-----------------------------+ +-----------------------------+ +-----------------------------+
| Số lượng khách có lượt | | Cơ cấu khách: đã/chưa | | Tỉ lệ KH theo thời gian |
| ghé không mua ℹ️ | | quay lại ghé ℹ️ | | chưa ghé lại ℹ️ |
| Đã loại khách chuyển đổi | | Trên tổng 1.250 KH | | Trên tổng 1.250 KH |
| sau ghé (DEC-029) | | (DEC-029) | | (DEC-029) |
| | | | | |
| 1.250 | | KH đã quay / Chưa quay | | 1-30 / 31-60 / 61-90 |
+-----------------------------+ +-----------------------------+ +-----------------------------+
Chu kỳ khách ghé
+--------------------------------+---------+---------+---------+-------+----------+
| Chu kỳ khách ghé ℹ️ | 1-30 ℹ️ | 31-60 | 61-90 | Tổng | % trên |
| Khoảng cách trung bình | Số ngày chưa ghé lại | tổng |
| giữa các lượt ghé (DEC-029) | Tính tới ngày kết thúc kỳ (DEC-029) | (T6) |
+--------------------------------+---------+---------+---------+-------+----------+
| Tổng ℹ️ | 450 | 620 | 180 | 1250 | 100% |
| Tỷ lệ KH theo thời gian | 36.00% | 49.60% | 14.40% | 100% | |
| chưa ghé lại ℹ️ | | | | | |
| KH chưa quay lại ghé ℹ️ | 300 | 380 | 120 | 800 | 64.00% |
| Chỉ có 1 lượt ghé qualified | | | | | |
| trong kỳ (DEC-029) | | | | | |
| Cùng ngày ℹ️ | 5 | 3 | 0 | 8 | 0.64% |
| Nhiều lượt ghé qualified | | | | | |
| trong 1 ngày (DEC-029) | | | | | |
| 1 - 30 ngày | 145 | 237 | 60 | 442 | 35.36% |
+--------------------------------+---------+---------+---------+-------+----------+Filter behavior
| Control | Default | Behavior |
|---|---|---|
| Loại chu kỳ | Chu kỳ theo ngày | Đổi sang tháng thì period reset 3 |
| Bước chu kỳ | 30 ngày hoặc 3 tháng | Debounce giống filter hiện có. Validation (DEC-024): by_day ∈ [1, 180], by_month ∈ [1, 12]. Out-of-range → inline error Bước chu kỳ phải từ 1 đến {max} {đơn vị}, không gọi BE, giữ giá trị cũ trong query state. |
| Thời gian báo cáo | Đầu tháng cách đây 2 tháng -> cuối tháng hiện tại | Query lại khi đổi. Max range (DEC-023): 365 ngày. Nếu user chọn to_date - from_date > 365, FE clamp from_date = to_date - 365 ngày + toast warning Phạm vi báo cáo tối đa 1 năm. Đã điều chỉnh ngày bắt đầu thành DD/MM/YYYY. |
| Chi nhánh | All branch user có quyền | Multi-select |
| Đặt lại | - | Reset toàn bộ filter về default |
Hidden rules:
- Source visit =
consultant,do_service. is_zero_order = true.- Loại khách đã chuyển đổi: nếu khách có order hợp lệ với
created_at > last_qualified_visit_date(trong kỳ, đếnto_date), không hiển thị (DEC-014).
Interaction
| Action | Feedback | Copy |
|---|---|---|
| Đổi filter | Data area loading | Đang tải dữ liệu... |
| Đặt lại | Reset filter + query lại | Không cần toast |
| Click bucket có data (value > 0) | Mở SCR-02 | Title theo row + column; cursor pointer |
Click bucket rỗng (value 0/empty) | KHÔNG mở popup (DEC-019) | Cursor default, không có hover effect |
| Query lỗi | Inline error | Không thể tải báo cáo. Vui lòng thử lại. |
B3) SCR-02 — Popup chi tiết khách ghé không mua
Mục tiêu màn hình
Popup giúp CSKH xem khách cụ thể trong bucket và mở hồ sơ khách để gọi/chăm sóc lại. Popup không phải màn export và không hiển thị field tài chính phase 1.
Title format (DEC-021)
Format cố định: {Row label} • {Column label} (separator • — bullet với 2 space mỗi bên). Ví dụ:
KH chưa quay lại ghé • 31 - 60 ngày1 - 30 ngày • 4 - 6 tháng(chu kỳ row × bucket column)Cùng ngày • ≤ 3 thángTổng • 31 - 60 ngày(popup từ row Tổng)
KHÔNG dùng pattern (Chu kỳ: ...) của tab purchase.
text
Popup: KH chưa quay lại ghé • 31 - 60 ngày
--------------------------------------------------------------------------------
[Tìm tên hoặc số điện thoại_______________________________________________]
+-----+----------------+------------+--------------------+----------------------+---------------------+------------------------------------+----------------------+--------------------------+
| STT | Khách hàng | SĐT | Chi nhánh ℹ️ | Lượt ghé gần nhất ℹ️ | Loại ghé ℹ️ | Số lượt ghé không mua trong kỳ ℹ️ | Đơn mua gần nhất ℹ️ | Số ngày chưa ghé lại ℹ️ |
+-----+----------------+------------+--------------------+----------------------+---------------------+------------------------------------+----------------------+--------------------------+
| 1 | Nguyễn Thị Lan | 0909xxxxxx | CN Quận 1 | 10/04/2026 | Tư vấn | 1 | 15/03/2026 | 20 |
| 2 | Trần Minh Anh | 0912xxxxxx | Nhiều chi nhánh | 08/04/2026 | Tư vấn + Làm dịch vụ | 2 | Chưa từng mua | 22 |
+-----+----------------+------------+-----------------+-------------------+-------------------+--------------------------------+--------------------+----------------------+Popup columns
| Cột | Data | Behavior |
|---|---|---|
| STT | index | Theo page |
| Khách hàng | display_name + avatar nếu có | Click mở hồ sơ khách |
| SĐT | phone_number | Mask/format theo component phone hiện có |
| Chi nhánh | branch_name | Nhiều chi nhánh nếu same-day latest multi-branch |
| Lượt ghé gần nhất | last_visit_date | DD/MM/YYYY |
| Loại ghé | visit_source | Map consultant -> Tư vấn, do_service -> Làm dịch vụ. Visit có cả 2 source: hiển thị Tư vấn + Làm dịch vụ (thứ tự cố định, separator +, không xuống dòng, không chip — DEC-017) |
| Số lượt ghé không mua trong kỳ | count_visit_no_order | Đếm qualified visits của khách trong kỳ filter |
| Đơn mua gần nhất | closest_order_date | Chưa từng mua nếu null (không giới hạn trong kỳ filter) |
| Số ngày chưa ghé lại | days_since_last_visit | Integer ngày |
V2.8 update (DEC-029, DEC-030): Mỗi column header trong bảng popup trên (Chi nhánh, Lượt ghé gần nhất, Loại ghé, Số lượt ghé không mua trong kỳ, Đơn mua gần nhất, Số ngày chưa ghé lại) phải render icon ℹ️ inline cạnh tên column. Hover/tap ℹ️ hiển thị tooltip copy lift từ B9 required.
Responsive/accessibility
| Area | Rule |
|---|---|
| Matrix desktop | Sticky header, horizontal scroll nếu nhiều bucket |
| Matrix tablet/mobile | Giữ scroll ngang; không co chữ đến mức mất nghĩa |
| Popup desktop | Width tối đa như dialog hiện có, bảng scroll ngang |
| Popup mobile/tablet | Full-width dialog, focus vào search khi mở |
| Keyboard | Esc đóng popup, Tab đi từ search -> table -> pagination |
| Screen reader | Icon info có aria-label theo tooltip |
B4) Notification và action feedback
Không có thông báo hệ thống. Feedback nằm tại màn:
| Situation | Copy |
|---|---|
| Loading | Đang tải dữ liệu... |
| Empty report | Chưa có khách ghé không mua (chưa chuyển đổi) trong điều kiện lọc này. Vui lòng đổi thời gian hoặc chi nhánh. |
| Empty popup | Chưa có khách hàng trong nhóm này. |
| Đơn mua gần nhất = null | Chưa từng mua (rename T2: rõ all-time, không nhầm "trong kỳ") |
| Lỗi báo cáo | Không thể tải báo cáo. Vui lòng thử lại. |
| Lỗi popup | Không thể tải danh sách khách. Vui lòng thử lại. |
| Date range vượt 365 ngày (DEC-023) | Toast: Phạm vi báo cáo tối đa 1 năm. Đã điều chỉnh ngày bắt đầu thành DD/MM/YYYY. (info, auto-dismiss 5s) |
| Bước chu kỳ ngoài range (DEC-024) | Inline error dưới input: Bước chu kỳ phải từ 1 đến 180 ngày / Bước chu kỳ phải từ 1 đến 12 tháng |
B5) Xuất dữ liệu
Không áp dụng phase 1. Không hiển thị nút Tải xuống trong popup tab mới. Nếu phase sau bật export, phải có quyết định mới, action permission và QA export cases.
B6) Từ điển nội dung hiển thị
| Key gợi ý | Text VI | Ghi chú |
|---|---|---|
customer_cycle.visit_no_purchase_tab | Chu kỳ khách ghé không mua | Tab label |
customer_cycle.visit_no_purchase_total | Số lượng khách có lượt ghé không mua | Card 1 |
customer_cycle.visit_frequency | Cơ cấu khách: đã/chưa quay lại ghé | Donut |
customer_cycle.return_visit_customer | KH đã quay lại ghé | Segment |
customer_cycle.non_return_visit_customer | KH chưa quay lại ghé | Segment/row |
customer_cycle.days_since_visit | Tỉ lệ KH theo thời gian chưa ghé lại | Donut |
customer_cycle.visit_cycle_matrix | Chu kỳ khách ghé | Matrix title |
customer_cycle.empty_report | Chưa có khách ghé không mua (chưa chuyển đổi) trong điều kiện lọc này. Vui lòng đổi thời gian hoặc chi nhánh. | Empty |
customer_cycle.empty_popup | Chưa có khách hàng trong nhóm này. | Empty popup |
customer_cycle.visit_source.consultant | Tư vấn | Map source |
customer_cycle.visit_source.do_service | Làm dịch vụ | Map source |
customer_cycle.visit_source.separator | + | Separator multi-source (DEC-017) |
customer_cycle.matrix.same_day_row | Cùng ngày | Matrix row label (rename T3 từ 0 ngày) |
customer_cycle.popup.col_count_visit | Số lượt ghé không mua trong kỳ | Popup column (rename T4) |
customer_cycle.popup.col_closest_order | Đơn mua gần nhất | Popup column (rename T1) |
customer_cycle.popup.no_order | Chưa từng mua | Popup empty value (rename T2) |
customer_cycle.tab.full | Chu kỳ khách ghé không mua | Tab label viewport ≥768px (DEC-022) |
customer_cycle.tab.short | Khách ghé không mua | Tab label viewport <768px (DEC-022) |
customer_cycle.popup.title_separator | • | Separator title popup (DEC-021) |
customer_cycle.filter.date_range_max_warning | Phạm vi báo cáo tối đa 1 năm. Đã điều chỉnh ngày bắt đầu thành {date}. | Toast warning (DEC-023) |
customer_cycle.filter.duration_invalid_day | Bước chu kỳ phải từ 1 đến 180 ngày | Inline error by_day (DEC-024) |
customer_cycle.filter.duration_invalid_month | Bước chu kỳ phải từ 1 đến 12 tháng | Inline error by_month (DEC-024) |
customer_cycle.info_strip | Tab hiển thị khách đã ghé tư vấn / làm dịch vụ nhưng chưa phát sinh doanh số. Đã loại khách đã mua sau lượt ghé. | Info strip permanent (DEC-028) |
customer_cycle.card1.subtext | Đã loại khách chuyển đổi sau ghé | Card 1 sub-text (DEC-029) |
customer_cycle.donut1.subtext | Trên tổng {total} KH | Donut 1 sub-text (DEC-029, runtime interpolate {total}) |
customer_cycle.donut2.subtext | Trên tổng {total} KH | Donut 2 sub-text (DEC-029) |
customer_cycle.matrix.header_doc.subtext | Khoảng cách trung bình giữa các lượt ghé | Matrix header dọc sub-text (DEC-029) |
customer_cycle.matrix.header_ngang.subtext | Tính tới ngày kết thúc kỳ | Matrix header ngang sub-text (DEC-029) |
customer_cycle.matrix.row_cung_ngay.subtext | Nhiều lượt ghé qualified trong 1 ngày | Row Cùng ngày sub-text (DEC-029) |
customer_cycle.matrix.row_chua_quay_lai.subtext | Chỉ có 1 lượt ghé qualified trong kỳ | Row KH chưa quay lại ghé sub-text (DEC-029) |
customer_cycle.matrix.col_percent | % trên tổng | Matrix column header rename T6 (DEC-029, cũ Tỷ lệ) |
B7) Đo lường sử dụng
Phase 1 không thêm event tracking. Report module hiện chưa có pattern telemetry/event logging thống nhất, nên các event dưới đây chỉ là future reference, không phải implementation contract và không có QA phase 1.
| Event candidate | Trigger | Payload gợi ý |
|---|---|---|
report_visit_no_purchase_viewed | User mở tab | branch_ids, type_cycle, duration |
report_visit_no_purchase_bucket_opened | User click bucket | row_label, column_label, customer_count |
Nếu sau này cần đo usage, mở decision riêng để chốt naming convention, payload chuẩn, nơi gửi event, và QA analytics cases.
B8) Quy tắc responsive và accessibility
| Check | Điều kiện đạt |
|---|---|
| Text không overlap | Tab label dài vẫn có outside arrows/scroll như XTabs hiện tại |
| Matrix nhiều bucket | Horizontal scroll hoạt động, sticky header không che row |
| Popup focus | Focus trap trong dialog, search là focus đầu tiên |
| Icon tooltip | Icon thông tin có tooltip tiếng Việt |
| Empty/error copy | Có hướng xử lý, không dùng copy trạng thái tiếng Anh |
B9) Tooltip dictionary
Mục tiêu: user PO/BA/Marketing/CSKH hiểu cách tính và nghiệp vụ ngay khi hover, không cần mở docs.
Format mỗi tooltip ưu tiên: định nghĩa ngắn, công thức rút gọn nếu có, ví dụ ngắn nếu giúp, edge case nếu liên quan DEC.
V2.8 update (DEC-030): Tất cả 19 tooltip dưới đây là required cho Phase 1 GA — gộp 7 v2.7 required + 11 lift từ backlog + 1 new (popup Chi nhánh). Không còn phân tách "core" vs "backlog".
Required tooltip (19 items)
| Field/Icon | Tooltip Text | Điều kiện hiện |
|---|---|---|
Chi nhánh | Lọc theo chi nhánh user có quyền. Để trống = tất cả chi nhánh được phép. Hệ thống tự loại chi nhánh ngoài quyền của user. | Hover icon info trong filter |
Row KH chưa quay lại ghé | Khách chỉ có 1 lượt ghé qualified trong kỳ → chưa đủ dữ liệu tính chu kỳ. Khác với Cùng ngày. | Hover icon info ở row label |
Row Cùng ngày | Khách có nhiều lượt ghé qualified trong cùng 1 ngày; chu kỳ tính ra = 0 ngày. Không có nghĩa là 0 ngày chưa ghé lại. | Hover icon info ở row label |
Loại ghé | Loại tương tác của khách trong lượt ghé qualified gần nhất. Có thể là Tư vấn, Làm dịch vụ, hoặc Tư vấn + Làm dịch vụ. | Hover icon info trong popup |
Số lượt ghé không mua trong kỳ | Đếm số lượt ghé qualified của khách trong kỳ filter; không tính source khác như order, payment. | Hover icon info trong popup |
Đơn mua gần nhất | Đơn hàng hợp lệ gần nhất của khách tính tới ngày kết thúc kỳ. Có thể là đơn cũ trước lượt ghé. Hiển thị Chưa từng mua nếu khách chưa từng có đơn hợp lệ. | Hover icon info trong popup |
Số ngày chưa ghé lại | Ngày kết thúc kỳ trừ lượt ghé qualified gần nhất. VD kỳ kết thúc 30/04, ghé gần nhất 10/04 → 20 ngày. | Hover icon info trong popup |
Loại chu kỳ (filter) | Cách chia bucket thời gian trong matrix. "Chu kỳ theo tháng" = duration × 30 ngày, không phải tháng dương lịch. VD chọn tháng + bước 3 → bucket 90 ngày. | Hover icon info |
Bước chu kỳ (filter) | Số đơn vị thời gian (ngày hoặc tháng) dùng để chia bucket cột "Số ngày chưa ghé lại". VD bước 30 → bucket 1-30, 31-60, 61-90 ngày...Range cho phép: 1-180 ngày hoặc 1-12 tháng. | Hover icon info |
Thời gian báo cáo (filter) | Khoảng thời gian xét lượt ghé qualified. Khách phải có ≥1 lượt ghé consultant hoặc do_service (chưa phát sinh doanh số) trong kỳ này mới được đưa vào báo cáo.Tối đa 1 năm để bảo đảm matrix dễ đọc và performance ổn định. | Hover icon info |
Số lượng khách có lượt ghé không mua (overview) | Số khách duy nhất có lượt Tư vấn hoặc Làm dịch vụ chưa phát sinh doanh số, và chưa có đơn mua hợp lệ sau lượt ghé gần nhất trong kỳ lọc. Công thức: qualified − convertedVD: Lan ghé 10/04, mua 12/04 → đã chuyển đổi → loại. Anh ghé 10/04 + 20/04, mua 15/04 (giữa 2 lượt) → giữ. | Hover icon info |
Cơ cấu khách: đã/chưa quay lại ghé (overview) | Tỉ lệ khách đã hình thành chu kỳ ghé so với khách chỉ ghé 1 lần. • Đã quay lại: ≥2 lượt ghé qualified trong kỳ. • Chưa quay lại: đúng 1 lượt. Tổng = 100%. | Hover icon info |
Tỉ lệ KH theo thời gian chưa ghé lại (overview) | Phân bố khách theo bucket "Số ngày chưa ghé lại" = ngày kết thúc kỳ − lượt ghé gần nhất. VD: 36% khách thuộc bucket 1-30 ngày nghĩa là 36% khách có lượt ghé gần nhất cách ngày kết thúc 1-30 ngày. | Hover icon info |
Header trục dọc Chu kỳ khách ghé (matrix) | Số ngày trung bình giữa các lượt ghé qualified liên tiếp của cùng khách trong kỳ. VD: ghé 1/3, 21/3, 10/4 → gaps 20 + 20 → chu kỳ = 20 ngày → khách thuộc bucket 1-30 ngày. | Hover icon info |
Header trục ngang Số ngày/tháng chưa ghé lại (matrix) | Tính từ lượt ghé qualified gần nhất đến ngày kết thúc kỳ. "Chu kỳ theo tháng" → bucket tính theo duration × 30 ngày. Bucket đầu = ≤ N tháng (0 < days ≤ N×30); kế tiếp N+1 - 2N tháng; cuối > M tháng. | Hover icon info |
Row Tổng (matrix) | Tổng khách trong từng bucket cột. Đã loại khách đã chuyển đổi sau ghé (DEC-014). | Hover icon info |
Row Tỷ lệ KH theo thời gian chưa ghé lại (matrix) | % khách trong bucket cột so với tổng khách. Cùng dữ liệu với donut "Tỉ lệ KH theo thời gian chưa ghé lại". | Hover icon info |
Lượt ghé gần nhất (popup) | Ngày của lượt ghé qualified gần nhất trong kỳ filter (Tư vấn hoặc Làm dịch vụ, chưa phát sinh doanh số). | Hover icon info trong popup |
Chi nhánh (popup) | Chi nhánh của lượt ghé qualified gần nhất. Nếu khách có nhiều lượt ghé cùng ngày ở các chi nhánh khác nhau, hiển thị Nhiều chi nhánh. | Hover icon info trong popup (NEW v2.8) |
B-POST) Verification
| Check | Status |
|---|---|
| Mọi UI hiện hữu có Delta Status | Đạt |
| Target wireframe hiển thị vùng KEEP quanh delta | Đạt |
| Copy user-facing tiếng Việt tự nhiên | Đạt |
| Không dùng copy CTA/trạng thái tiếng Anh | Đạt |
| Export phase 1 bị ẩn | Đạt |
| Permission/no-permission state có mô tả | Đạt |
| Responsive/accessibility có contract | Đạt |
UI/PRD/Dev cùng thuật ngữ KH chưa quay lại ghé | Đạt |
B-PITFALLS) Anti-pattern review
| Pitfall | Đã phòng tránh ở |
|---|---|
| Chỉ đổi label nhưng giữ purchase data | PRD FR-002, Dev C3/C5 |
| Bỏ sót persistence tab | PRD FR-001, Dev C6 |
| Popup vẫn có export | UI B3/B5, QA TC-005 |
Dùng rời bỏ như churn | UI B6/B9, PRD A9 |
Gộp Cùng ngày với KH chưa quay lại ghé | PRD A10, UI B9, QA TC-004 |
| Liệt khách đã chuyển đổi vào popup CSKH | PRD FR-002 AC + DEC-014, Dev C3 SQL exclude, QA TC-010 |
Tab Chu kỳ khách hàng rớt persistence sau refresh do bug to | PRD FR-001 AC + DEC-015, Dev C1/C6, QA TC-001 |
| Manager bypass branch scope qua DevTools | PRD FR-002/FR-006 AC + DEC-016, Dev C8, QA TC-012 |
Loại ghé hiển thị thứ tự ngẫu nhiên giữa các row | UI B3/B6 + DEC-017, Dev C5/C6 normalize, QA TC-013 |
User nhầm 0 ngày là "0 ngày chưa ghé lại" thay vì chu kỳ 0 ngày | Rename T3 thành Cùng ngày + tooltip B9 mô tả rõ |
User nhầm Chưa có đơn hàng là "chưa mua trong kỳ" | Rename T2 thành Chưa từng mua + tooltip popup Đơn mua gần nhất rõ scope all-time |
User nhầm Số lần ghé không mua đếm cả ngoài kỳ hoặc source khác | Rename T4 thành Số lượt ghé không mua trong kỳ + tooltip rõ scope |
User nhầm bucket by_month 3 tháng là điểm mốc thay vì range | DEC-018 đổi label ≤ 3 tháng cho tab mới |
| Click cell value=0 mở popup empty vô nghĩa | DEC-019 disable click; cursor default; QA TC-015 |
| Action timeout @ 500k rows | DEC-020 benchmark gate + SQL fallback plan; QA TC-016 |
| Tab label overflow trên mobile gây cắt chữ | DEC-022 responsive label; QA TC-017 |
| User chọn date range nhiều năm → matrix không đọc nổi | DEC-023 max 365 ngày + clamp + toast |
| User nhập bước chu kỳ extreme (9999, -1) | DEC-024 FE/BE validate; QA TC-018 |
| Popup title inconsistent giữa tab cũ/mới | DEC-021 lock format • cho tab mới; tab cũ giữ |
| User mở tab không hiểu context, không biết "khách ghé không mua" là gì | Info strip permanent (DEC-028); QA TC-020 |
| User hover mới biết denominator donut → mờ hồ | Inline sub-text Trên tổng {total} KH dưới donut (DEC-029); QA TC-021 |
Cột bảng cuối Tỷ lệ không nói mẫu số | Rename T6 → % trên tổng (DEC-029); QA TC-022 |
| User mới không biết hover ℹ️ để xem tooltip → bỏ lỡ context | Sub-text inline + tooltip ℹ️ kết hợp 3-layer (DEC-027/029/030) |