Giao diện
US-H06: Nhân bản pipeline trong cùng portal ​
Thuộc FS: 00-overview.md — SAPP HubSpot tools (sapp.001–004) 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. Nguồn: inventory A-08 (dev), A-09 (prod) và A-11 cho popup lỗi. Danh sách pipeline (M1) mô tả đầy đủ ở US-H04.⚠️ US này có đường ghi PROD (Duplicate khi Environment = Prod). Việc đó là cố ý hay bug chưa được xác nhận — OQ-H-03. Tài liệu mô tả đúng hiện trạng, không khẳng định là thiết kế.
| Field | Value |
|---|---|
| US ID | US-H06 |
| Role thực hiện | R1 - Người dùng tool (Assumption — OQ-H-01 về G-01) |
| Phase | As-built (đã triển khai) |
| Priority | N/A — as-built |
| Status | Draft |
| NFR liên quan | NFR-H-01, NFR-H-02, NFR-H-04, NFR-H-05, NFR-H-07 — xem Overview §5 |
| OQ liên quan | OQ-H-01, OQ-H-02, OQ-H-03, OQ-H-04, OQ-H-10, OQ-H-13, OQ-H-14, OQ-H-15 — xem Overview §6 |
1. US Statement ​
text
As a Người dùng tool [Assumption — OQ-H-01 về G-01]
I want nhân bản một pipeline Deal / Ticket (kèm toàn bộ stage) thành pipeline mới với label mới, trong chính portal đang chọn
So that [Assumption — OQ-H-04] có sẵn một pipeline cùng cấu trúc để thử nghiệm mà không sửa pipeline gốc2. 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 | Tool sapp.004 đang bật / hiển thị / đã mở khoá — US-P02 | Admin CMS | Runtime |
| D2 | env_systems: token của portal đang chọn (Dev → SAPP_DEV_HUBSPOT_ACCESS_TOKEN; Prod → SAPP_PROD_HUBSPOT_ACCESS_TOKEN) — US-P04 | Admin CMS | Runtime |
| D3 | Danh sách pipeline đã tải ở US-H04 | — | Runtime |
3. Business Rules ​
| ID | Rule | Áp dụng ở | Đã cover bởi (AC) | Nguồn code (Mode B/C) |
|---|---|---|---|---|
| BR-01 | Nhân bản luôn trong cùng portal: nguồn và đích đều là portal đang chọn ở Environment. Với Environment = Dev: nút "Duplicate" có trên mỗi thẻ pipeline và pipeline mới được tạo ở dev | M1 - action A1; M2 - action A2 | AC-M1.1, AC-M2.1 | fe/src/ui/components/works/sapp/pipeline-tool.tsx:337-353,209-214; be/apps/api/src/sapp/sapp.controller.ts:993-1004,1339-1350 |
| BR-02 | Modal "Duplicate pipeline", phụ đề "Source: <label nguồn> · into <env> portal"; ô "New label" điền sẵn <label nguồn> (copy) | M2 - field New label | AC-M2.1 | fe/src/ui/components/works/sapp/pipeline-tool.tsx:339-346,426-433,471-485 |
| BR-03 | Trên màn, label bắt buộc (sau khi cắt khoảng trắng); trống → popup "Missing label", không gọi API | M2 - action A2 | AC-M2.2 | fe/src/ui/components/works/sapp/pipeline-tool.tsx:201-208 |
| BR-04 | Không ghi đè: pipeline đích so khớp theo label (chính xác) trong cùng portal; đã có → trả nguyên pipeline đó, không tạo (NFR-H-07) | M2 - action A2 | AC-M2.3 | be/libs/modules/src/lib/hubspot/common.service.ts:1068,1081-1084 |
| BR-05 | Pipeline mới giữ thứ tự hiển thị và toàn bộ stage của nguồn (label, thứ tự, metadata); id pipeline / stage do HubSpot cấp mới | M2 - action A2 | AC-M2.1 | be/libs/modules/src/lib/hubspot/common.service.ts:1034-1046,1086-1087 |
| BR-06 | Thành công → đóng modal, popup "Duplicate completed — Pipeline "<label>" was created in the <env> portal.", rồi tự tải lại danh sách của portal / object đang chọn | M2 - action A2 | AC-M2.1 | fe/src/ui/components/works/sapp/pipeline-tool.tsx:166-179 |
| BR-07 | Lỗi: nguồn không có → 12007; đọc danh sách đích lỗi → 12002; tạo lỗi → 12008. Popup "Failed to duplicate pipeline" (NFR-H-04) | M2 - action A2 | EC-M2.1 | be/libs/modules/src/lib/hubspot/common.service.ts:1018-1023,1075-1079,1093-1098; fe/src/ui/components/works/sapp/pipeline-tool.tsx:165 |
| BR-08 | Khi đang nhân bản: nút "Cancel", "Duplicate" và ô nhập bị khoá, modal không đóng được | M2 | AC-M2.4 | fe/src/ui/components/works/sapp/pipeline-tool.tsx:185,434-439,448,456,486 |
⚠️ Observed (không phải BR):
- Nút "Duplicate" cũng hiện khi Environment = Prod và ghi vào portal HubSpot prod — A1b / A2 nhánh Prod, AC-M2.5, OQ-H-03.
- Popup "… was created …" kể cả khi không tạo gì (đích đã có) — AC-M2.3, OQ-H-14.
- Gọi API trực tiếp với label rỗng → trả pipeline nguồn như thành công — EC-M2.4, OQ-H-10.
- Danh sách không làm mới khi đổi Environment — EC-M1.1, OQ-H-15.
4. Design ​
a. Mối liên hệ giữa các màn & User Flow ​
text
[M1: Pipeline manager, danh sách đã tải] (US-H04)
↓ bấm "Duplicate" trên một pipeline (Environment = Dev hoặc Environment = Prod ⚠️)
[M2: Modal "Duplicate pipeline" — "into <env> portal"]
├─ Cancel ────────────────────────────→ đóng modal
├─ label trống ───────────────────────→ popup "Missing label", modal vẫn mở
├─ chưa có label đích → tạo ──────────→ popup "Duplicate completed", đóng modal, tải lại danh sách
│ (Dev → ghi dev · Prod → ⚠️ GHI PROD)
├─ đã có label đích → không tạo ──────→ popup "Duplicate completed … was created" (⚠️ Observed)
└─ lỗi (12001/12002/12007/12008) ─────→ popup "Failed to duplicate pipeline", modal vẫn mởLink flow chi tiết: N/A — as-built, không có Figma.
b. Chi tiết từng màn ​
Màn M1 - Pipeline manager (nút "Duplicate") ​
| Field | Value |
|---|---|
| Figma frame | N/A |
| Trigger vào màn | Như US-H04 M1, đã bấm "Load pipelines" (Prod hoặc Dev) |
| Mô tả ngắn | Mỗi thẻ pipeline có nút "Duplicate" mở modal M2, ở cả Prod và Dev. Hiển thị danh sách: xem US-H04 M1 |
UI States ​
| State | Khi nào | Hiển thị | Figma frame |
|---|---|---|---|
| Default | Danh sách đã tải | Nút "Duplicate" trên mỗi thẻ có id (Prod: cạnh "Clone to dev"; Dev: nút ghi duy nhất) | N/A |
| Loading | Đang nhân bản / tải lại sau nhân bản | Mọi nút / ô chọn bị khoá (US-H04 BR-07) | N/A |
| Has data | — | Như US-H04 M1; sau nhân bản thành công, danh sách có thêm pipeline mới | N/A |
| Empty | Không có pipeline | "No pipelines found." — không có nút | N/A |
| Error | — | Xem M2 | N/A |
| Disabled | Pipeline không có id | Nút "Duplicate" bị khoá | N/A |
Fields ​
N/A — M1 không có field nhập riêng cho US này; Environment / Object type mô tả ở US-H04 M1.
Filter / Search / Sort ​
Xem US-H04 M1 — không đổi.
Actions ​
A1a - Mở modal Duplicate (Environment = Dev) ​
| Item | Description |
|---|---|
| Trigger | Environment = Dev, bấm "Duplicate" trên một thẻ |
| Behavior | Mở M2 với nguồn = thẻ đó, label điền sẵn <label> (copy), phụ đề "… · into dev portal" (BR-02). Không gọi API, không ghi |
| Success Result | M2 mở |
Error cases:
| Case | Expected Handling |
|---|---|
| Pipeline không có id | Nút bị khoá |
A1b - Mở modal Duplicate (Environment = Prod) — ⚠️ Observed ​
| Item | Description |
|---|---|
| Trigger | Environment = Prod, bấm "Duplicate" trên một thẻ |
| Behavior | Mở M2 như A1a, phụ đề "… · into prod portal". Không có bước xác nhận riêng cho prod ngoài modal nhập label (fe/src/ui/components/works/sapp/pipeline-tool.tsx:317-353,431-433). Cố ý hay bug → OQ-H-03 |
| Success Result | M2 mở |
Error cases:
| Case | Expected Handling |
|---|---|
| Pipeline không có id | Nút bị khoá |
Acceptance Criteria của màn M1 ​
AC-M1.1 - Nút Duplicate ở Dev ​
gherkin
Given Environment = Dev và danh sách đã tải
Then mỗi thẻ pipeline có nút "Duplicate" và không có nút "Clone to dev"Maps to:
- Business Rule: BR-01
AC-M1.2 - Nút Duplicate ở Prod (hành vi hiện tại) ​
gherkin
Given Environment = Prod và danh sách đã tải
Then ⚠️ Observed: mỗi thẻ pipeline có cả nút "Clone to dev" và nút "Duplicate" (OQ-H-03)Maps to:
- OQ: OQ-H-03
Negative AC / Edge Cases của màn M1 ​
| ID | Tình huống | Hành vi mong đợi |
|---|---|---|
| EC-M1.1 | Tải danh sách ở Dev, đổi Environment sang Prod mà không tải lại, bấm "Duplicate" | ⚠️ Observed — hành vi hiện tại theo code: danh sách dev vẫn hiện; modal ghi "into prod portal"; xác nhận sẽ gọi duplicate trong prod với id lấy từ danh sách dev. Id không có ở prod → 12007; id trùng một pipeline prod → tạo pipeline trong prod (fe/src/ui/components/works/sapp/pipeline-tool.tsx:54-56,269,339-346,209-214). Phát hiện khi viết R3, chưa chạy thử — OQ-H-15 |
| EC-M1.2 | Đổi Object type sau khi tải danh sách | Tương tự EC-M1.1: nhân bản dùng object mới với id của danh sách cũ — OQ-H-15 |
Màn M2 - Modal "Duplicate pipeline" ​
| Field | Value |
|---|---|
| Figma frame | N/A |
| Trigger vào màn | A1a hoặc A1b ở M1 |
| Mô tả ngắn | Nhập label mới (bắt buộc) rồi xác nhận. Chú thích: "A pipeline with this label is created in the <env> portal, reusing the source stages. If one with the same label already exists it is returned unchanged." (fe/src/ui/components/works/sapp/pipeline-tool.tsx:499-503) |
UI States ​
| State | Khi nào | Hiển thị | Figma frame |
|---|---|---|---|
| Default | Vừa mở | Tiêu đề "Duplicate pipeline", phụ đề "Source: <label nguồn> · into <env> portal", ô "New label" = <label nguồn> (copy) (placeholder "e.g. Sales pipeline (copy)"), nút "Cancel", "Duplicate" | N/A |
| Loading | Đang nhân bản | Nút "Duplicating…"; Cancel, ô nhập, đóng modal không tác dụng (BR-08) | N/A |
| Has data | N/A — thành công thì modal đóng | — | N/A |
| Empty | Ô label trống | Bấm "Duplicate" → popup "Missing label" (BR-03) | N/A |
| Error | API lỗi | Popup "Failed to duplicate pipeline"; modal vẫn mở | N/A |
| Disabled | Đang nhân bản | Như Loading | N/A |
Fields ​
| Field | Required | Type | Default | Format | Validation | Error Message | Visibility | Depends On |
|---|---|---|---|---|---|---|---|---|
| New label | Yes (trên màn) | Text | <label nguồn> (copy) | Cắt khoảng trắng hai đầu | BR-03, BR-04 | "Missing label" — "Please enter a label for the duplicated pipeline." | Always | Là khoá so khớp tồn tại trong portal đích (BR-04) |
Filter / Search / Sort ​
N/A — modal xác nhận.
Actions ​
A2 - Duplicate (xác nhận) ​
| Item | Description |
|---|---|
| Trigger | Bấm "Duplicate" trong modal hoặc nhấn Enter trong ô label |
| Behavior | Kiểm BR-03 → gọi POST /api/v1/sapp/{dev|prod}/hubspot/{deals|tickets}/pipelines/:pipelineId/duplicate theo Environment đang chọn, body label. BE: đọc pipeline nguồn và danh sách pipeline của cùng portal; có pipeline cùng label → trả nguyên (BR-04); chưa có → tạo pipeline mới kèm stage (BR-05). Environment = Dev: ghi vào HubSpot portal dev. ⚠️ Ghi PROD — Environment = Prod: ghi vào HubSpot portal prod (portal CRM thật): tạo pipeline mới hiện ra cho mọi người dùng HubSpot prod (be/apps/api/src/sapp/sapp.controller.ts:956-977,1302-1323; be/libs/services/src/lib/hubspot/hubspot.service.ts:246-254). ⚠️ Observed — cố ý hay bug → OQ-H-03 |
| Success Result | Đóng modal; popup "Duplicate completed — Pipeline "<label>" was created in the <env> portal."; tải lại danh sách (BR-06) |
Error cases:
| Case | Expected Handling |
|---|---|
| Label trống | Popup "Missing label" (BR-03), không gọi API |
| Pipeline nguồn không tồn tại | Popup "Failed to duplicate pipeline" — "HubSpot pipeline not found (12007)" |
| Đọc danh sách đích lỗi | Popup … "HubSpot API request failed (12002)" |
| HubSpot từ chối tạo | Popup … "Failed to clone HubSpot pipeline (12008)" |
| Thiếu token portal | Popup … "(12001)" (NFR-H-02) |
A3 - Cancel ​
| Item | Description |
|---|---|
| Trigger | Bấm "Cancel" hoặc đóng modal khi không đang nhân bản |
| Behavior | Đóng modal, xoá label; không gọi API |
| Success Result | Về M1 |
Error cases:
| Case | Expected Handling |
|---|---|
| Đang nhân bản | Không đóng được (BR-08) |
Acceptance Criteria của màn M2 ​
AC-M2.1 - Nhân bản trong dev ​
gherkin
Given Environment = Dev, Object type = Deal, dev có pipeline "Sales Pipeline" (5 stage), chưa có "Sales Pipeline (copy)"
When người dùng bấm "Duplicate" trên "Sales Pipeline", giữ label "Sales Pipeline (copy)" và bấm "Duplicate"
Then portal dev có pipeline mới "Sales Pipeline (copy)" với 5 stage cùng label, thứ tự, metadata (id mới)
And pipeline gốc và portal prod không bị thay đổi
And modal đóng, popup "Duplicate completed — Pipeline "Sales Pipeline (copy)" was created in the dev portal."
And danh sách dev tự tải lại và có thêm pipeline mớiMaps to:
- Business Rule: BR-01, BR-02, BR-05, BR-06
- Field/Action: New label / A2
AC-M2.2 - Label trống ​
gherkin
Given modal Duplicate đang mở
When người dùng xoá hết ô "New label" (hoặc chỉ để khoảng trắng) và bấm "Duplicate"
Then hiện popup "Missing label" — "Please enter a label for the duplicated pipeline."
And không có lời gọi API nào, modal vẫn mởMaps to:
- Business Rule: BR-03
AC-M2.3 - Label đích đã tồn tại (hành vi hiện tại) ​
gherkin
Given portal đang chọn đã có pipeline label "Sales Pipeline (copy)"
When người dùng nhân bản "Sales Pipeline" với label "Sales Pipeline (copy)"
Then không có pipeline nào được tạo hay sửa
And ⚠️ Observed: popup vẫn "Duplicate completed — Pipeline "Sales Pipeline (copy)" was created in the <env> portal." (OQ-H-14)
And danh sách vẫn tự tải lạiMaps to:
- Business Rule: BR-04, BR-06
- NFR: NFR-H-07
- OQ: OQ-H-14
AC-M2.4 - Khoá khi đang nhân bản ​
gherkin
Given người dùng đã bấm "Duplicate" trong modal
When yêu cầu chưa có kết quả
Then nút hiện "Duplicating…", ô label và "Cancel" bị khoá, modal không đóng đượcMaps to:
- Business Rule: BR-08
AC-M2.5 - Nhân bản trong prod (⚠️ Ghi PROD — hành vi hiện tại) ​
gherkin
Given Environment = Prod, Object type = Deal, danh sách prod đã tải, prod chưa có "Sales Pipeline (copy)"
When người dùng bấm "Duplicate" trên "Sales Pipeline" của prod, giữ label và bấm "Duplicate"
Then ⚠️ Ghi PROD: portal HubSpot prod có pipeline mới "Sales Pipeline (copy)" với toàn bộ stage của nguồn
And không có bước xác nhận riêng nào cho prod ngoài modal nhập label (phụ đề "into prod portal")
And popup "Duplicate completed — Pipeline "Sales Pipeline (copy)" was created in the prod portal."
And danh sách prod tự tải lại
# ⚠️ Observed — cố ý hay bug chưa xác nhận (OQ-H-03)Maps to:
- OQ: OQ-H-03
- REL: REL-H-04
Negative AC / Edge Cases của màn M2 ​
| ID | Tình huống | Hành vi mong đợi |
|---|---|---|
| EC-M2.1 | Pipeline nguồn bị xoá sau khi tải danh sách | Popup "Failed to duplicate pipeline" — "(12007)"; modal vẫn mở (BR-07) |
| EC-M2.2 | Label đích khác nguồn chỉ ở hoa / thường, hoặc pipeline sẵn có mang khoảng trắng thừa | So khớp chính xác, chỉ cắt khoảng trắng label mới → tạo thêm pipeline (be/libs/modules/src/lib/hubspot/common.service.ts:1068,1081-1083) — OQ-H-13. Ở Prod, hệ quả là thêm pipeline trong prod |
| EC-M2.3 | Người dùng sửa label thành đúng label nguồn (vd "Sales Pipeline") | Label đích trùng chính pipeline nguồn → trả pipeline nguồn, không tạo; popup vẫn "… was created …" (BR-04, ⚠️ Observed — OQ-H-14) |
| EC-M2.4 | Gọi API trực tiếp (không qua màn) với label rỗng / chỉ khoảng trắng | ⚠️ Observed — qua được validate; BE rơi về label nguồn, tìm thấy chính pipeline nguồn và trả nó như thành công, không tạo gì (be/libs/interface/src/lib/dto/hubspot/hubspot-pipeline.dto.ts:52-55; be/libs/modules/src/lib/hubspot/common.service.ts:1068,1081-1084) — OQ-H-10, Overview §2.2 |
| EC-M2.5 | Gọi API duplicate prod trực tiếp, không đăng nhập | Hoạt động (NFR-P-01) → ⚠️ Ghi PROD không qua gate FE — OQ-H-02, OQ-H-03 |
5. Cross-feature AC ​
AC-X.1 - Tool bị gate chặn ​
gherkin
Given tool sapp.004 đang bị ẩn / tắt / khoá mã bí mật
When người dùng mở /works/sapp/tools/hs-pipeline
Then hành vi theo gate của nền tảng (US-P02)
And route duplicate (dev và prod) vẫn gọi trực tiếp được (NFR-P-01, Overview §2.2)Maps to:
- REL-H-01 (Affects - Hub + gate tool)
AC-X.2 - Token theo portal đang chọn ​
gherkin
Given SAPP_PROD_HUBSPOT_ACCESS_TOKEN chưa cấu hình, token dev có
When người dùng nhân bản với Environment = Prod
Then nhận lỗi 12001 và không có gì được tạo; nhân bản với Environment = Dev vẫn hoạt độngMaps to:
- REL-H-02 (Affects - Cấu hình env_systems)
AC-X.3 - Pipeline mới xuất hiện trong HubSpot prod ​
gherkin
Given nhân bản thành công với Environment = Prod (AC-M2.5)
When người dùng HubSpot prod mở danh sách pipeline Deal trên HubSpot
Then thấy pipeline mới "Sales Pipeline (copy)" [suy từ code, chưa chạy thử]Maps to:
- REL-H-04 (Be Affected - HubSpot portal prod)