Customer Portal & My Home — Case thường gặp
403 khi vào Customer Portal lúc bootstrap
Chỉ app_type ∈ {aoadmin, onsite} mới gọi staff endpoints; customer mode không gọi Manage/CurrentUser / Employee/LinkOrg.
Ví dụ thực tế: User không có staff profile mở portal bị 403. → Chẩn đoán gọi sai endpoint theo mode. →
app_typecustomer mode nhưng vẫn gọiManage/CurrentUser→ bỏ call đó khỏi bootstrap; hết 403.
Giờ hiển thị lệch theo browser
ServiceAreaId = Local → không convert, giờ phải khớp wall-clock source ở mọi browser. MultiZone → resolve effective timezone từ User.TimezoneIdentifier, null → browser.
Ví dụ thực tế: Staff mở Calendar trên laptop ở timezone khác, enterprise
Local. → Giờ không được đổi theo máy. → Kiểm traEnterpriseSetting.ServiceAreaId; nếuLocal, frontend không convert, SyncfusionScheduleComponentkhông reinterpret qua user-local timezone.
Prompt/popup timezone không hiện hoặc hiện sai lúc
Gating theo thứ tự: authenticated (không guest/unresolved) → MultiZone → User.TimezoneIdentifier null → chưa dismiss trong session. Save hay dismiss đều set flag; hai surface dùng hai key riêng.
Ví dụ thực tế: User MultiZone có sẵn timezone vẫn thấy popup. → Popup không auto-trigger khi đã có timezone. →
User.TimezoneIdentifierkhông null → bỏ qua popup; user vẫn tự mở modal quaTimezoneIndicatorbadge nếu muốn.
My Subscriptions hiện dữ liệu account khác
Backend phải scope cứng theo authenticated account; client filter (nếu gửi) bị ignore/reject. Loading/error state không render row stale của account khác.
Ví dụ thực tế: Request My Subscriptions kèm account identifier của người khác. → Không được trả dữ liệu chéo account. → Backend bỏ qua client filter, scope theo account đã đăng nhập; không hiện enrollment account khác.
Section My Subscriptions thiếu/không đúng tab
Map tab ↔ status: Pending → Submitted, Confirmed → Approved, Cancelled → Canceled; Waiting/Cart → ẩn section. Đổi tab lọc trên data đã fetch, không re-fetch.
Ví dụ thực tế: Khách mở tab Confirmed nhưng không thấy section. → Xem có enrollment
Approvednào không. → Tab Confirmed render section chỉ khi cóApproved; không có → bỏ header/divider section.
Period đã cancel vẫn xuất hiện ở Booking Range
Filter booked period = IsActive = true và StatusId != Canceled; cancel workflow để IsActive = true nên lọc IsActive một mình không đủ.
Ví dụ thực tế: Enrollment có period cancel (
StatusId = Canceled,IsActive = true). → Không được hiện trong list lẫn calendar. → Booking Range và drawer View Calendar cùng loại period đó vì thiếuStatusId != Canceled.
Share link trỏ về Admin Portal hoặc lộ token
Mọi share action compose theo Customer Portal base URL, không theo browser origin (INV-CP-SHARELINK-001); không admin prefix, không bearer-token query param; URL hiển thị = URL copy.
Ví dụ thực tế: Staff bấm share từ Admin Portal, khách nhận link có token/session artifact. → Không được lộ admin context. → Compose lại theo Customer Portal origin, chỉ giữ param program/term/site/category/TPS; clipboard nhận đúng chuỗi hiển thị.
Card thiếu contact nhưng vẫn render row trống
Site card / Program Details chỉ render row khi có giá trị; thiếu email/phone/address → bỏ row, giữ thứ tự (name → phone → email → address).
Ví dụ thực tế: Site không có email. → Không để hàng trống gây rối mắt. → Booking UI Program Details chỉ hiện row mobile; landing card bỏ row email, giữ phone rồi address.
Sidebar timezone không hiện hoặc không lưu
Sidebar Select chỉ hiện khi MultiZone (enSetting.serviceAreaId === 1) và user đã login; option từ catalog frontend (getSystemTimezoneOptions()), không phụ thuộc API hydration. Save lỗi → re-enable + revert giá trị cũ.
Ví dụ thực tế: User đổi timezone trong sidebar nhưng request fail giữa chừng. → Không để hiện giá trị không lưu. →
updateUserTimezonethrow → Select re-enable, hiện lạitimezoneIdentifiercũ; không gọi lại tới khi user chọn lại.