Commit Graph

7 Commits

Author SHA1 Message Date
Macky
056753e8cb feat(saas): multi-tenant isolation (Phase 1) + rate-limit/audit/org-scoped export (Phase 2)
Phase 1 (tenant isolation):
- g.org_id set on require_auth; assert_tenant()/current_org_id() choke-point helpers.
- Multi-org provisioning: POST /api/admin/users {new_org:true} (super_admin) creates a
  new org + its first admin; GET /api/admin/orgs (super_admin sees all, admin own).
- Fixed latent create_org double-id bug (dict id != store key).
- test_saas_tenant.py: org2 admin blocked from org1 group (403), can't list org1
  groups/users, sees only own org; super_admin sees all.

Phase 2 (hardening):
- Rate limit login (per-IP + per-username) + chat send (per-user) to protect LLM cost
  and slow brute force; services/rate_limit.py (in-memory + disk, no external deps).
- Audit log data/audit/audit.jsonl on org.create, user.promote_super_admin, analytics.export.
- CSV export now org-scoped (admin exports only own org).
All 8 backend suites pass.
2026-08-09 09:35:26 +07:00
Macky
15c60ae400 feat(i18n + export + guide): locale-aware scenarios/debrief; CSV export; how-to-use page
Backend:
- SCENARIOS localized (th/en): scenario preamble + persona tone adaptation follow the
  trainee's locale; start accepts {locale} and stores it on the session; send/auto-finish
  use it so debrief text (why/coaching/turning points) is in the active language.
- /api/analytics/export returns per-trainee finished-session CSV (admin).

Frontend:
- Scenario picker labels localize by i18n.locale.
- New /guide page (non-IT how-to), linked from Training.
- Analytics: date-filter bar uses line icon + 'Download CSV' button (fetch w/ auth).
Rebuilt dist.
2026-08-08 15:06:48 +07:00
Macky
aa2eb8dd37 feat(chat): persona decides to buy/walk — auto-finish + tolerance (temper) + resume
The conversation now ENDS when the persona makes a decision (option C), not when the
trainee clicks a button:
- persona_id replies carry {reply, decision(none/buy/walk), mood}; when decision is
  buy/walk the session auto-finishes (won/lost) with a debrief that reveals latent
  details + per-turn 'turning points'.
- personas have a tolerance (1-5, 'temper'): impatient personas walk away fast after
  poor answers (fed via internal.misses on mood<=-1); tough 'wrong text' cases can
  still be won by a strong, gentle response (judge realism).
- trainee 'Finish' button removed; if they leave mid-chat an active session is resumed
  via /chat/resume (continue, not restart). One-shot lock still enforced once decided.
- mock/tests updated: persona deciding buy -> send auto-finishes won.
Rebuilt dist.
2026-08-08 14:34:34 +07:00
Macky
bd6a7ffa32 feat(chat): scenario-based training — choice of channel/Situation + realistic per-turn evaluation
Backend:
- Channel/initiation now driven by a SCENARIO chosen at chat start, not baked into the
  persona: social (customer opens), f2f_call (seller must open, proactive), recontact
  (customer re-contacts after 1-3 months).
- /chat/start accepts {scenario}; session stores scenario + internal{turns,score}.
- persona_reply takes scenario + adapts tone; system-role transcript entries are fed to
  the persona as hidden scene notes.
- JUDGE updated for realism: good response can WIN even in hard/tough-text scenarios;
  long/no-close chats (turns >~12) lose; pushy/ignoring-need loses. Efficiency rewarded.

Frontend:
- Scenario picker before chat (choose Social / Face-to-face-call / Re-contact).
- Chat thread renders role=system as a centered time-lapse/scene note.
- Choose-scenario i18n (EN+TH).
Rebuilt dist.
2026-08-08 11:27:05 +07:00
Macky
ac35be8906 feat(ui): 3-tab layout (Admin overview / My dashboard / Training) + Settings page
- App.vue: top tab bar (Admin overview [admin], My dashboard, Training) + Settings icon
- router: / = admin overview, /my/board, /training, /settings; non-admin '/' -> /my/board
- New views: Training.vue (pick ready product group -> personas), MyBoard.vue (win/lose +
  sessions summary), Settings.vue (change email/password, language)
- Backend: allow /api/me/board + /api/chat/sessions for any authenticated user (so the
  My dashboard tab works for admins too), not just 'user' role
- i18n EN/TH keys; rebuilt frontend/dist
Verified: build ok, /api/me/board + /api/chat/sessions + /api/groups all 200 for admin.
2026-08-07 22:07:16 +07:00
Macky
ff0f680090 [verified] Security hardening + UX/UI polish
Security (requesting-code-review pipeline + independent reviewer):
- Fix path traversal on file upload (basename sanitize + resolve-containment)
- Fix IDOR: org + owner scoping on all group/chat routes (_authorize_group/_get_owned_group),
  hide other users' personal groups in listings
- Remove XSS via v-html in Chat task (text interpolation)
- Add test_security.py (traversal + cross-user denial) — all pass

UX/UI (ui-ux-pro-max + frontend-dev-verification):
- Global: focus rings, 44px touch targets, hover/press transitions, input focus glow,
  prefers-reduced-motion, skeleton loaders, empty states, back links, spinner
- Login: password toggle, autocomplete, spinner, disabled-when-empty
- Cards lift on hover; dashboard skeleton + empty state; analyze button spinner

All backend tests pass (m0/m1/routes/security/e2e); frontend builds; served SPA verified via curl.
2026-08-07 16:00:43 +07:00
Macky
c3d31c06e2 Sales Trainer v0.1: corporate sales-training simulator (Flask+Vue, 15 personas, chat simulator, judge, analytics)
- Auth/roles (no self-reg), admin user provision, JWT
- Analyze: sales kit + initial pain-fit from form/upload
- Persona generator: 15 personas (5/tier) w/ pain variety, negotiation, init mode, channel, latent/revealable, wrong_text special
- Chat simulator: per-mode initiation, one-shot, hidden signals, judge-LLM debrief+coaching
- Trainee loop: win/lose board, weak-areas, user-generated personas
- Admin analytics; EN+TH Vue SPA served by Flask
- Deploy: Dockerfile, docker-compose, README, eng-log + HANDOFF
- Tests (mock LLM): m0/m1/routes/e2e all pass
2026-08-07 15:31:06 +07:00