fix(ui): no raw JSON on Training list, pain no longer [object Object], add Chat button on GroupEdit, drop facebook/line from persona

- list_groups returns lightweight summaries (id/title/status/persona_count + product) —
  never the full personas array, so Training no longer shows a long JSON blob.
- PersonaForm toLines extracts .description/.text from pain/objection objects (fixes
  '[object Object]'); save re-wraps pains as {description}.
- GroupEdit persona cards get a 'แชท' (MessageSquare) button so you can start chatting
  right where you land after creating (chat was only on Personas page before).
- channel: default neutral 'social', removed facebook/line validation + removed from
  revealable_view (scenario now sets the tone, not fb/line).
All 9 backend suites pass. Rebuilt dist.
This commit is contained in:
Macky
2026-08-09 11:15:13 +07:00
parent f8719d77e3
commit cb2642a659
29 changed files with 69 additions and 42 deletions

View File

@@ -23,7 +23,7 @@ def ensure_persona_shape(p: dict[str, Any]) -> dict[str, Any]:
"name": p.get("name", ""),
"tier": p.get("tier", p.get("intent_tier", "B")),
"initiation_mode": p.get("initiation_mode", "customer"), # customer | seller
"channel": p.get("channel", "facebook"), # facebook | line
"channel": p.get("channel", "social"), # internal tone hint (scenario overrides)
# revealable
"profession": p.get("profession", ""),
"age_group": p.get("age_group", ""),
@@ -47,13 +47,11 @@ def ensure_persona_shape(p: dict[str, Any]) -> dict[str, Any]:
"tolerance": p.get("tolerance", 3), # misses before this persona walks away (temper)
"notes": p.get("notes", ""),
}
# validate
# validation
if base["tier"] not in DEFAULT_TIERS:
base["tier"] = "B"
if base["initiation_mode"] not in ("customer", "seller"):
base["initiation_mode"] = "customer"
if base["channel"] not in ("facebook", "line"):
base["channel"] = "facebook"
return base
@@ -63,7 +61,6 @@ def revealable_view(p: dict[str, Any]) -> dict[str, Any]:
"id": p.get("id"),
"name": p.get("name"),
"tier": p.get("tier"),
"channel": p.get("channel"),
"initiation_mode": p.get("initiation_mode"),
"profession": p.get("profession"),
"age_group": p.get("age_group"),