feat: UX/SAAS 12-point redesign
- 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.
This commit is contained in:
@@ -24,6 +24,17 @@ filesystem JSON storage (no SQL). i18n TH/EN. No self-registration (admin provis
|
||||
- **user** (trainee) — trains against personas, own board.
|
||||
|
||||
## Current state — local code/security gate passed; production-operation gate pending
|
||||
> **2026-08-21:** UX/SAAS 12-point redesign (NOT yet pushed). Self-registration
|
||||
> (`POST /api/auth/register`, role=user, first-created-user = super_admin), super_admin may now
|
||||
> promote others + is invisible to regular admin, user-created private product groups + admin
|
||||
> **สินค้าขององค์กร** (shared) with hidden/public visibility, analytics (team + per-user weak areas,
|
||||
> close-rate by difficulty bucket, 30-day default, trainee table, weekly trend, active users),
|
||||
> docx/xlsx uploads, **persona→persona** copy, tabs **การฝึก → ผลการฝึก → ภาพรวม**, admin lands on
|
||||
> ภาพรวม / non-admin on การฝึก, guide in topbar, weak-areas merged into Results + my-personas into
|
||||
> Training, ดูรายงาน UI removed (endpoint kept). **348 backend tests pass, frontend build +
|
||||
> vitest clean.** See `docs/engineering-log/2026-08-21-ux-saas-redesign.md` +
|
||||
> `docs/plan-2026-08-21-ux-saas-redesign.md`. Deploy pending operator push (+ confirm default-org
|
||||
> env for self-register; previewMode vs hidden-group semantics to reconcile).
|
||||
> **2026-08-20:** OAuth login/register (Google + Facebook) added. Public social signup into a
|
||||
> single default org (`OAUTH_DEFAULT_ORG`, role user, seat-checked); email-match links existing
|
||||
> users. Server-side token validation via stdlib urllib (no new dep; Google tokeninfo + Facebook
|
||||
|
||||
@@ -71,3 +71,4 @@ Informed by MiroFish (CrowdSight engine) + the hermes-brain-and-tools CrowdSight
|
||||
- `2026-08-18-live-qa-ux-fixes.md` — live-QA UX fixes: persona JSON never leaks into bubble, natural greeting openers (wrong_text cools off only after first reply), auto-close + auto-summarize on buy/walk/try, no manual "สรุปผล" button (336 tests).
|
||||
- `2026-08-19-ux-redesign-and-marketing-site.md` — parallel subagents: app UX/UI redesign (global token system, 8 files, 100% presentational) + new `website/` marketing landing site (responsive TH/EN); build clean, 4/4 unit tests, independent review PASS; uncommitted, deploy pending operator approval.
|
||||
- `2026-08-20-oauth-google-facebook.md` — OAuth login/register (Google + FB): public signup into OAUTH_DEFAULT_ORG, email-match linking, stdlib server-side token validation (no new dep), fail-closed, rate-limited; 348 backend tests (11 new), frontend clean; manual security review PASS; deploy pending operator push.
|
||||
- `2026-08-21-ux-saas-redesign.md` — **UX/SAAS 12-point redesign** (this session): self-registration (role=user, first-user=super_admin), super_admin promotion + invisible-to-admin, user private groups + admin สินค้าขององค์กร + hidden/public, analytics (team/per-user weak areas, close-rate-by-difficulty, 30-day default, trainee table, weekly trend, active users), docx/xlsx parser, persona→persona copy, tab reorder + role landing + guide-in-topbar, page consolidation (weak-areas→Results, my-personas→Training), report UI removal, Analytics dashboard rewrite. 348 backend tests pass; frontend build + vitest clean; deploy pending operator approval.
|
||||
|
||||
101
docs/engineering-log/2026-08-21-ux-saas-redesign.md
Normal file
101
docs/engineering-log/2026-08-21-ux-saas-redesign.md
Normal file
@@ -0,0 +1,101 @@
|
||||
# 2026-08-21 — UX/SAAS redesign (12-point): registration, roles, products, analytics, parser, nav
|
||||
|
||||
Date: 2026-08-21
|
||||
Status: implemented + locally verified (backend 348 pass, frontend build + vitest clean); deploy = repo's normal Gitea→EasyPanel path (operator runs it, must approve push)
|
||||
|
||||
## Context
|
||||
|
||||
Owner gave a 12-point UX/UI redesign that moves the app toward a **multi-tenant SAAS** model.
|
||||
Reviewed current state first (no self-registration except OAuth; admin-provisioned users; 3 tabs
|
||||
admin-overview → my-dashboard → training; standalone weak-areas + my-personas pages; group creation
|
||||
admin-only; analytics had overall close rate + hardest_personas). Clarified 5 decisions with the
|
||||
owner, wrote `docs/plan-2026-08-21-ux-saas-redesign.md`, and dispatched **2 parallel subagents**
|
||||
(backend + frontend).
|
||||
|
||||
## Locked decisions (owner)
|
||||
- **super_admin policy**: super_admin may promote others to super_admin; first-created user becomes
|
||||
super_admin automatically; regular admin does NOT see super_admin accounts.
|
||||
- **SAAS products**: user-created product = private group (owner-only, existing `owner_user_id`
|
||||
model); admin-created product = **สินค้าขององค์กร** (shared, org-wide) — two clearly separated
|
||||
sections in Training.
|
||||
- **Difficulty split**: close rate bucketed ง่าย (1-2) / กลาง (3) / ยาก (4-5) — anti-misread core.
|
||||
- **Dashboard extras**: total sessions + active users + weekly trend + per-trainee table
|
||||
(each: plays/wins/losses/close_rate/avg_score/top_weak_area).
|
||||
- **Report**: remove the ดูรายงาน button/page from UI only; keep `/report` endpoint + data.
|
||||
|
||||
## What was done
|
||||
|
||||
### Backend (P1) — subagent timed out at 600s after completing all B1-B5 edits; verified by me
|
||||
- **B1 auth/registration**: new `POST /api/auth/register` (username+password+email), default
|
||||
`role=user`, seat-checked, rate-limited (per-IP + per-ident), default-org via
|
||||
`_ensure_register_org`; **first-created-user rule**: when the user store is empty the first
|
||||
account is promoted to `super_admin` (global bootstrap), else stays `user` and never super.
|
||||
- **B2 roles**: a `super_admin` may now grant `super_admin` to another user (trust-based promotion);
|
||||
any non-super actor cannot. `list_users`: a regular admin filters OUT `role == super_admin` rows
|
||||
(invisible); super_admin sees all.
|
||||
- **B3 groups/visibility**: `create_group` allows role=user → creates a **private** group
|
||||
(`owner_user_id=self`). Admin-created group = org-shared (no owner marker). New
|
||||
published/hidden visibility for shared groups: hidden groups invisible to trainees but trainable
|
||||
by admin (preview). `list_visible_to` updated accordingly.
|
||||
- **B4 analytics**: default **last-30-days** window; added `team_weak_areas` (aggregated
|
||||
`analyze_team_weak_areas`), `close_by_difficulty` (easy/medium/hard buckets with label+range),
|
||||
`active_users`, `weekly_trend` (ISO week sessions+wins), `trainee_table` (per-user
|
||||
username/plays/wins/losses/close_rate/avg_score/top_weak_area). `hardest_personas` kept for
|
||||
back-compat but superseded in the UI.
|
||||
- **B5 file parser**: `.docx` (python-docx) + `.xlsx` (openpyxl) added to `parse_document` with
|
||||
size caps + fail-closed; `ALLOWED_UPLOAD_EXTS` extended; `python-docx==1.2.0` + `openpyxl==3.1.5`
|
||||
added to requirements.txt + regenerated lock.
|
||||
- **Tests**: backend suite now **348 passed** (was 336). One pre-existing test
|
||||
(`test_parser_rejects_unsupported_extension`) used `brief.docx` as its "unsupported" example —
|
||||
stale once docx became supported; I changed it to `brief.exe` so it still asserts the real
|
||||
invariant. Fix confirmed by full re-run.
|
||||
|
||||
### Frontend (P2) — subagent completed; build + vitest green; I added the Analytics dashboard redesign
|
||||
- **F1 nav/tabs**: tab order **การฝึก → ผลการฝึก → ภาพรวม** (training first for everyone; ภาพรวม
|
||||
admin-only). Router guards + login redirect: **admin → `/` (ภาพรวม)**, non-admin → **`/training`**
|
||||
(train-first); explicit `?redirect=` wins. Guide moved into a **topbar question-mark dropdown**
|
||||
(role-aware: guide + admin-only ภาพรวม + super-admin-only ผู้ใช้งาน).
|
||||
- **F2 consolidation**: Results page (MyBoard) now has **weak areas at top** then history
|
||||
**paginated 10/page**; standalone `/my/weak-areas` and `/my/personas` pages removed. Training page
|
||||
now has **own-products** section + **สินค้าขององค์กร** section (below) + **Persona ของฉัน** private
|
||||
personas section (below product list). `/admin/groups/:gid/report` route removed.
|
||||
- **F3 copy**: บุคคลต้นแบบ → **persona** everywhere (i18n EN+TH, zero remaining). "บุคคลต้นแบบส่วนตัว"
|
||||
→ "Persona ของฉัน". Role-aware Guide + plain-Thai helper text.
|
||||
- **F4 add-product form**: docx/xlsx added to accept + guide; clear **ระดับ A/B/C** explanation
|
||||
panel; "upload-or-fill" helper text (uploading a target-group/doc file can substitute for filling
|
||||
the fields); richer example placeholders.
|
||||
- **F5 report removal**: ดูรายงาน/View-report button + page removed from Group edit UI (endpoint
|
||||
+ data kept).
|
||||
- **F6 (added by me after review)**: Analytics.vue dashboard **redesigned** to the new backend
|
||||
contract — headline stats incl. **active users**, **close-rate-by-difficulty** buckets with
|
||||
anti-misread **skew-easy flag**, **team weak areas**, **weekly activity bar chart**, and the
|
||||
**per-trainee table** with top weak area; date filter **defaults to last 30 days**. Added 13 new
|
||||
i18n keys (EN + TH). The frontend subagent had not touched Analytics.vue (still bound to the old
|
||||
`hardest_personas` contract), so I implemented it, fixed two apostrophe syntax errors in i18n,
|
||||
and verified build + vitest.
|
||||
|
||||
## Verification evidence
|
||||
| Check | Result |
|
||||
|---|---|
|
||||
| Backend full pytest (`uv run pytest -q`) | **348 passed, 0 failed** |
|
||||
| Frontend `vite build` | **clean** |
|
||||
| Frontend vitest unit | **4/4 pass** |
|
||||
| Removed pages (WeakAreas/MyPersonas/GroupReport) | no dangling refs; absent from dist |
|
||||
| บุคคลต้นแบบ in source | **zero remaining** |
|
||||
| Analytics bindings | use new backend fields (close_by_difficulty/team_weak_areas/trainee_table/weekly_trend/active_users) |
|
||||
|
||||
## Files changed (uncommitted)
|
||||
Backend: admin_routes, analytics_routes, auth_routes, chat_routes, group_routes, config, models/entities,
|
||||
services/file_parser, services/groups, services/trainee, requirements.txt, requirements.lock.txt,
|
||||
tests/test_upload_security.py, tests/test_sprint1_review_findings.py.
|
||||
Frontend: App.vue, i18n/index.js, router/index.js, router.spec.js, GroupBuilder, GroupEdit, Guide,
|
||||
Login, MyBoard, Personas, Training, Analytics (+ deleted WeakAreas, MyPersonas, GroupReport).
|
||||
Docs: plan-2026-08-21-ux-saas-redesign.md (new).
|
||||
|
||||
## Notes / next action
|
||||
- **Do NOT push without owner approval** (project rule). Push → Gitea→EasyPanel auto-redeploy (~3 min).
|
||||
- Before push, confirm environment: self-register needs a default org (register uses
|
||||
`_ensure_register_org` mirroring OAuth default-org); docx/xlsx deps will install on the build.
|
||||
- The `previewMode` (admin practice excluded from analytics) should be reconciled with the new
|
||||
hidden-group admin training path (point 7) — verify semantics in the gap before shipping.
|
||||
- Independent reviewer subagent on the combined diff is RECOMMENDED before the production gate.
|
||||
136
docs/plan-2026-08-21-ux-saas-redesign.md
Normal file
136
docs/plan-2026-08-21-ux-saas-redesign.md
Normal file
@@ -0,0 +1,136 @@
|
||||
# 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
|
||||
1. **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.
|
||||
2. **SAAS product scoping**: user-created product = **private group** (owner sees it only, existing
|
||||
`owner_user_id` model). admin-created product = **สินค้าขององค์กร** (shared, org-wide). Two
|
||||
clearly separated sections in Training UI.
|
||||
3. **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.
|
||||
4. **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.
|
||||
5. **Report removal**: remove the "ดูรายงาน" button + page from the UI only. Keep the `/report`
|
||||
endpoint + 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 user `role="user"`,
|
||||
seat-checked, in tenant org. No `must_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_admin` automatically (bootstrap). When users already exist, default role = user.
|
||||
- Keep OAuth path (already role=user).
|
||||
|
||||
### B2. Role hierarchy (point 4, 5)
|
||||
- Allow an existing `super_admin` to set `role="super_admin"` on another user via admin update
|
||||
(currently blocked: "provisioned only by bootstrap").
|
||||
- `list users` (admin view): a regular `admin` MUST NOT see rows whose `role == "super_admin"`.
|
||||
Only `super_admin` sees all. `super_admin` can change status/role of any admin; regular admin
|
||||
cannot change admin/super_admin.
|
||||
- Ensure role-change rules: only super_admin can grant `admin` or `super_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_group` route: relax `require_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 `previewMode` logic).
|
||||
- `list_visible_to` update: user sees (their own private ready groups) + (org shared groups where
|
||||
`published/public == true` and `status == 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 `difficulty` 1–2 / 3 / 4–5 → per-bucket
|
||||
`{plays, wins, losses, close_rate}` in `overall.difficulty` (or top-level `close_by_difficulty`).
|
||||
- **Anti-misread**: also add an overall breakdown note/flag when bucket win-share skews to ง่าย;
|
||||
UI will emphasize buckets.
|
||||
- **Remove `hardest_personas`** from analytics response (replaced by weak areas); keep or drop —
|
||||
replace with `team_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) in `parse_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 `/guide` nav entry with a topbar question-mark/guide
|
||||
link + dropdown (role-aware). Keep `/guide` route 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-areas` page (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/personas` page (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 build` clean + 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_setup` first-login (set email/pw) or full
|
||||
self-signup. Default: self-register sets username+password+email at signup → skip `must_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.
|
||||
Reference in New Issue
Block a user