Macky 22d3e51980 ux(ui): beginner-friendly guidance across trainee/admin flows + fix AdminUsers to username
- Chat: add 'How to practice' guide card; render revealed persona as readable
  label:value grid instead of JSON.
- AdminUsers: switch create form to username (matches username login), add step guide,
  role explanation (user=ฝึก, admin=ดูแลระบบ).
- Analytics (admin overview) + MyBoard (personal): add plain-Thai explainer subtitle.
- Rebuild dist. Verified user create with username -> 201.
2026-08-08 10:34:24 +07:00

🎯 Sales Trainer

A corporate, multi-user sales-training simulator. Admins upload/describe a product; the app analyzes it and generates 15 realistic customer personas (5 per buying-intent tier) with real, varied pains. Trainees chat one-on-one with each persona to practice closing a sale — customers negotiate, stall, and refuse unless their pain is genuinely resolved. Debrief reveals + coaches.

Built on patterns from the CrowdSight / MiroFish swarm engine and clean-room Hermes Brain & Tools plugin.


Features

  • Login + roles (no self-registration): super_admin / admin / user.
    • Admin builds & edits persona groups (product + 15 personas), hand-edits personas, sees analytics.
    • User (trainee) can't create — only selects a group and practices; sees own results.
  • Input via form (product / segment / description) and/or file upload (.pdf/.md/.txt). Product data is used mainly to extract pains; personas are reusable across similar products.
  • 15 personas (5 × tier A/B/C):
    • A = ready to buy · B = unsure · C = not interested but has pain (hardest).
    • Varied demographics, income, occupation, lifestyle, personality — consistent with product.
    • Pain variety (directly-solvable / partial / unrelated red-herring).
    • Negotiation levers (price, freebies, delivery time, scope, payment).
    • Initiation mode: customer opens OR seller must open the sale (outbound, e.g. insurance).
    • Channel: Facebook / LINE.
    • Special tier-C "wrong_text" persona (appears to buy, loses interest, but still has pain).
  • One-shot rule: a persona is chatted once per user (final); shared across other users.
  • Chat realism: all tiers can lose; everything negotiates; hidden internal signals + latent fields (pain/income/personality/budget) revealed only after the result.
  • Debrief: short summary + coaching (how to answer better on weak-score messages), scored by a separate judge LLM (no speed factor).
  • Training loop: win/lose board, weak-area analysis, and user-generated personas (weak-area "lock" or manual form).
  • Admin analytics: close rate, avg score, hardest personas.
  • EN + TH UI.

Quick start

Local (dev)

# backend (Python 3.11)
cd backend
uv venv --python 3.11 .venv
uv pip install -r requirements.txt --python .venv/bin/python
cp .env.example .env   # edit LLM keys + JWT_SECRET
uv run python run.py   # Flask on :5001

# frontend (separate terminal)
cd frontend
npm install
npm run dev            # Vite on :3000 -> proxies /api to :5001

The first run creates a default super-admin: username admin / password 1234. On first login you'll be forced to set your email + change the password before using the app.

Docker / EasyPanel

cp .env.example .env   # set LLM_API_KEY + a strong JWT_SECRET
docker compose up -d   # single container serving frontend + API on :5001

LLM config

Any OpenAI-compatible endpoint (OpenAI, DeepSeek, or custom base URL):

LLM_PROVIDER=deepseek        # deepseek | openai | custom
LLM_BASE_URL=                # optional override
LLM_MODEL_NAME=deepseek-chat # optional override
LLM_API_KEY=sk-...

Architecture

frontend/   Vue 3 + Vite SPA (login, dashboard, group builder, personas, chat, debrief,
            gen-persona, weak-areas, analytics). Built to dist/ and served by Flask.
backend/    Flask API (JWT auth, roles, groups, analyzer, persona generator, chat simulator,
            judge, trainee loop, analytics). Filesystem JSON persistence (no external DB).
  app/services/  analyzer · persona_generator · simulator (+ judge) · report · trainee · groups · sessions
docs/PLAN.md   full design & decisions record
  • Storage: backend/data/ — JSON files per entity (users, orgs, groups, sessions, my_personas).
  • LLM calls: analyzer (sales kit + pain-fit), persona generator, persona chat, judge.

Tests

Run with the built-in deterministic mock LLM (no external key needed):

cd backend
uv run python scripts/test_m0.py      # auth/roles/no-self-registration
uv run python scripts/test_m1.py      # group create + failure handling + role visibility
uv run python scripts/test_routes.py  # all API routes registered
uv run python scripts/test_e2e.py     # full flow: analyze→personas→chat→debrief→one-shot→board→analytics

Real-model verification requires a live LLM_API_KEY in .env.


Default accounts

Role Username Password
super_admin admin 1234 (forced to set email + change on first login)

Admins create additional users (users/login has no self-registration).

⚠️ Data persistence (IMPORTANT)

All data (users, passwords, groups, sessions) lives in DATA_DIR (default /app/backend/data). This directory MUST be a persistent volume in production. If the container restarts/recreates without a volume, all data is wiped — including the admin password you changed on first login, so you will not be able to log back in with your new password.

  • Docker run: -v ./data:/app/backend/data
  • Docker Compose: already mounts ./data:/app/backend/data (see docker-compose.yml)
  • EasyPanel / other platforms: mount a persistent volume to /app/backend/data (or point DATA_DIR at a mounted path), otherwise user data resets on every deploy.
Description
No description provided
Readme 2.3 MiB
Languages
Python 67%
Vue 24.3%
JavaScript 6%
CSS 2.1%
Dockerfile 0.5%
Other 0.1%