- auth: self-registration (role=user); first-created-user becomes super_admin - roles: super_admin may promote others; regular admin cannot see super_admin accounts - products: user-created private groups; admin 'สินค้าขององค์กร' (shared) with hidden/public; users can create groups - analytics: team + per-user weak areas, close-rate-by-difficulty buckets, 30-day default, weekly trend, trainee table, active users; dashboard redesigned - files: docx + xlsx upload support (python-docx + openpyxl) - ui: tabs การฝึก→ผลการฝึก→ภาพรวม; admin lands on ภาพรวม / user on การฝึก; guide in topbar - consolidate: weak-areas merged into Results (10/page), my-personas merged into Training - copy: บุคคลต้นแบบ→persona everywhere; clearer add-product form (A/B/C, upload-or-fill) - report: remove ดูรายงาน UI entry (endpoint kept) Backend 348 tests pass; frontend build + vitest clean.
8.6 KiB
8.6 KiB
UX/SAAS Redesign — Plan (2026-08-21)
Owner review of Sales Trainer → 12-point redesign toward a multi-tenant SAAS model. Decisions locked via clarify 2026-08-21.
P0 — Locked decisions
- Super admin policy: super_admin MAY promote others to super_admin (operates on trust). The first user created becomes super_admin automatically. Regular admin does NOT see super_admin usernames in the user list and CANNOT change/see them; only super_admin manages admins.
- SAAS product scoping: user-created product = private group (owner sees it only, existing
owner_user_idmodel). admin-created product = สินค้าขององค์กร (shared, org-wide). Two clearly separated sections in Training UI. - Difficulty split: 3 buckets — ง่าย (difficulty 1–2) / กลาง (3) / ยาก (4–5). Close rate shown per bucket so viewers cannot misread "many wins = good" when trainees pick only easy personas.
- Dashboard extras: total sessions + active users + weekly training trend + per-trainee table (each user: sessions · wins · close rate · dominant weak area). Anti-misread priority: the difficulty split must be prominent — never let aggregate wins appear good when wins skew easy.
- Report removal: remove the "ดูรายงาน" button + page from the UI only. Keep the
/reportendpoint + stored report data (non-breaking; admin tooling still works via API).
P1 — Backend changes
B1. Auth / registration (point 1, 5)
- New
POST /api/auth/register(username + password + email) → creates userrole="user", seat-checked, in tenant org. Nomust_setup(password set at signup) unless desired else first-login setup. - First-created-user rule: when the platform has zero users, the first registered/created
user is promoted to
super_adminautomatically (bootstrap). When users already exist, default role = user. - Keep OAuth path (already role=user).
B2. Role hierarchy (point 4, 5)
- Allow an existing
super_adminto setrole="super_admin"on another user via admin update (currently blocked: "provisioned only by bootstrap"). list users(admin view): a regularadminMUST NOT see rows whoserole == "super_admin". Onlysuper_adminsees all.super_admincan change status/role of any admin; regular admin cannot change admin/super_admin.- Ensure role-change rules: only super_admin can grant
adminorsuper_admin; admin grants user only.
B3. Group visibivity / creation (point 6, 7)
- Allow
user(trainee) to create groups → these become private (owner_user_id = self,status= ready after analyze).create_grouproute: relaxrequire_roles("admin")to allow user with forced owner marker + private visibility. - Admin-created group = org-shared สินค้าขององค์กร (no
owner_user_id). - New visibility field on admin/shared groups for point 7:
visibility/published:public(default when shared) → visible to all trainees.draft/hidden→ hidden from trainees; admin can still train it (preview mode).- Admins always see both. A hidden group is trainable by admin (its sessions flagged preview so
they don't pollute trainee analytics — reuse existing
previewModelogic).
list_visible_toupdate: user sees (their own private ready groups) + (org shared groups wherepublished/public == trueandstatus == ready). Admin sees all org shared (incl hidden/draft)- never other users' private groups (except super_admin).
B4. Analytics (point 3)
- Default date window = last 30 days (today−30 → today). Keep optional from/to override.
- Team weak areas: aggregate finished trainee sessions → same dimension analysis as per-user
weak-areas, but across all org trainees (reuse
analyze_weak_areas, feed with all finished trainee sessions). - Per-user weak areas: in the per-trainee table (B5) each row carries that user's top weak area.
- Close rate by difficulty: bucket persona by
difficulty1–2 / 3 / 4–5 → per-bucket{plays, wins, losses, close_rate}inoverall.difficulty(or top-levelclose_by_difficulty). - Anti-misread: also add an overall breakdown note/flag when bucket win-share skews to ง่าย; UI will emphasize buckets.
- Remove
hardest_personasfrom analytics response (replaced by weak areas); keep or drop — replace withteam_weak_areas. - Extra metrics:
total_sessions,active_users(distinct trainees with ≥1 session in window),weekly_trend(sessions+wins per ISO week in window),trainee_table(per user: play/win/loss/ close_rate/avg_score/top_weak_area).
B5. File parser (point 12)
- Support
.docx(python-docx) and.xlsx(openpyxl) inparse_document, with size caps + safety.
P2 — Frontend changes
F1. Nav / tabs / landing (point 2, 3, 10)
- Tab order: การฝึก → ผลการฝึก → ภาพรวม (training first for everyone).
- Renames:
myDashboard→ผลการฝึก;adminOverview→ภาพรวม (admin only tab). - Admin landing: after login an admin lands on ภาพรวม (analyze-first). Non-admin lands on การฝึก (train-first). (Router redirect + redirectAfterLogin update.)
- First-time user with no products → go to add-product form directly.
- Guide moved to topbar: replace standalone
/guidenav entry with a topbar question-mark/guide link + dropdown (role-aware). Keep/guideroute for the full page if desired, but entry point = topbar.
F2. Page consolidation (point 3, 4, 5)
- ผลการฝึก page = MyBoard: weak-areas section at top (before history), then history
paginated 10/page. Remove standalone
/my/weak-areaspage (merge logic in). - การฝึก page = Training: product grid (user's own products section, then สินค้าขององค์กร
section below), then Persona ของฉัน (private personas) section below product list. Remove
standalone
/my/personaspage (merge logic in). - Admin only: manage/edit product routes stay.
F3. Copy / language (point 1, 6, 8, 11)
- Replace บุคคลต้นแบบ → persona everywhere (i18n EN + TH + docs). Note: user explicitly reversed the old "persona→บุคคลต้นแบบ" standard.
- "บุคคลต้นแบบส่วนตัว" → Persona ของฉัน / Persona ส่วนตัว (short, clear).
- Guide rewrite aware of role (user sees train-personas; admin sees manage/analytics/users).
- Polished, plain-Thai helper text throughout.
F4. Add-product form (point 12)
- Richer example placeholders for product + segment + description (e.g. "เน้นเพศหญิง รายได้สูง… จำกัดสถานการณ์ที่จำลองได้"). Provide several ready examples.
- Clear ระดับ A/B/C explanation panel.
- Upload: accept
.pdf .md .txt .docx .xlsx; helper text: "อัปโหลดไฟล์กลุ่มเป้าหมาย/รายละเอียด แทนการกรอกช่องด้านบนได้" (encourage upload-or-fill, not both required). - Wording pass: product form labels/descriptions clearer.
F5. Report removal UI (point 9)
- Remove "ดูรายงาน"/Download report button + GroupReport link from Group edit UI. Keep backend endpoint untouched.
P3 — Deliverables / verification
- Backend pytest suite green (existing 336 + new tests: register default role, first-user super, super visibility filter, admin promote, user-group creation, draft/hidden visibility, analytics difficulty buckets + 30-day default, docx/xlsx parse).
- Frontend
vite buildclean + vitest unit green. - Independent reviewer subagent on the diff.
- Mobile QA at 320×568 + 500×768 after rebuild (per project rule).
- Docs: engineering-log entry + HANDOFF update.
- Deploy: Gitea→EasyPanel path, operator approval before push.
Open items / notes
- Whether first user (username+password) uses
must_setupfirst-login (set email/pw) or full self-signup. Default: self-register sets username+password+email at signup → skipmust_setup. - Org/tenancy: self-register lands in
OAUTH_DEFAULT_ORG-style default org (like OAuth) unless a tenant-invite flow exists. First-user-super_admin is global bootstrap (org-less) — mirror existing super_admin boot semantics. - "สินค้าขององค์กร" naming + section split UI confirmed by owner in clarify.