Giao diện
US-I02: Tạo contact test (trùng email → cập nhật contact cũ) ​
Thuộc FS: 00-overview.md — SAPP-24395 Contact / Deal instances & Bulk product creation Phần chung (tổng quan, roles, flow tổng, related features, NFR, Open Questions, References) xem ở Overview. File này chỉ chứa nội dung riêng của US. As-built tại commit
e77c47a— suy từ code, chưa chạy thử.
| Field | Value |
|---|---|
| US ID | US-I02 |
| Role thực hiện | R1 - Người dùng tool (Assumption — OQ-I-16 về G-01) |
| Phase | As-built (đã triển khai) |
| Priority | N/A — as-built |
| Status | Draft |
| NFR liên quan | NFR-I-01, NFR-I-04, NFR-I-05, NFR-I-06, NFR-I-07 — xem Overview §5 |
| OQ liên quan | OQ-I-02, OQ-I-05, OQ-I-16, OQ-I-18 — xem Overview §6 |
Nguồn inventory: A-03, A-04. Ghi: HubSpot dev · HubSpot PROD (qua tab "Staging").
1. US Statement ​
text
As a Người dùng tool (Assumption — OQ-I-16)
I want nhập thông tin một contact (Họ tên bắt buộc, các trường khác tuỳ chọn) và tạo nó trên HubSpot của môi trường đang chọn
So that [Assumption] tôi có contact để gắn vào deal test theo case (US-I03)2. Dependencies & Definition Of Ready ​
N/A — đã triển khai (Mode B). Dependency runtime:
| # | Cần sẵn sàng | Ai cung cấp | Trạng thái |
|---|---|---|---|
| D1 | Token HubSpot SAPP_DEV_HUBSPOT_ACCESS_TOKEN / SAPP_PROD_HUBSPOT_ACCESS_TOKEN trong env_systems (US-P04) | Admin CMS | N/A — runtime |
| D2 | Route đọc định nghĩa property HubSpot (dựng widget) — US-H01 | BE | N/A — runtime |
3. Business Rules ​
| ID | Rule | Áp dụng ở | Đã cover bởi (AC) | Nguồn code (Mode B/C) |
|---|---|---|---|---|
| BR-01 | Họ tên là bắt buộc: trống (sau khi bỏ khoảng trắng) thì chặn ngay trên màn với "Thiếu thông tin" / "Họ tên là bắt buộc."; API cũng từ chối nếu thiếu | M1 - field Họ tên; A1 | AC-M1.2 | fe/src/ui/components/works/sapp/sapp-24395-tool.tsx:277-281; be/libs/interface/src/lib/dto/sapp/sapp-instance.dto.ts:13-14 |
| BR-02 | Chỉ các trường có giá trị (sau khi bỏ khoảng trắng đầu cuối) mới được gửi sang HubSpot; trường bỏ trống không ghi gì | M1 - A1 | AC-M1.1 | fe/src/ui/components/works/sapp/sapp-24395-tool.tsx:283-288; be/apps/api/src/sapp/sapp.controller.ts:2094-2108 |
| BR-03 | Mỗi trường trên form ghi vào đúng một thuộc tính contact HubSpot: Họ tên → "[TVTS] Họ tên"; Email → email; Số điện thoại → SĐT marketing; Địa chỉ → địa chỉ; Tỉnh/Thành phố → khu vực sinh sống; Ngày sinh → D.O.B (tên nội bộ ở bảng Fields) | M1 - Fields | AC-M1.1 | be/apps/api/src/sapp/sapp.controller.ts:2094-2108, :1731-1732; fe/src/ui/components/works/sapp/sapp-24395-tool.tsx:64-93 |
| BR-04 | Kiểu ô nhập (text / chọn / ngày …) và danh sách lựa chọn của mỗi trường được dựng theo định nghĩa property trên HubSpot của môi trường đang chọn | M1 - Fields | AC-M1.4 | fe/src/ui/components/works/sapp/sapp-24395-tool.tsx:322-334; fe/src/ui/components/works/sapp/hubspot-property-field.tsx:246-400 |
| BR-05 | Môi trường ghi theo tab: Dev → portal HubSpot dev; Staging → portal HubSpot production (dùng đúng credential prod; code ghi chú là chủ đích vì không có portal staging riêng); Prod → portal production (chỉ qua API, tab bị tắt) | M1 - A1 | AC-M1.3 | be/libs/constants/src/lib/hubspot/hubspot.constants.ts:19-26; be/libs/modules/src/lib/hubspot/common.service.ts:47-58 |
| BR-06 | Tạo thành công: contact (id + tên) được thêm vào cuối danh sách của môi trường; tên hiển thị lấy từ Họ tên HubSpot trả về, không có thì lấy Họ tên đã nhập; form được xoá trắng; popup "Đã tạo contact instance" — "Contact "<tên>" (id: <id>) đã được tạo trong portal <env>." | M1 - A1 | AC-M1.1 | be/libs/modules/src/lib/sapp/common.service.ts:1547-1582; fe/src/ui/components/works/sapp/sapp-24395-tool.tsx:257-272 |
| BR-07 | HubSpot từ chối vì lý do khác email trùng → lỗi 12006 "Failed to create HubSpot object"; lỗi khác → 13007 "Failed to create SAPP contact instance"; màn hiện popup "Tạo contact instance thất bại" | M1 - A1 | AC-M1.5 | be/libs/modules/src/lib/hubspot/common.service.ts:639-651; be/libs/modules/src/lib/sapp/common.service.ts:1572-1581; fe/src/ui/components/works/sapp/sapp-24395-tool.tsx:260 |
Không phải BR (⚠️ Observed → OQ): email trùng → cập nhật đè contact có sẵn (inventory R-04 → OQ-I-05); contact dùng lại bị thêm lặp vào danh sách và popup vẫn nói "đã được tạo" (OQ-I-18); tab Staging ghi vào prod (OQ-I-02).
4. Design ​
a. Mối liên hệ giữa các màn & User Flow ​
text
[M1: mục "1. Tạo HubSpot contact instance" (trong màn US-I01)]
↓ nhập form, bấm "Tạo contact"
├─ Họ tên trống ─────────────→ popup "Thiếu thông tin", ở lại M1
├─ HubSpot tạo mới OK ────────→ popup "Đã tạo contact instance", contact vào danh sách
├─ HubSpot báo email trùng ───→ ⚠️ cập nhật contact cũ → popup như tạo mới (OQ-I-05, OQ-I-18)
└─ lỗi khác ──────────────────→ popup "Tạo contact instance thất bại"Link flow chi tiết: N/A.
b. Chi tiết từng màn ​
Màn M1 - Form "1. Tạo HubSpot contact instance" ​
| Field | Value |
|---|---|
| Figma frame | N/A |
| Trigger vào màn | Mở màn Contact / Deal instances (US-I01), chọn tab môi trường |
| Mô tả ngắn | Form 6 trường (lưới 2 cột), mô tả "Nhập thông tin contact rồi tạo trong portal <env>. Chỉ Họ tên là bắt buộc — các trường khác tùy chọn. Chỉ id & name được lưu lại (không lưu email/phone)." Nút "Tạo contact" + "Tải lại" |
UI States ​
| State | Khi nào | Hiển thị | Figma frame |
|---|---|---|---|
| Default | Vừa vào | 6 ô trống; "Họ tên *" có dấu sao | N/A |
| Loading | Đang tạo | Nút đổi "Đang tạo…"; các ô và nút bị khoá | N/A |
| Has data | Tạo xong | Popup thành công; form trắng; contact mới ở cuối danh sách | N/A |
| Empty | — | N/A — form luôn hiện | N/A |
| Error | Thiếu Họ tên / API lỗi | Popup lỗi (BR-01 / BR-07); dữ liệu đã nhập được giữ | N/A |
| Disabled | Đang tạo | Như Loading | N/A |
Fields ​
| Field | Required | Type | Default | Format | Validation | Error Message | Visibility | Depends On |
|---|---|---|---|---|---|---|---|---|
Họ tên * (HubSpot leadin_h_tnipa7107b_39faf8acbcd97440649d6d3d99a5b929) | Yes | Theo định nghĩa HubSpot (BR-04) | Rỗng | Placeholder "Nguyễn Văn A" | BR-01 | "Họ tên là bắt buộc." | Always | Tab môi trường |
Email (email) | No | Theo HubSpot | Rỗng | Placeholder "a@example.com" | Không validate định dạng ở tool; HubSpot tự kiểm | Lỗi HubSpot → BR-07 | Always | — |
Số điện thoại (mkt__phone_number) | No | Theo HubSpot | Rỗng | Placeholder "0356020000" | Không validate ở tool | — | Always | — |
Địa chỉ (dia_chi) | No | Theo HubSpot | Rỗng | Placeholder "123 Đường ABC" | — | — | Always | — |
Tỉnh / Thành phố (khuvucsinhsong) | No | Theo HubSpot (có thể là danh sách chọn) | Rỗng | Placeholder "Hà Nội" | — | — | Always | — |
Ngày sinh (d_o_b) | No | Theo HubSpot | Rỗng | — | — | — | Always | — |
CCCD / Hộ chiếu không nằm trên contact — được ghi lên deal (US-I03 BR-06).
Filter / Search / Sort (chỉ màn List/Report — xóa nếu không phải) ​
N/A — màn form, không phải List.
Actions ​
A1 - Tạo contact ​
| Item | Description |
|---|---|
| Trigger | Bấm "Tạo contact" |
| Behavior | Kiểm BR-01 → gửi các trường có giá trị (BR-02, BR-03) → HubSpot tạo contact trên portal theo tab (BR-05). Nếu HubSpot báo email đã tồn tại: ⚠️ Observed — lấy contact cũ và cập nhật đè bằng dữ liệu vừa nhập (OQ-I-05). Sau đó ghi mục {id, tên} vào danh sách Redis của môi trường (BR-06). Ghi vào: HubSpot (dev hoặc production) + Redis mind_keep. ⚠️ Ghi PROD (qua Staging): ở tab Staging, contact được tạo / cập nhật trên portal HubSpot production |
| Success Result | Popup "Đã tạo contact instance"; form trắng; contact thêm vào danh sách |
Error cases:
| Case | Expected Handling |
|---|---|
| Họ tên trống | Popup "Thiếu thông tin" — "Họ tên là bắt buộc."; không gọi API |
| HubSpot từ chối (không phải trùng email) | Popup "Tạo contact instance thất bại" — "Failed to create HubSpot object (12006)" |
| Lỗi khác (Redis, mạng…) | Popup "Tạo contact instance thất bại" — "Failed to create SAPP contact instance (13007)" hoặc "Đã có lỗi xảy ra." |
Acceptance Criteria của màn M1 ​
AC-M1.1 - Tạo contact mới ở Dev ​
gherkin
Given đang ở tab Dev
And nhập Họ tên "Nguyễn Văn A", Email "a+new@example.com", để trống các trường khác
When bấm "Tạo contact"
Then HubSpot dev có contact mới với "[TVTS] Họ tên" = "Nguyễn Văn A", email = "a+new@example.com", các thuộc tính khác không được ghi
And popup "Đã tạo contact instance" hiện "Contact "Nguyễn Văn A" (id: <id>) đã được tạo trong portal dev."
And form trở về trắng và contact nằm cuối danh sách "Contact đã tạo"Maps to:
- Business Rule: BR-02, BR-03, BR-06
- Field/Action: A1
AC-M1.2 - Thiếu Họ tên ​
gherkin
Given Họ tên chỉ gồm khoảng trắng
When bấm "Tạo contact"
Then hiện popup "Thiếu thông tin" — "Họ tên là bắt buộc."
And không có request nào gửi tới BEMaps to:
- Business Rule: BR-01
AC-M1.3 - ⚠️ Ghi PROD qua tab Staging ​
gherkin
Given đang ở tab Staging
When tạo contact "Test Staging"
Then contact được tạo trên portal HubSpot **production** (credential prod)
And contact chỉ xuất hiện trong danh sách của tab Staging, không có trong danh sách tab ProdMaps to:
- Business Rule: BR-05; US-I01 BR-03
- Field/Action: A1 — OQ-I-02
AC-M1.4 - Widget theo định nghĩa HubSpot ​
gherkin
Given property "khuvucsinhsong" trên HubSpot của môi trường là kiểu danh sách chọn
When mở form
Then ô "Tỉnh / Thành phố" hiện dạng chọn với các lựa chọn của HubSpotMaps to:
- Business Rule: BR-04
AC-M1.5 - HubSpot từ chối ​
gherkin
Given HubSpot trả lỗi không phải trùng email (ví dụ giá trị không hợp lệ)
When bấm "Tạo contact"
Then popup "Tạo contact instance thất bại" — "Failed to create HubSpot object (12006)"
And form giữ nguyên dữ liệu đã nhậpMaps to:
- Business Rule: BR-07
AC-M1.6 - ⚠️ Observed: email trùng cập nhật đè contact cũ ​
gherkin
Given trên portal đã có contact X với email "hv@example.com" (có thể là học viên thật nếu đang ở tab Staging = production)
When tạo contact Họ tên "Test", email "hv@example.com"
Then (hành vi hiện tại) không báo lỗi; contact X bị cập nhật "[TVTS] Họ tên" = "Test" và các trường khác vừa nhập
And API trả contact X kèm cờ dùng lại = true
And popup vẫn hiện "đã được tạo" (không phân biệt tạo mới / dùng lại)
And contact X được thêm thêm một dòng vào danh sách tool (có thể lặp — OQ-I-18)Maps to:
- Business Rule: N/A — ⚠️ Observed (inventory R-04) → OQ-I-05, OQ-I-18
Negative AC / Edge Cases của màn M1 ​
| ID | Tình huống | Hành vi mong đợi |
|---|---|---|
| EC-M1.1 | Mất mạng giữa lúc tạo | Popup "Tạo contact instance thất bại" với thông báo lỗi chung; form giữ dữ liệu |
| EC-M1.2 | Bấm "Tạo contact" hai lần liên tiếp | Nút bị khoá trong lúc đang tạo, lần hai không gửi |
| EC-M1.3 | Họ tên có ký tự đặc biệt / emoji | Gửi nguyên văn sang HubSpot; HubSpot quyết định chấp nhận hay lỗi (BR-07) |
| EC-M1.4 | Đổi tab khi đang nhập dở | Form bị xoá (US-I01 BR-02) |
| EC-M1.5 | Gọi API prod trực tiếp (tab Prod tắt) | ⚠️ Observed — vẫn tạo / cập nhật contact trên production, không cần đăng nhập (NFR-I-01 → OQ-I-02) |
| EC-M1.6 | Email trùng một contact học viên thật trên production (tab Staging) | ⚠️ Observed — contact thật bị ghi đè (AC-M1.6, OQ-I-05) |
5. Cross-feature AC ​
AC-X.1 - Dữ liệu contact production bị ảnh hưởng ​
gherkin
Given tab Staging
When tạo contact với email của một học viên đang có trên HubSpot production
Then contact của học viên đó bị cập nhật các trường vừa nhập (hành vi hiện tại — ⚠️ Observed)Maps to:
- REL-05 (Be Affected - Dữ liệu HubSpot portal production)
AC-X.2 - Logic tạo contact dùng chung với SAPP-26878 ​
gherkin
Given tool SAPP-26878 chạy bước tạo contact
When logic "tạo hoặc dùng lại contact" của 24395 thay đổi
Then hành vi bước đó ở 26878 thay đổi theoMaps to:
- REL-04 (Be Affected - SAPP-26878 US-D03)