diff --git a/backend/app/api/group_routes.py b/backend/app/api/group_routes.py
index 2153456..a167c38 100644
--- a/backend/app/api/group_routes.py
+++ b/backend/app/api/group_routes.py
@@ -139,7 +139,7 @@ def create_group():
"product": product,
"segment": (data.get("segment") or ""),
"description": (data.get("description") or ""),
- "channel": (data.get("channel") or "facebook"),
+ "channel": (data.get("channel") or "social"),
"language": (data.get("language") or "th"),
"files": saved_files,
"file_text": file_text[:60000],
@@ -209,7 +209,7 @@ def analyze_group(gid: str):
segment=inp.get("segment", ""),
description=inp.get("description", ""),
file_text=inp.get("file_text", ""),
- channel=inp.get("channel", "facebook"),
+ channel=inp.get("channel", "social"),
)
except Exception as exc:
s["groups"].update(gid, status="failed", error=str(exc))
@@ -223,7 +223,7 @@ def analyze_group(gid: str):
personas = PersonaGenerator(s["llm"]).generate(
sales_kit=sales_kit,
language=inp.get("language", "th"),
- channel=inp.get("channel", "facebook"),
+ channel=inp.get("channel", "social"),
)
personas = existing + personas
except Exception as exc:
diff --git a/backend/app/api/me_routes.py b/backend/app/api/me_routes.py
index 4021067..2dec955 100644
--- a/backend/app/api/me_routes.py
+++ b/backend/app/api/me_routes.py
@@ -74,7 +74,7 @@ def _personal_group(s, actor) -> dict:
g["id"],
status="ready",
owner_user_id=actor["id"],
- input={"channel": "facebook", "language": "th"},
+ input={"channel": "social", "language": "th"},
sales_kit={"productName": "personal practice", "valueProps": [], "features": []},
)
return s["groups"].get(g["id"])
diff --git a/backend/app/services/own_persona.py b/backend/app/services/own_persona.py
index 7170de1..9d401c3 100644
--- a/backend/app/services/own_persona.py
+++ b/backend/app/services/own_persona.py
@@ -35,7 +35,7 @@ def generate_own_persona(llm: LLMClient, *, mode: str, spec: dict[str, Any]) ->
if not isinstance(persona, dict):
raise ValueError("own-persona generator returned invalid data")
persona.setdefault("tier", "B")
- persona.setdefault("channel", "facebook")
+ persona.setdefault("channel", "social")
persona.setdefault("initiation_mode", "customer")
persona.setdefault("pains", [])
persona.setdefault("negotiation_levers", [])
diff --git a/backend/app/services/persona_generator.py b/backend/app/services/persona_generator.py
index e24ce98..492b172 100644
--- a/backend/app/services/persona_generator.py
+++ b/backend/app/services/persona_generator.py
@@ -8,7 +8,8 @@ from ..llm import LLMClient
from .persona_prompts import PERSONA_SYSTEM
TIERS = ["A", "B", "C"]
-PER_TIER = 5
+PER_TIER = 5 # 5 per tier = 15 total
+TARGET = 15 # total personas the system generates (no "add more" button needed)
class PersonaGenerator:
@@ -20,7 +21,7 @@ class PersonaGenerator:
*,
sales_kit: dict[str, Any],
language: str = "en",
- channel: str = "facebook",
+ channel: str = "social",
) -> list[dict[str, Any]]:
kit_json = json.dumps(sales_kit, ensure_ascii=False)[:12000]
lang_name = "Thai" if language == "th" else "English"
@@ -46,13 +47,15 @@ class PersonaGenerator:
PERSONA_SYSTEM, prompt, temperature=0.8, max_tokens=14000
)
personas = result.get("personas") or []
- if (isinstance(personas, list) and len(personas) >= 15) or attempt >= 3:
+ if (isinstance(personas, list) and len(personas) >= TARGET) or attempt >= 3:
break
if not isinstance(personas, list) or not personas:
raise ValueError("persona generator returned no personas")
normalized, counts = [], {"A": 0, "B": 0, "C": 0}
for idx, p in enumerate(personas, start=1):
+ if len(normalized) >= TARGET:
+ break # already reached 20 total
if not isinstance(p, dict):
continue
tier = p.get("tier", p.get("intent_tier"))
@@ -86,7 +89,6 @@ class PersonaGenerator:
if len(normalized) < 8:
raise ValueError(f"expected ~15 personas, generated only {len(normalized)}")
- # NOTE: if we're short of 15 (real LLMs occasionally return 14/13), we ACCEPT what we
- # got rather than crashing the whole analyze — the caller/UI can top up with
- # "create more personas". A retry loop lives in generate().
+ # NOTE: if we're short of 15 (real LLMs occasionally return 14), we ACCEPT what we
+ # got rather than crashing the whole analyze — with TARGET=15 there's normally no gap.
return normalized
diff --git a/backend/app/services/simulator.py b/backend/app/services/simulator.py
index 4046609..ed2ad5f 100644
--- a/backend/app/services/simulator.py
+++ b/backend/app/services/simulator.py
@@ -102,21 +102,16 @@ class Simulator:
"social": "Chat style: short, casual, quick social-messaging replies.",
"f2f_call": "Style: natural, conversational like a live face-to-face or phone talk.",
}.get(scenario, "")
- # Personality trait: a "recontact" customer asked about this before and is only now
- # coming back, already warmer / more ready to buy. Reference it in chat naturally.
- if persona.get("recontact"):
- adapt += (
- "\nYou contacted/interacted with this seller BEFORE (earlier contact) and are now "
- "coming back — you already know the product basics, so you're warmer and more ready "
- "to decide. Mention this naturally (e.g. 'I asked about this a while back')."
- )
+ # NOTE: 'recontact' is a MID-CHAT behavior, not baked into the opening — the trainee
+ # chats with this customer normally first. At the right turn (see chat_routes) a
+ # time-lapse system note is inserted and only THEN does the customer re-engage warmer.
tolerance = int(persona.get("tolerance", 3) or 3)
system = CHAT_SYSTEM.format(
name=persona.get("name", "Customer"),
tone=persona.get("communication_style", "natural, casual"),
profession=persona.get("profession", "customer"),
age_group=persona.get("age_group", "adult"),
- channel=persona.get("channel", "facebook") + (f" ({scenario})" if scenario else ""),
+ channel=persona.get("channel", "social") + (f" ({scenario})" if scenario else ""),
background=persona.get("background", ""),
personality=persona.get("personality", ""),
lifestyle=persona.get("lifestyle", ""),
diff --git a/frontend/dist/assets/AdminUsers-RvxZ4Zuo.js b/frontend/dist/assets/AdminUsers-CnHWEscS.js
similarity index 98%
rename from frontend/dist/assets/AdminUsers-RvxZ4Zuo.js
rename to frontend/dist/assets/AdminUsers-CnHWEscS.js
index 9e868cf..7f00272 100644
--- a/frontend/dist/assets/AdminUsers-RvxZ4Zuo.js
+++ b/frontend/dist/assets/AdminUsers-CnHWEscS.js
@@ -1,4 +1,4 @@
-import{c as k,_ as h,p as b,a as r,b as e,l as m,u as o,m as l,t as n,i as u,w as p,v,H as U,g as f,F as V,x as C,y as w,r as y,o as i,C as M}from"./index-rfeHzF-F.js";import{U as z}from"./users-BgT8ke9W.js";/**
+import{c as k,_ as h,p as b,a as r,b as e,l as m,u as o,m as l,t as n,i as u,w as p,v,H as U,g as f,F as V,x as C,y as w,r as y,o as i,C as M}from"./index-yErsUbD6.js";import{U as z}from"./users-BK4yWB6N.js";/**
* @license lucide-vue-next v1.0.0 - ISC
*
* This source code is licensed under the ISC license.
diff --git a/frontend/dist/assets/Analytics-DLu98n1n.js b/frontend/dist/assets/Analytics-Cdjv87WO.js
similarity index 97%
rename from frontend/dist/assets/Analytics-DLu98n1n.js
rename to frontend/dist/assets/Analytics-Cdjv87WO.js
index 128c3f1..fdcd00c 100644
--- a/frontend/dist/assets/Analytics-DLu98n1n.js
+++ b/frontend/dist/assets/Analytics-Cdjv87WO.js
@@ -1,4 +1,4 @@
-import{c as x,_ as L,p as M,a as p,b as s,l as i,u as a,m as c,t as e,i as n,q as w,w as f,v as b,s as U,g as N,F as R,x as j,y as A,z as B,r as m,h as E,o as y}from"./index-rfeHzF-F.js";import{U as P}from"./users-BgT8ke9W.js";import{P as S}from"./plus-GGHS2V4y.js";/**
+import{c as x,_ as L,p as M,a as p,b as s,l as i,u as a,m as c,t as e,i as n,q as w,w as f,v as b,s as U,g as N,F as R,x as j,y as A,z as B,r as m,h as E,o as y}from"./index-yErsUbD6.js";import{U as P}from"./users-BK4yWB6N.js";import{P as S}from"./plus-Y1qXSgej.js";/**
* @license lucide-vue-next v1.0.0 - ISC
*
* This source code is licensed under the ISC license.
diff --git a/frontend/dist/assets/Chat-CeTQMGNO.js b/frontend/dist/assets/Chat-Dr4nNqcU.js
similarity index 97%
rename from frontend/dist/assets/Chat-CeTQMGNO.js
rename to frontend/dist/assets/Chat-Dr4nNqcU.js
index 365d14a..48525b4 100644
--- a/frontend/dist/assets/Chat-CeTQMGNO.js
+++ b/frontend/dist/assets/Chat-Dr4nNqcU.js
@@ -1,4 +1,4 @@
-import{c as P,_ as G,p as K,y as S,a as l,l as k,q as D,u as a,b as s,t,i as n,F as j,x as T,m as g,g as m,C as L,w as R,v as $,d as q,k as H,r as u,B as J,E as O,z as U,o}from"./index-rfeHzF-F.js";import{T as Y}from"./target-CLzEfHJ7.js";import{A as Q}from"./arrow-left-De8eM538.js";/**
+import{c as P,_ as G,p as K,y as S,a as l,l as k,q as D,u as a,b as s,t,i as n,F as j,x as T,m as g,g as m,C as L,w as R,v as $,d as q,k as H,r as u,B as J,E as O,z as U,o}from"./index-yErsUbD6.js";import{T as Y}from"./target-DmESUw9c.js";import{A as Q}from"./arrow-left-hWxF5h5m.js";/**
* @license lucide-vue-next v1.0.0 - ISC
*
* This source code is licensed under the ISC license.
diff --git a/frontend/dist/assets/GroupBuilder-DuRDlcKB.js b/frontend/dist/assets/GroupBuilder-Odxme5l4.js
similarity index 98%
rename from frontend/dist/assets/GroupBuilder-DuRDlcKB.js
rename to frontend/dist/assets/GroupBuilder-Odxme5l4.js
index 752fbcc..5b73961 100644
--- a/frontend/dist/assets/GroupBuilder-DuRDlcKB.js
+++ b/frontend/dist/assets/GroupBuilder-Odxme5l4.js
@@ -1,4 +1,4 @@
-import{c as v,_ as M,a as k,l as u,q as z,b as e,u as l,m as o,t as r,i,w as g,v as y,H as V,g as C,z as A,r as m,y as x,j as B,o as w}from"./index-rfeHzF-F.js";import{A as q}from"./arrow-left-De8eM538.js";/**
+import{c as v,_ as M,a as k,l as u,q as z,b as e,u as l,m as o,t as r,i,w as g,v as y,H as V,g as C,z as A,r as m,y as x,j as B,o as w}from"./index-yErsUbD6.js";import{A as q}from"./arrow-left-hWxF5h5m.js";/**
* @license lucide-vue-next v1.0.0 - ISC
*
* This source code is licensed under the ISC license.
diff --git a/frontend/dist/assets/GroupEdit-Bzhg1xqU.js b/frontend/dist/assets/GroupEdit-Bzhg1xqU.js
new file mode 100644
index 0000000..367fb40
--- /dev/null
+++ b/frontend/dist/assets/GroupEdit-Bzhg1xqU.js
@@ -0,0 +1,14 @@
+import{c as q,_ as E,I as F,o as u,a as d,b as t,t as a,w as o,v as i,H as G,u as f,h as M,g as A,J as I,r as g,p as D,l as k,q as T,m as C,i as B,G as J,F as j,x as z,D as H,k as R,y as N,z as K,C as O}from"./index-yErsUbD6.js";import{U as Q}from"./users-BK4yWB6N.js";/**
+ * @license lucide-vue-next v1.0.0 - ISC
+ *
+ * This source code is licensed under the ISC license.
+ * See the LICENSE file in the root directory of this source tree.
+ */const W=q("message-square",[["path",{d:"M22 17a2 2 0 0 1-2 2H6.828a2 2 0 0 0-1.414.586l-2.202 2.202A.71.71 0 0 1 2 21.286V5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2z",key:"18887p"}]]);/**
+ * @license lucide-vue-next v1.0.0 - ISC
+ *
+ * This source code is licensed under the ISC license.
+ * See the LICENSE file in the root directory of this source tree.
+ */const X=q("pencil",[["path",{d:"M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z",key:"1a8usu"}],["path",{d:"m15 5 4 4",key:"1mk7zo"}]]),Y={class:"persona-form"},Z={class:"muted"},h={class:"sec"},ee={class:"row"},te={class:"f"},le={class:"f"},ne={class:"f"},se={class:"row"},oe={class:"f"},ie={class:"f"},ae={class:"f"},re={class:"sec"},ue={class:"row"},de={class:"f"},me={class:"f"},ve={class:"f"},fe={key:0,class:"sec"},ge={key:1,class:"sec locked"},ye={key:2,class:"sec"},be={class:"muted"},ce={class:"row actions"},ke=["disabled"],pe={__name:"PersonaForm",props:{persona:{type:Object,required:!0}},emits:["save","cancel"],setup(p,{emit:P}){const c=p,V=P,s=I({name:"",difficulty:1,tier:"B",profession:"",age_group:"",location:"",income:"",budget:"",lifestyle:"",background:"",personality:"",communication_style:"",goal:"",decision_timeline:"",opener:"",channel:"line",initiation_mode:"customer",special:""}),y=g(""),b=g(""),m=g(""),x=g(!1);function w(l){return l?Array.isArray(l)?l.map(e=>typeof e=="string"?e:e&&e.description||e&&e.text||"").filter(Boolean).join(`
+`):String(l):""}function U(l){return l.split(`
+`).map(e=>e.trim()).filter(Boolean)}F(()=>c.persona,l=>{Object.assign(s,{name:(l==null?void 0:l.name)||"",difficulty:(l==null?void 0:l.difficulty)??1,tier:(l==null?void 0:l.tier)||"B",profession:(l==null?void 0:l.profession)||"",age_group:(l==null?void 0:l.age_group)||"",location:(l==null?void 0:l.location)||"",income:(l==null?void 0:l.income)||"",budget:(l==null?void 0:l.budget)||"",lifestyle:(l==null?void 0:l.lifestyle)||"",background:(l==null?void 0:l.background)||"",personality:(l==null?void 0:l.personality)||"",communication_style:(l==null?void 0:l.communication_style)||"",goal:(l==null?void 0:l.goal)||"",decision_timeline:(l==null?void 0:l.decision_timeline)||"",opener:(l==null?void 0:l.opener)||"",channel:(l==null?void 0:l.channel)||"line",initiation_mode:(l==null?void 0:l.initiation_mode)||"customer",special:(l==null?void 0:l.special)||""}),y.value=w(l==null?void 0:l.pains),b.value=w(l==null?void 0:l.objections),m.value=w(l==null?void 0:l.negotiation_levers)},{immediate:!0});function S(){V("save",{...s,pains:U(y.value).map(l=>({description:l})),objections:U(b.value),negotiation_levers:U(m.value)})}return(l,e)=>(u(),d("div",Y,[t("h3",null,a(p.persona.name||"New persona"),1),t("p",Z,a(p.persona.profession)+" · "+a(p.persona.personality),1),t("div",h,[e[28]||(e[28]=t("h4",null,"👤 ข้อมูลพื้นฐาน",-1)),t("div",ee,[t("div",te,[e[21]||(e[21]=t("label",null,"ชื่อ",-1)),o(t("input",{"onUpdate:modelValue":e[0]||(e[0]=n=>s.name=n)},null,512),[[i,s.name]])]),t("div",le,[e[22]||(e[22]=t("label",null,"ระดับความยาก (1-5)",-1)),o(t("input",{"onUpdate:modelValue":e[1]||(e[1]=n=>s.difficulty=n),type:"number",min:"1",max:"5"},null,512),[[i,s.difficulty,void 0,{number:!0}]])]),t("div",ne,[e[24]||(e[24]=t("label",null,"ระดับลูกค้า",-1)),o(t("select",{"onUpdate:modelValue":e[2]||(e[2]=n=>s.tier=n)},[...e[23]||(e[23]=[t("option",{value:"A"},"A — พร้อมตัดสินใจซื้อ",-1),t("option",{value:"B"},"B — ยังไม่แน่ใจ",-1),t("option",{value:"C"},"C — ไม่สนใจแต่มีปัญหา",-1)])],512),[[G,s.tier]])])]),t("div",se,[t("div",oe,[e[25]||(e[25]=t("label",null,"อาชีพ",-1)),o(t("input",{"onUpdate:modelValue":e[3]||(e[3]=n=>s.profession=n)},null,512),[[i,s.profession]])]),t("div",ie,[e[26]||(e[26]=t("label",null,"ช่วงอายุ",-1)),o(t("input",{"onUpdate:modelValue":e[4]||(e[4]=n=>s.age_group=n)},null,512),[[i,s.age_group]])]),t("div",ae,[e[27]||(e[27]=t("label",null,"พื้นที่",-1)),o(t("input",{"onUpdate:modelValue":e[5]||(e[5]=n=>s.location=n)},null,512),[[i,s.location]])])])]),t("div",re,[e[32]||(e[32]=t("h4",null,"💼 ข้อมูลลูกค้า",-1)),t("div",ue,[t("div",de,[e[29]||(e[29]=t("label",null,"รายได้/สถานะการเงิน",-1)),o(t("input",{"onUpdate:modelValue":e[6]||(e[6]=n=>s.income=n)},null,512),[[i,s.income]])]),t("div",me,[e[30]||(e[30]=t("label",null,"งบประมาณ",-1)),o(t("input",{"onUpdate:modelValue":e[7]||(e[7]=n=>s.budget=n)},null,512),[[i,s.budget]])]),t("div",ve,[e[31]||(e[31]=t("label",null,"ไลฟ์สไตล์",-1)),o(t("input",{"onUpdate:modelValue":e[8]||(e[8]=n=>s.lifestyle=n)},null,512),[[i,s.lifestyle]])])]),e[33]||(e[33]=t("label",null,"ภูมิหลัง",-1)),o(t("textarea",{"onUpdate:modelValue":e[9]||(e[9]=n=>s.background=n)},null,512),[[i,s.background]]),e[34]||(e[34]=t("label",null,"บุคลิก / วิธีพูดคุย",-1)),o(t("textarea",{"onUpdate:modelValue":e[10]||(e[10]=n=>s.personality=n)},null,512),[[i,s.personality]]),e[35]||(e[35]=t("label",null,"สไตล์การสื่อสาร",-1)),o(t("textarea",{"onUpdate:modelValue":e[11]||(e[11]=n=>s.communication_style=n)},null,512),[[i,s.communication_style]])]),f(M).isSuperAdmin?(u(),d("div",fe,[e[36]||(e[36]=t("h4",null,"🎯 การขาย",-1)),e[37]||(e[37]=t("label",null,"เป้าหมาย",-1)),o(t("textarea",{"onUpdate:modelValue":e[12]||(e[12]=n=>s.goal=n)},null,512),[[i,s.goal]]),e[38]||(e[38]=t("label",null,"กรอบเวลาในการตัดสินใจ",-1)),o(t("input",{"onUpdate:modelValue":e[13]||(e[13]=n=>s.decision_timeline=n)},null,512),[[i,s.decision_timeline]]),e[39]||(e[39]=t("label",null,"Pain (ปัญหา) แบบ 1 ต่อบรรทัด",-1)),o(t("textarea",{"onUpdate:modelValue":e[14]||(e[14]=n=>y.value=n),placeholder:`เช่น ต้นทุนสูงเกินไป
+ระบบล้าสมัย`},null,512),[[i,y.value]]),e[40]||(e[40]=t("label",null,"ข้อโต้แย้ง (Objections) 1 ต่อบรรทัด",-1)),o(t("textarea",{"onUpdate:modelValue":e[15]||(e[15]=n=>b.value=n)},null,512),[[i,b.value]]),e[41]||(e[41]=t("label",null,"สิ่งที่ใช้ต่อรอง (เช่น ส่วนลด, ฟรีติดตั้ง)",-1)),o(t("textarea",{"onUpdate:modelValue":e[16]||(e[16]=n=>m.value=n)},null,512),[[i,m.value]]),e[42]||(e[42]=t("label",null,"ช่องทางสำหรับลูกค้าเปิดบทสนทนา (opener)",-1)),o(t("textarea",{"onUpdate:modelValue":e[17]||(e[17]=n=>s.opener=n)},null,512),[[i,s.opener]])])):(u(),d("div",ge,[e[43]||(e[43]=t("h4",null,"🔒 ส่วนสูตรการขาย",-1)),e[44]||(e[44]=t("p",{class:"muted"},"ปิดการแก้ไข — เฉพาะผู้ดูแลระดับสูงเท่านั้นที่ดู/แก้ได้ (เพื่อรักษาความได้เปรียบ)",-1)),e[45]||(e[45]=t("label",null,"เป้าหมาย",-1)),o(t("textarea",{"onUpdate:modelValue":e[18]||(e[18]=n=>s.goal=n)},null,512),[[i,s.goal]]),e[46]||(e[46]=t("label",null,"กรอบเวลาในการตัดสินใจ",-1)),o(t("input",{"onUpdate:modelValue":e[19]||(e[19]=n=>s.decision_timeline=n)},null,512),[[i,s.decision_timeline]])])),f(M).isSuperAdmin&&s.special?(u(),d("div",ye,[t("label",be,"พิเศษ: "+a(s.special),1)])):A("",!0),t("div",ce,[t("button",{class:"primary",onClick:S,disabled:x.value},a(x.value?"กำลังบันทึก…":"💾 บันทึกบุคคลต้นแบบ"),9,ke),t("button",{onClick:e[20]||(e[20]=n=>l.$emit("cancel"))},"ปิด")])]))}},Ve=E(pe,[["__scopeId","data-v-59429170"]]),xe={class:"row",style:{"align-items":"center","margin-bottom":"8px"}},we={style:{margin:"0"}},Ue={key:0,class:"muted"},$e={key:1,class:"error",style:{margin:"12px 0"}},Ce={key:2,class:"card empty-state"},Be={class:"grid"},Ae={class:"row",style:{"justify-content":"space-between","align-items":"center"}},Se={class:"diff"},_e={class:"muted"},je={class:"muted"},ze={class:"row",style:{gap:"8px","margin-top":"auto"}},Ne={class:"primary",style:{width:"100%"}},Pe=["onClick"],Le={class:"modal"},Me={__name:"GroupEdit",setup(p){const c=R().params.gid,V=g(null),s=g([]),y=g(!1),b=g(""),m=g(null);async function x(){const n=await N.getGroup(c);V.value=n.group,s.value=(await N.listPersonas(c)).personas}function w(n){return s.value.filter(r=>r.tier===n)}function U(n){return B.t(n==="A"?"tierA":n==="B"?"tierB":"tierC")}function S(n){return{A:"tier-a",B:"tier-b",C:"tier-c"}[n]||""}function l(n){m.value=JSON.parse(JSON.stringify(n))}async function e(n){y.value=!0;try{await N.updatePersona(c,m.value.id,n),m.value=null,await x()}catch(r){b.value=r.message}finally{y.value=!1}}return D(x),(n,r)=>{const L=K("router-link");return u(),d("div",null,[k(L,{to:"/training",class:"btn-back"},{default:T(()=>[C("← "+a(f(B).t("training")),1)]),_:1}),t("div",xe,[t("h2",we,[k(f(Q),{size:22,"stroke-width":1.8,style:{"vertical-align":"-4px"}}),C(" "+a(f(B).t("managePersonas")),1)])]),V.value?(u(),d("p",Ue,[r[2]||(r[2]=C("สินค้า: ",-1)),t("strong",null,a(V.value.title),1)])):A("",!0),b.value?(u(),d("div",$e,a(b.value),1)):A("",!0),r[4]||(r[4]=J('
',1))])]))}};export{b as default};
diff --git a/frontend/dist/assets/Login-CsAwuAd_.js b/frontend/dist/assets/Login-Dk-IRHf2.js
similarity index 98%
rename from frontend/dist/assets/Login-CsAwuAd_.js
rename to frontend/dist/assets/Login-Dk-IRHf2.js
index 06e3249..eb23440 100644
--- a/frontend/dist/assets/Login-CsAwuAd_.js
+++ b/frontend/dist/assets/Login-Dk-IRHf2.js
@@ -1,4 +1,4 @@
-import{c as k,_ as x,a as y,b as e,t as l,u as a,i as n,w as h,v as M,d as w,e as B,f as _,g as E,r,h as f,j as S,k as V,o as s}from"./index-rfeHzF-F.js";/**
+import{c as k,_ as x,a as y,b as e,t as l,u as a,i as n,w as h,v as M,d as w,e as B,f as _,g as E,r,h as f,j as S,k as V,o as s}from"./index-yErsUbD6.js";/**
* @license lucide-vue-next v1.0.0 - ISC
*
* This source code is licensed under the ISC license.
diff --git a/frontend/dist/assets/MyBoard-xB9d_Obs.js b/frontend/dist/assets/MyBoard-DTBModgt.js
similarity index 94%
rename from frontend/dist/assets/MyBoard-xB9d_Obs.js
rename to frontend/dist/assets/MyBoard-DTBModgt.js
index 0f0a361..93bdaef 100644
--- a/frontend/dist/assets/MyBoard-xB9d_Obs.js
+++ b/frontend/dist/assets/MyBoard-DTBModgt.js
@@ -1 +1 @@
-import{_ as f,p as x,y as v,a as r,b as t,l as b,u as e,m as k,t as s,i as a,g as _,w as B,A as N,F as D,x as S,r as m,B as c,o as i,C as T}from"./index-rfeHzF-F.js";import{L as V}from"./layout-dashboard-DNjo1OkM.js";const C={style:{margin:"0"}},L={key:0,class:"row stat-row"},M={class:"card stat"},z={class:"card stat won"},F={class:"card stat lost"},j={class:"card stat"},A={key:1,class:"card"},E={key:2,class:"card empty-state"},G={class:"card",style:{"margin-top":"20px"}},I={style:{"margin-top":"0"}},q={__name:"MyBoard",setup(H){const o=m([]),u=m([]),p=m(!0),y=c(()=>o.value.length),g=c(()=>o.value.filter(n=>n.my_outcome==="won").length),h=c(()=>o.value.filter(n=>n.my_outcome==="lost").length),w=c(()=>o.value.filter(n=>n.my_outcome==="not_tried").length);return x(async()=>{try{o.value=(await v.myBoard()).board||[],u.value=(await v.mySessions()).sessions||[]}finally{p.value=!1}}),(n,l)=>(i(),r("div",null,[t("div",null,[t("h2",C,[b(e(V),{size:22,"stroke-width":1.8,style:{"vertical-align":"-4px"}}),k(" "+s(e(a).t("myDashboard")),1)]),l[0]||(l[0]=t("div",{class:"muted",style:{"margin-top":"4px","margin-bottom":"16px"}},"สรุปผลการฝึกของตัวคุณเอง — ดูว่าปิดการขายได้กี่ครั้ง ยังฝึกกับใครบ้าง",-1))]),p.value?_("",!0):(i(),r("div",L,[t("div",M,[t("span",null,s(e(a).t("total")),1),t("strong",null,s(y.value),1)]),t("div",z,[t("span",null,s(e(a).t("won")),1),t("strong",null,s(g.value),1)]),t("div",F,[t("span",null,s(e(a).t("lost")),1),t("strong",null,s(h.value),1)]),t("div",j,[t("span",null,s(e(a).t("notTried")),1),t("strong",null,s(w.value),1)])])),p.value?(i(),r("div",A,[...l[1]||(l[1]=[t("div",{class:"skeleton",style:{height:"50px"}},null,-1)])])):o.value.length===0?(i(),r("div",E,[...l[2]||(l[2]=[t("strong",null,"No practice yet",-1),t("span",null,"Go to Training and try closing a sale with a persona.",-1)])])):_("",!0),B(t("div",G,[t("h3",I,s(e(a).t("mySessions")),1),(i(!0),r(D,null,S(u.value,d=>(i(),r("div",{key:d.id,style:{display:"flex","justify-content":"space-between",padding:"8px 0","border-bottom":"1px solid var(--border)"}},[t("span",null,s(d.persona_name),1),t("span",{class:T(["badge",d.outcome])},s(d.outcome==="won"?e(a).t("won"):e(a).t("lost")),3)]))),128))],512),[[N,u.value.length]])]))}},O=f(q,[["__scopeId","data-v-6767b6b8"]]);export{O as default};
+import{_ as f,p as x,y as v,a as r,b as t,l as b,u as e,m as k,t as s,i as a,g as _,w as B,A as N,F as D,x as S,r as m,B as c,o as i,C as T}from"./index-yErsUbD6.js";import{L as V}from"./layout-dashboard-DplDGQ9R.js";const C={style:{margin:"0"}},L={key:0,class:"row stat-row"},M={class:"card stat"},z={class:"card stat won"},F={class:"card stat lost"},j={class:"card stat"},A={key:1,class:"card"},E={key:2,class:"card empty-state"},G={class:"card",style:{"margin-top":"20px"}},I={style:{"margin-top":"0"}},q={__name:"MyBoard",setup(H){const o=m([]),u=m([]),p=m(!0),y=c(()=>o.value.length),g=c(()=>o.value.filter(n=>n.my_outcome==="won").length),h=c(()=>o.value.filter(n=>n.my_outcome==="lost").length),w=c(()=>o.value.filter(n=>n.my_outcome==="not_tried").length);return x(async()=>{try{o.value=(await v.myBoard()).board||[],u.value=(await v.mySessions()).sessions||[]}finally{p.value=!1}}),(n,l)=>(i(),r("div",null,[t("div",null,[t("h2",C,[b(e(V),{size:22,"stroke-width":1.8,style:{"vertical-align":"-4px"}}),k(" "+s(e(a).t("myDashboard")),1)]),l[0]||(l[0]=t("div",{class:"muted",style:{"margin-top":"4px","margin-bottom":"16px"}},"สรุปผลการฝึกของตัวคุณเอง — ดูว่าปิดการขายได้กี่ครั้ง ยังฝึกกับใครบ้าง",-1))]),p.value?_("",!0):(i(),r("div",L,[t("div",M,[t("span",null,s(e(a).t("total")),1),t("strong",null,s(y.value),1)]),t("div",z,[t("span",null,s(e(a).t("won")),1),t("strong",null,s(g.value),1)]),t("div",F,[t("span",null,s(e(a).t("lost")),1),t("strong",null,s(h.value),1)]),t("div",j,[t("span",null,s(e(a).t("notTried")),1),t("strong",null,s(w.value),1)])])),p.value?(i(),r("div",A,[...l[1]||(l[1]=[t("div",{class:"skeleton",style:{height:"50px"}},null,-1)])])):o.value.length===0?(i(),r("div",E,[...l[2]||(l[2]=[t("strong",null,"No practice yet",-1),t("span",null,"Go to Training and try closing a sale with a persona.",-1)])])):_("",!0),B(t("div",G,[t("h3",I,s(e(a).t("mySessions")),1),(i(!0),r(D,null,S(u.value,d=>(i(),r("div",{key:d.id,style:{display:"flex","justify-content":"space-between",padding:"8px 0","border-bottom":"1px solid var(--border)"}},[t("span",null,s(d.persona_name),1),t("span",{class:T(["badge",d.outcome])},s(d.outcome==="won"?e(a).t("won"):e(a).t("lost")),3)]))),128))],512),[[N,u.value.length]])]))}},O=f(q,[["__scopeId","data-v-6767b6b8"]]);export{O as default};
diff --git a/frontend/dist/assets/Personas-CT2K1C7x.js b/frontend/dist/assets/Personas-CjtqghLF.js
similarity index 95%
rename from frontend/dist/assets/Personas-CT2K1C7x.js
rename to frontend/dist/assets/Personas-CjtqghLF.js
index 6a919f9..b9d6f92 100644
--- a/frontend/dist/assets/Personas-CT2K1C7x.js
+++ b/frontend/dist/assets/Personas-CjtqghLF.js
@@ -1 +1 @@
-import{_ as T,p as $,a as l,l as u,q as _,b as t,t as s,u as e,i as n,h as g,m as d,g as x,F as y,x as p,k as L,y as N,z as V,r as w,o as a,S,C as b,f as A}from"./index-rfeHzF-F.js";import{T as F}from"./target-CLzEfHJ7.js";import{A as j}from"./arrow-left-De8eM538.js";const q={class:"row",style:{"align-items":"center"}},D={style:{margin:"0"}},E={class:"muted",style:{"margin-left":"auto"}},I={key:0,class:"card guide"},M={key:1,class:"row",style:{margin:"12px 0",gap:"10px"}},R={class:"primary"},G={class:"grid"},H={class:"row",style:{"justify-content":"space-between"}},J={class:"diff"},K={class:"muted"},O={class:"muted"},Q={class:"muted"},U={class:"muted",style:{"margin-top":"6px"}},W={class:"primary",style:{width:"100%"}},X={class:"primary",style:{width:"100%"}},Y={key:1,class:"muted",style:{"margin-top":"auto","font-size":"12px"}},Z={__name:"Personas",setup(tt){const c=L().params.gid,h=w([]),C=w(!0);async function B(){try{h.value=(await N.listPersonas(c)).personas}finally{C.value=!1}}function z(r){return h.value.filter(i=>i.tier===r)}function P(r){return n.t(r==="A"?"tierA":r==="B"?"tierB":"tierC")}function k(r){return r==="won"?n.t("won"):r==="lost"?n.t("lost"):n.t("notTried")}return $(B),(r,i)=>{const m=V("router-link");return a(),l("div",null,[u(m,{to:"/training",class:"btn-back"},{default:_(()=>[u(e(j),{size:16,"stroke-width":2}),d(" "+s(e(n).t("training")),1)]),_:1}),t("div",q,[t("h2",D,s(e(n).t("personas")),1),t("span",E,s(e(n).t("selectPersona")),1)]),e(g).isAdmin?x("",!0):(a(),l("div",I,[t("strong",null,[u(e(F),{size:17,"stroke-width":1.8,style:{"vertical-align":"-3px"}}),i[0]||(i[0]=d(" วิธีฝึก",-1))]),i[1]||(i[1]=t("ol",{style:{margin:"8px 0 0","padding-left":"20px","line-height":"1.8"}},[t("li",null,"เลือกลูกค้าจำลอง (บุคคลต้นแบบ) คนหนึ่งที่อยากฝึกด้วย"),t("li",null,"ระดับ A ง่ายสุด → ระดับ C ยากสุด (ดูจากดาว ★ ความยาก)"),t("li",null,[d("กด "),t("strong",null,"แชท"),d(" → เลือกสถานการณ์ (โซเชียล / พบหน้า-โทร) → เริ่มคุยกับลูกค้า")]),t("li",null,"ลูกค้าจะตัดสินใจเองว่าซื้อหรือไม่ซื้อ (ฝึกได้คนละครั้งเท่านั้น)")],-1))])),e(g).isAdmin?(a(),l("div",M,[u(m,{to:`/admin/groups/${e(c)}/edit`},{default:_(()=>[t("button",R,[u(e(S),{size:18,"stroke-width":1.8}),d(" "+s(e(n).t("managePersonas")),1)])]),_:1},8,["to"]),i[2]||(i[2]=t("span",{class:"muted"},"Admin: จัดการรายละเอียดบุคคลต้นแบบได้ที่นี่",-1))])):x("",!0),(a(),l(y,null,p(["A","B","C"],f=>t("div",{key:f,style:{margin:"20px 0"}},[t("h4",null,s(P(f)),1),t("div",G,[(a(!0),l(y,null,p(z(f),o=>(a(),l("div",{key:o.id,class:"card pcard lift"},[t("div",H,[t("strong",null,s(o.name),1),t("span",{class:b(["badge",o.my_outcome])},s(k(o.my_outcome)),3)]),t("div",J,[(a(),l(y,null,p(5,v=>t("span",{key:v,class:b(["star",{on:v<=(o.difficulty||1)}])},"★",2)),64)),t("span",K,s(e(n).t("difficulty"))+" "+s(o.difficulty||1)+"/5",1)]),t("div",O,[d(s(o.profession)+" · "+s(o.age_group)+" · "+s(o.location),1),i[3]||(i[3]=t("br",null,null,-1)),t("span",Q,s(e(n).t("difficulty"))+" "+s(o.difficulty||1)+"/5",1)]),t("div",U,s(o.product_context),1),e(g).isAdmin?(a(),A(m,{key:0,to:`/groups/${e(c)}/chat/${o.id}`,style:{"margin-top":"auto"}},{default:_(()=>[t("button",W,s(e(n).t("chat")),1)]),_:1},8,["to"])):(a(),l(y,{key:1},[o.my_outcome==="not_tried"?(a(),A(m,{key:0,to:`/groups/${e(c)}/chat/${o.id}`,style:{"margin-top":"auto"}},{default:_(()=>[t("button",X,s(e(n).t("chat")),1)]),_:1},8,["to"])):(a(),l("div",Y,"✓ "+s(e(n).t("trained"))+" ("+s(k(o.my_outcome))+")",1))],64))]))),128))])])),64))])}}},at=T(Z,[["__scopeId","data-v-ec851b01"]]);export{at as default};
+import{_ as T,p as $,a as l,l as u,q as _,b as t,t as s,u as e,i as n,h as g,m as d,g as x,F as y,x as p,k as L,y as N,z as V,r as w,o as a,S,C as b,f as A}from"./index-yErsUbD6.js";import{T as F}from"./target-DmESUw9c.js";import{A as j}from"./arrow-left-hWxF5h5m.js";const q={class:"row",style:{"align-items":"center"}},D={style:{margin:"0"}},E={class:"muted",style:{"margin-left":"auto"}},I={key:0,class:"card guide"},M={key:1,class:"row",style:{margin:"12px 0",gap:"10px"}},R={class:"primary"},G={class:"grid"},H={class:"row",style:{"justify-content":"space-between"}},J={class:"diff"},K={class:"muted"},O={class:"muted"},Q={class:"muted"},U={class:"muted",style:{"margin-top":"6px"}},W={class:"primary",style:{width:"100%"}},X={class:"primary",style:{width:"100%"}},Y={key:1,class:"muted",style:{"margin-top":"auto","font-size":"12px"}},Z={__name:"Personas",setup(tt){const c=L().params.gid,h=w([]),C=w(!0);async function B(){try{h.value=(await N.listPersonas(c)).personas}finally{C.value=!1}}function z(r){return h.value.filter(i=>i.tier===r)}function P(r){return n.t(r==="A"?"tierA":r==="B"?"tierB":"tierC")}function k(r){return r==="won"?n.t("won"):r==="lost"?n.t("lost"):n.t("notTried")}return $(B),(r,i)=>{const m=V("router-link");return a(),l("div",null,[u(m,{to:"/training",class:"btn-back"},{default:_(()=>[u(e(j),{size:16,"stroke-width":2}),d(" "+s(e(n).t("training")),1)]),_:1}),t("div",q,[t("h2",D,s(e(n).t("personas")),1),t("span",E,s(e(n).t("selectPersona")),1)]),e(g).isAdmin?x("",!0):(a(),l("div",I,[t("strong",null,[u(e(F),{size:17,"stroke-width":1.8,style:{"vertical-align":"-3px"}}),i[0]||(i[0]=d(" วิธีฝึก",-1))]),i[1]||(i[1]=t("ol",{style:{margin:"8px 0 0","padding-left":"20px","line-height":"1.8"}},[t("li",null,"เลือกลูกค้าจำลอง (บุคคลต้นแบบ) คนหนึ่งที่อยากฝึกด้วย"),t("li",null,"ระดับ A ง่ายสุด → ระดับ C ยากสุด (ดูจากดาว ★ ความยาก)"),t("li",null,[d("กด "),t("strong",null,"แชท"),d(" → เลือกสถานการณ์ (โซเชียล / พบหน้า-โทร) → เริ่มคุยกับลูกค้า")]),t("li",null,"ลูกค้าจะตัดสินใจเองว่าซื้อหรือไม่ซื้อ (ฝึกได้คนละครั้งเท่านั้น)")],-1))])),e(g).isAdmin?(a(),l("div",M,[u(m,{to:`/admin/groups/${e(c)}/edit`},{default:_(()=>[t("button",R,[u(e(S),{size:18,"stroke-width":1.8}),d(" "+s(e(n).t("managePersonas")),1)])]),_:1},8,["to"]),i[2]||(i[2]=t("span",{class:"muted"},"Admin: จัดการรายละเอียดบุคคลต้นแบบได้ที่นี่",-1))])):x("",!0),(a(),l(y,null,p(["A","B","C"],f=>t("div",{key:f,style:{margin:"20px 0"}},[t("h4",null,s(P(f)),1),t("div",G,[(a(!0),l(y,null,p(z(f),o=>(a(),l("div",{key:o.id,class:"card pcard lift"},[t("div",H,[t("strong",null,s(o.name),1),t("span",{class:b(["badge",o.my_outcome])},s(k(o.my_outcome)),3)]),t("div",J,[(a(),l(y,null,p(5,v=>t("span",{key:v,class:b(["star",{on:v<=(o.difficulty||1)}])},"★",2)),64)),t("span",K,s(e(n).t("difficulty"))+" "+s(o.difficulty||1)+"/5",1)]),t("div",O,[d(s(o.profession)+" · "+s(o.age_group)+" · "+s(o.location),1),i[3]||(i[3]=t("br",null,null,-1)),t("span",Q,s(e(n).t("difficulty"))+" "+s(o.difficulty||1)+"/5",1)]),t("div",U,s(o.product_context),1),e(g).isAdmin?(a(),A(m,{key:0,to:`/groups/${e(c)}/chat/${o.id}`,style:{"margin-top":"auto"}},{default:_(()=>[t("button",W,s(e(n).t("chat")),1)]),_:1},8,["to"])):(a(),l(y,{key:1},[o.my_outcome==="not_tried"?(a(),A(m,{key:0,to:`/groups/${e(c)}/chat/${o.id}`,style:{"margin-top":"auto"}},{default:_(()=>[t("button",X,s(e(n).t("chat")),1)]),_:1},8,["to"])):(a(),l("div",Y,"✓ "+s(e(n).t("trained"))+" ("+s(k(o.my_outcome))+")",1))],64))]))),128))])])),64))])}}},at=T(Z,[["__scopeId","data-v-ec851b01"]]);export{at as default};
diff --git a/frontend/dist/assets/Settings-DqpxLARx.js b/frontend/dist/assets/Settings-sM2KFjew.js
similarity index 98%
rename from frontend/dist/assets/Settings-DqpxLARx.js
rename to frontend/dist/assets/Settings-sM2KFjew.js
index 3a978a9..d012f03 100644
--- a/frontend/dist/assets/Settings-DqpxLARx.js
+++ b/frontend/dist/assets/Settings-sM2KFjew.js
@@ -1,4 +1,4 @@
-import{c as U,_ as T,r as c,h as u,a as h,b as e,l as k,u as a,S as $,m as v,t as o,i as s,w as b,v as x,g as V,C as S,y as M,o as g}from"./index-rfeHzF-F.js";import{L as D}from"./lock-BhsUQe9Y.js";/**
+import{c as U,_ as T,r as c,h as u,a as h,b as e,l as k,u as a,S as $,m as v,t as o,i as s,w as b,v as x,g as V,C as S,y as M,o as g}from"./index-yErsUbD6.js";import{L as D}from"./lock-CCBV2bwv.js";/**
* @license lucide-vue-next v1.0.0 - ISC
*
* This source code is licensed under the ISC license.
diff --git a/frontend/dist/assets/Setup-BrHw5pFL.js b/frontend/dist/assets/Setup-BWzXOByZ.js
similarity index 94%
rename from frontend/dist/assets/Setup-BrHw5pFL.js
rename to frontend/dist/assets/Setup-BWzXOByZ.js
index 014327a..ea90fa2 100644
--- a/frontend/dist/assets/Setup-BrHw5pFL.js
+++ b/frontend/dist/assets/Setup-BWzXOByZ.js
@@ -1 +1 @@
-import{_ as U,a as v,b as e,l as K,u as s,m,t as a,i as l,h as _,w,v as b,d as h,n as N,g as T,r,j as B,o as f}from"./index-rfeHzF-F.js";import{L as C}from"./lock-BhsUQe9Y.js";const M={class:"setup-wrap"},D={class:"card setup-card"},L={class:"muted"},P={class:"terms"},j={key:0,class:"error",role:"alert"},z=["disabled"],E={key:0,class:"spinner"},I={key:1},V="/legal",R={__name:"Setup",setup(q){const S=B(),i=r(""),o=r(""),p=r(""),y=r(!1),u=r(""),d=r(!1);async function c(){if(u.value="",o.value.length<4){u.value=l.t("passwordTooShort");return}if(o.value!==p.value){u.value=l.t("passwordMismatch");return}d.value=!0;try{await _.finishSetup(i.value.trim(),o.value,!0),S.push("/")}catch(k){u.value=k.message}finally{d.value=!1}}return(k,t)=>{var g,x;return f(),v("div",M,[e("div",D,[e("h1",null,[K(s(C),{size:22,"stroke-width":1.8,style:{"vertical-align":"-4px"}}),m(" "+a(s(l).t("setupTitle")),1)]),e("p",L,[m(a(s(l).t("setupSubtitle"))+" ",1),e("strong",null,a(((g=s(_).user)==null?void 0:g.name)||((x=s(_).user)==null?void 0:x.username)),1)]),e("label",null,a(s(l).t("email")),1),w(e("input",{"onUpdate:modelValue":t[0]||(t[0]=n=>i.value=n),type:"email",autocomplete:"email",class:"wide",onKeyup:h(c,["enter"])},null,544),[[b,i.value]]),e("label",null,a(s(l).t("newPassword")),1),w(e("input",{"onUpdate:modelValue":t[1]||(t[1]=n=>o.value=n),type:"password",autocomplete:"new-password",class:"wide",onKeyup:h(c,["enter"])},null,544),[[b,o.value]]),e("label",null,a(s(l).t("confirmPassword")),1),w(e("input",{"onUpdate:modelValue":t[2]||(t[2]=n=>p.value=n),type:"password",autocomplete:"new-password",class:"wide",onKeyup:h(c,["enter"])},null,544),[[b,p.value]]),e("label",P,[w(e("input",{"onUpdate:modelValue":t[3]||(t[3]=n=>y.value=n),type:"checkbox"},null,512),[[N,y.value]]),e("span",null,[t[4]||(t[4]=m("ฉันยอมรับ ",-1)),e("a",{href:V,target:"_blank",rel:"noopener"},"ข้อกำหนดการใช้งาน"),t[5]||(t[5]=m(" และ ",-1)),e("a",{href:V,target:"_blank",rel:"noopener"},"นโยบายความเป็นส่วนตัว")])]),u.value?(f(),v("div",j,a(u.value),1)):T("",!0),e("button",{class:"primary",style:{width:"100%","margin-top":"16px"},disabled:d.value||!i.value||!o.value||o.value!==p.value||!y.value,onClick:c},[d.value?(f(),v("span",E)):(f(),v("span",I,a(s(l).t("save")),1))],8,z)])])}}},G=U(R,[["__scopeId","data-v-a3748c0a"]]);export{G as default};
+import{_ as U,a as v,b as e,l as K,u as s,m,t as a,i as l,h as _,w,v as b,d as h,n as N,g as T,r,j as B,o as f}from"./index-yErsUbD6.js";import{L as C}from"./lock-CCBV2bwv.js";const M={class:"setup-wrap"},D={class:"card setup-card"},L={class:"muted"},P={class:"terms"},j={key:0,class:"error",role:"alert"},z=["disabled"],E={key:0,class:"spinner"},I={key:1},V="/legal",R={__name:"Setup",setup(q){const S=B(),i=r(""),o=r(""),p=r(""),y=r(!1),u=r(""),d=r(!1);async function c(){if(u.value="",o.value.length<4){u.value=l.t("passwordTooShort");return}if(o.value!==p.value){u.value=l.t("passwordMismatch");return}d.value=!0;try{await _.finishSetup(i.value.trim(),o.value,!0),S.push("/")}catch(k){u.value=k.message}finally{d.value=!1}}return(k,t)=>{var g,x;return f(),v("div",M,[e("div",D,[e("h1",null,[K(s(C),{size:22,"stroke-width":1.8,style:{"vertical-align":"-4px"}}),m(" "+a(s(l).t("setupTitle")),1)]),e("p",L,[m(a(s(l).t("setupSubtitle"))+" ",1),e("strong",null,a(((g=s(_).user)==null?void 0:g.name)||((x=s(_).user)==null?void 0:x.username)),1)]),e("label",null,a(s(l).t("email")),1),w(e("input",{"onUpdate:modelValue":t[0]||(t[0]=n=>i.value=n),type:"email",autocomplete:"email",class:"wide",onKeyup:h(c,["enter"])},null,544),[[b,i.value]]),e("label",null,a(s(l).t("newPassword")),1),w(e("input",{"onUpdate:modelValue":t[1]||(t[1]=n=>o.value=n),type:"password",autocomplete:"new-password",class:"wide",onKeyup:h(c,["enter"])},null,544),[[b,o.value]]),e("label",null,a(s(l).t("confirmPassword")),1),w(e("input",{"onUpdate:modelValue":t[2]||(t[2]=n=>p.value=n),type:"password",autocomplete:"new-password",class:"wide",onKeyup:h(c,["enter"])},null,544),[[b,p.value]]),e("label",P,[w(e("input",{"onUpdate:modelValue":t[3]||(t[3]=n=>y.value=n),type:"checkbox"},null,512),[[N,y.value]]),e("span",null,[t[4]||(t[4]=m("ฉันยอมรับ ",-1)),e("a",{href:V,target:"_blank",rel:"noopener"},"ข้อกำหนดการใช้งาน"),t[5]||(t[5]=m(" และ ",-1)),e("a",{href:V,target:"_blank",rel:"noopener"},"นโยบายความเป็นส่วนตัว")])]),u.value?(f(),v("div",j,a(u.value),1)):T("",!0),e("button",{class:"primary",style:{width:"100%","margin-top":"16px"},disabled:d.value||!i.value||!o.value||o.value!==p.value||!y.value,onClick:c},[d.value?(f(),v("span",E)):(f(),v("span",I,a(s(l).t("save")),1))],8,z)])])}}},G=U(R,[["__scopeId","data-v-a3748c0a"]]);export{G as default};
diff --git a/frontend/dist/assets/Training-C7WaLvfq.js b/frontend/dist/assets/Training-6jie9iTF.js
similarity index 95%
rename from frontend/dist/assets/Training-C7WaLvfq.js
rename to frontend/dist/assets/Training-6jie9iTF.js
index b8f633e..4c02b68 100644
--- a/frontend/dist/assets/Training-C7WaLvfq.js
+++ b/frontend/dist/assets/Training-6jie9iTF.js
@@ -1,4 +1,4 @@
-import{c as x,_ as b,p as C,y as f,h as l,a as d,b as s,l as c,u as t,m,t as o,i as n,q as _,f as A,g as h,F as z,x as M,r as v,z as T,o as r,C as B,D as N}from"./index-rfeHzF-F.js";import{T as P}from"./target-CLzEfHJ7.js";import{B as V}from"./book-open-tl-QQwuW.js";import{P as $}from"./plus-GGHS2V4y.js";/**
+import{c as x,_ as b,p as C,y as f,h as l,a as d,b as s,l as c,u as t,m,t as o,i as n,q as _,f as A,g as h,F as z,x as M,r as v,z as T,o as r,C as B,D as N}from"./index-yErsUbD6.js";import{T as P}from"./target-DmESUw9c.js";import{B as V}from"./book-open-Cmnj20ue.js";import{P as $}from"./plus-Y1qXSgej.js";/**
* @license lucide-vue-next v1.0.0 - ISC
*
* This source code is licensed under the ISC license.
diff --git a/frontend/dist/assets/arrow-left-De8eM538.js b/frontend/dist/assets/arrow-left-hWxF5h5m.js
similarity index 86%
rename from frontend/dist/assets/arrow-left-De8eM538.js
rename to frontend/dist/assets/arrow-left-hWxF5h5m.js
index 2f0d29b..2027936 100644
--- a/frontend/dist/assets/arrow-left-De8eM538.js
+++ b/frontend/dist/assets/arrow-left-hWxF5h5m.js
@@ -1,4 +1,4 @@
-import{c as e}from"./index-rfeHzF-F.js";/**
+import{c as e}from"./index-yErsUbD6.js";/**
* @license lucide-vue-next v1.0.0 - ISC
*
* This source code is licensed under the ISC license.
diff --git a/frontend/dist/assets/book-open-tl-QQwuW.js b/frontend/dist/assets/book-open-Cmnj20ue.js
similarity index 90%
rename from frontend/dist/assets/book-open-tl-QQwuW.js
rename to frontend/dist/assets/book-open-Cmnj20ue.js
index 05a9af9..9a460cf 100644
--- a/frontend/dist/assets/book-open-tl-QQwuW.js
+++ b/frontend/dist/assets/book-open-Cmnj20ue.js
@@ -1,4 +1,4 @@
-import{c as a}from"./index-rfeHzF-F.js";/**
+import{c as a}from"./index-yErsUbD6.js";/**
* @license lucide-vue-next v1.0.0 - ISC
*
* This source code is licensed under the ISC license.
diff --git a/frontend/dist/assets/index-rfeHzF-F.js b/frontend/dist/assets/index-yErsUbD6.js
similarity index 97%
rename from frontend/dist/assets/index-rfeHzF-F.js
rename to frontend/dist/assets/index-yErsUbD6.js
index fa120a2..24457f4 100644
--- a/frontend/dist/assets/index-rfeHzF-F.js
+++ b/frontend/dist/assets/index-yErsUbD6.js
@@ -1,4 +1,4 @@
-const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/Login-CsAwuAd_.js","assets/Login-j4sHK_z1.css","assets/Setup-BrHw5pFL.js","assets/lock-BhsUQe9Y.js","assets/Setup-BHyRmSn1.css","assets/Analytics-DLu98n1n.js","assets/users-BgT8ke9W.js","assets/plus-GGHS2V4y.js","assets/Analytics-CQgwlaXy.css","assets/MyBoard-xB9d_Obs.js","assets/layout-dashboard-DNjo1OkM.js","assets/MyBoard-B7ypQ1JT.css","assets/Training-C7WaLvfq.js","assets/target-CLzEfHJ7.js","assets/book-open-tl-QQwuW.js","assets/Training-Cb1U9s84.css","assets/Personas-CT2K1C7x.js","assets/arrow-left-De8eM538.js","assets/Personas-cgqlaBh7.css","assets/Chat-CeTQMGNO.js","assets/Chat-pa8hnBQD.css","assets/Settings-DqpxLARx.js","assets/Settings-BCN2EZQ5.css","assets/Guide-Cm54aYlg.js","assets/sparkles-DyyL2g7W.js","assets/GroupBuilder-DuRDlcKB.js","assets/GroupBuilder-7-9ZskJo.css","assets/GroupEdit-Ci_uwcwx.js","assets/GroupEdit-XuXM9ZDv.css","assets/AdminUsers-RvxZ4Zuo.js","assets/AdminUsers-B0sl0uHS.css"])))=>i.map(i=>d[i]);
+const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/Login-Dk-IRHf2.js","assets/Login-j4sHK_z1.css","assets/Setup-BWzXOByZ.js","assets/lock-CCBV2bwv.js","assets/Setup-BHyRmSn1.css","assets/Analytics-Cdjv87WO.js","assets/users-BK4yWB6N.js","assets/plus-Y1qXSgej.js","assets/Analytics-CQgwlaXy.css","assets/MyBoard-DTBModgt.js","assets/layout-dashboard-DplDGQ9R.js","assets/MyBoard-B7ypQ1JT.css","assets/Training-6jie9iTF.js","assets/target-DmESUw9c.js","assets/book-open-Cmnj20ue.js","assets/Training-Cb1U9s84.css","assets/Personas-CjtqghLF.js","assets/arrow-left-hWxF5h5m.js","assets/Personas-cgqlaBh7.css","assets/Chat-Dr4nNqcU.js","assets/Chat-pa8hnBQD.css","assets/Settings-sM2KFjew.js","assets/Settings-BCN2EZQ5.css","assets/Guide-fA0ESKf-.js","assets/GroupBuilder-Odxme5l4.js","assets/GroupBuilder-7-9ZskJo.css","assets/GroupEdit-Bzhg1xqU.js","assets/GroupEdit-sOm6hJOC.css","assets/AdminUsers-CnHWEscS.js","assets/AdminUsers-B0sl0uHS.css"])))=>i.map(i=>d[i]);
(function(){const t=document.createElement("link").relList;if(t&&t.supports&&t.supports("modulepreload"))return;for(const r of document.querySelectorAll('link[rel="modulepreload"]'))s(r);new MutationObserver(r=>{for(const i of r)if(i.type==="childList")for(const o of i.addedNodes)o.tagName==="LINK"&&o.rel==="modulepreload"&&s(o)}).observe(document,{childList:!0,subtree:!0});function n(r){const i={};return r.integrity&&(i.integrity=r.integrity),r.referrerPolicy&&(i.referrerPolicy=r.referrerPolicy),r.crossOrigin==="use-credentials"?i.credentials="include":r.crossOrigin==="anonymous"?i.credentials="omit":i.credentials="same-origin",i}function s(r){if(r.ep)return;r.ep=!0;const i=n(r);fetch(r.href,i)}})();/**
* @vue/shared v3.5.41
* (c) 2018-present Yuxi (Evan) You and Vue contributors
@@ -79,4 +79,4 @@ const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/Login-CsAwuAd_.
*
* This source code is licensed under the ISC license.
* See the LICENSE file in the root directory of this source tree.
- */const Eu=No("settings",[["path",{d:"M9.671 4.136a2.34 2.34 0 0 1 4.659 0 2.34 2.34 0 0 0 3.319 1.915 2.34 2.34 0 0 1 2.33 4.033 2.34 2.34 0 0 0 0 3.831 2.34 2.34 0 0 1-2.33 4.033 2.34 2.34 0 0 0-3.319 1.915 2.34 2.34 0 0 1-4.659 0 2.34 2.34 0 0 0-3.32-1.915 2.34 2.34 0 0 1-2.33-4.033 2.34 2.34 0 0 0 0-3.831A2.34 2.34 0 0 1 6.35 6.051a2.34 2.34 0 0 0 3.319-1.915",key:"1i5ecw"}],["circle",{cx:"12",cy:"12",r:"3",key:"1v7zrd"}]]),Bs="st_token";function Mo(){return localStorage.getItem(Bs)}function ws(e){e?localStorage.setItem(Bs,e):localStorage.removeItem(Bs)}async function ee(e,t,n,s=!1){const r={},i=Mo();i&&(r.Authorization=`Bearer ${i}`);let o=n;!s&&n!==void 0&&n!==null&&(r["Content-Type"]="application/json",o=JSON.stringify(n));const l=await fetch(t,{method:e,headers:r,body:o});let c=null;try{c=await l.json()}catch{}if(!l.ok){const f=c&&(c.error||c.message)||`HTTP ${l.status}`;throw new Error(f)}return c}const Ss={login:(e,t)=>ee("POST","/api/auth/login",{username:e,password:t}),me:()=>ee("GET","/api/auth/me"),setup:e=>ee("POST","/api/auth/setup",e),updateProfile:e=>ee("PATCH","/api/auth/profile",e),adminCreateUser:e=>ee("POST","/api/admin/users",e),adminListUsers:()=>ee("GET","/api/admin/users"),adminUpdateUser:(e,t)=>ee("PUT",`/api/admin/users/${e}`,t),createGroup:e=>ee("POST","/api/groups",e,!0),listGroups:()=>ee("GET","/api/groups"),getGroup:e=>ee("GET",`/api/groups/${e}`),deleteGroup:e=>ee("DELETE",`/api/groups/${e}`),analyzeGroup:(e,t={})=>ee("POST",`/api/groups/${e}/analyze${t.append?"?append=true":""}`),listPersonas:e=>ee("GET",`/api/groups/${e}/personas`),getPersona:(e,t)=>ee("GET",`/api/groups/${e}/personas/${t}`),updatePersona:(e,t,n)=>ee("PUT",`/api/groups/${e}/personas/${t}`,n),chatStart:(e,t,n,s)=>ee("POST",`/api/chat/${e}/personas/${t}/chat/start`,{scenario:n,locale:s}),chatResume:(e,t)=>ee("GET",`/api/chat/${e}/personas/${t}/chat/resume`),chatSend:(e,t,n)=>ee("POST",`/api/chat/${e}/personas/${t}/chat/send`,{text:n}),chatFinish:(e,t)=>ee("POST",`/api/chat/${e}/personas/${t}/chat/finish`),mySessions:()=>ee("GET","/api/chat/sessions"),myBoard:()=>ee("GET","/api/me/board"),weakAreas:()=>ee("GET","/api/me/weak-areas"),myPersonas:()=>ee("GET","/api/me/personas"),generatePersona:e=>ee("POST","/api/me/personas/generate",e),analytics:(e={})=>{const t=new URLSearchParams;e.from&&t.set("from",e.from),e.to&&t.set("to",e.to);const n=t.toString();return ee("GET",`/api/analytics${n?`?${n}`:""}`)}},Ce=Yt({user:null,token:Mo(),mustSetup:!1,get role(){return this.user?this.user.role:null},get isAdmin(){return this.role==="admin"||this.role==="super_admin"},get isSuperAdmin(){return this.role==="super_admin"},async load(){var e;if(!this.token)return null;try{const t=await Ss.me();return this.user=t.user,this.mustSetup=!!((e=t.user)!=null&&e.must_setup),this.user}catch{return this.user=null,this.mustSetup=!1,ws(null),null}},async login(e,t){const n=await Ss.login(e,t);return this.token=n.token,ws(n.token),this.user=n.user,this.mustSetup=!!n.must_setup,n.user},async finishSetup(e,t,n=!1){const s=await Ss.setup({username:this.user.username||this.user.id,email:e,password:t,accepted_terms:n});return this.user=s.user,this.mustSetup=!1,s.user},logout(){this.user=null,this.token=null,this.mustSetup=!1,ws(null)}}),As={en:{app:"Sales Trainer",login:"Login",logout:"Logout",username:"Username",email:"Email",password:"Password",newPassword:"New password",confirmPassword:"Confirm password",save:"Save",passwordTooShort:"Password must be at least 4 characters",passwordMismatch:"Passwords do not match",setupTitle:"Set up your account",setupSubtitle:"First login for ",loginError:"Invalid credentials",dashboard:"Dashboard",training:"Training",myDashboard:"My dashboard",adminOverview:"Admin overview",guideTitle:"How to use / คู่มือใช้งาน",settings:"Settings",account:"Account",profile:"Profile",readonly:"read-only",name:"Display name",saveProfile:"Save profile",changePassword:"Change password",optional:"optional",saved:"Saved",total:"Total",dateRange:"Date range",apply:"Apply",clear:"Clear",hardestPersonas:"Hardest personas",closeRate:"Close rate",managePersonas:"Manage personas",difficulty:"Difficulty",trained:"Trained",addProduct:"Add product",trainingSubtitle:"Pick a product group, then choose a persona to practice closing the sale.",noTraining:"No training groups available",groups:"Persona Groups",myTraining:"My Training",adminTools:"Admin Tools",users:"Users",analytics:"Analytics",groupBuilder:"Group Builder",create:"Create",analyze:"Analyze",manual:"Manual",edit:"Edit",product:"Product",segment:"Initial customer segment (optional)",description:"Additional description / scenario (optional)",channel:"Channel",facebook:"Facebook",line:"LINE",language:"Language",thai:"Thai",english:"English",personas:"Personas",tierA:"Tier A — Ready to buy",tierB:"Tier B — Unsure",tierC:"Tier C — Not interested but has pain",selectPersona:"Select a persona to practice",chooseScenario:"Choose a scenario",chatGuideTitle:"How to practice",chatGuideText:'You are playing the salesperson. Chat with this customer and try to close the sale. Ask about their needs, solve their pain, and handle their objections. When you are done, press "Finish & get result" to see your score and coaching.',chat:"Chat",start:"Start",send:"Send",finish:"Finish & get result",debrief:"Result & Coaching",won:"Won",lost:"Lost",notTried:"Not tried",score:"Score",pain:"Pain",why:"Reason",reveal:"Revealed persona details",generatePersona:"Generate my persona",weakAreas:"My weak areas",mySessions:"My sessions",openSaleTask:"The customer did NOT message first. You must open the sale.",sellerInitiated:"You must open the sale (outbound)",customerInitiated:"The customer will message you first"},th:{app:"ระบบฝึกทักษะการขาย",login:"เข้าสู่ระบบ",logout:"ออกจากระบบ",username:"ชื่อผู้ใช้",email:"อีเมล",password:"รหัสผ่าน",newPassword:"รหัสผ่านใหม่",confirmPassword:"ยืนยันรหัสผ่าน",save:"บันทึก",passwordTooShort:"รหัสผ่านต้องมีความยาวอย่างน้อย 4 ตัวอักษร",passwordMismatch:"รหัสผ่านที่ยืนยันไม่ตรงกัน",setupTitle:"ตั้งค่าบัญชีของคุณ",setupSubtitle:"เข้าสู่ระบบครั้งแรกสำหรับ ",loginError:"ชื่อผู้ใช้หรือรหัสผ่านไม่ถูกต้อง กรุณาลองอีกครั้ง",dashboard:"หน้าหลัก",training:"การฝึก",myDashboard:"ภาพรวมผลการฝึก",adminOverview:"ภาพรวมผู้ดูแล",settings:"การตั้งค่า",account:"บัญชี",profile:"ข้อมูลส่วนตัว",readonly:"อ่านอย่างเดียว",name:"ชื่อที่แสดง",saveProfile:"บันทึกข้อมูล",changePassword:"เปลี่ยนรหัสผ่าน",optional:"ไม่บังคับ",saved:"บันทึกเรียบร้อย",total:"ทั้งหมด",dateRange:"ช่วงเวลา",apply:"กรอง",clear:"ล้าง",hardestPersonas:"บุคคลต้นแบบที่ขายยากที่สุด",closeRate:"อัตราปิดการขาย",managePersonas:"จัดการบุคคลต้นแบบ",difficulty:"ระดับความยาก",trained:"ฝึกแล้ว",addProduct:"เพิ่มสินค้า",trainingSubtitle:"เลือกกลุ่มสินค้า แล้วเลือกบุคคลต้นแบบเพื่อฝึกปิดการขาย",noTraining:"ยังไม่มีกลุ่มฝึก",groups:"กลุ่มบุคคลต้นแบบลูกค้า (Persona)",myTraining:"ประวัติการฝึก",adminTools:"เครื่องมือผู้ดูแลระบบ",users:"ผู้ใช้งาน",analytics:"สถิติ",groupBuilder:"สร้างกลุ่มบุคคลต้นแบบ",create:"สร้าง",analyze:"วิเคราะห์",manual:"ระบุเอง",edit:"แก้ไข",product:"สินค้า/บริการ/ไอเดีย",segment:"กลุ่มลูกค้าเป้าหมายเบื้องต้น (ไม่บังคับ)",description:"คำอธิบายหรือรายละเอียดเพิ่มเติม (ไม่บังคับ)",channel:"ช่องทางติดต่อ",facebook:"Facebook",line:"LINE",language:"ภาษา",thai:"ไทย",english:"อังกฤษ",personas:"บุคคลต้นแบบ",tierA:"ระดับ A — พร้อมตัดสินใจซื้อ",tierB:"ระดับ B — ยังไม่แน่ใจ",tierC:"ระดับ C — ไม่สนใจแต่มีปัญหา",selectPersona:"เลือกบุคคลต้นแบบเพื่อฝึก",chooseScenario:"เลือกสถานการณ์",chatGuideTitle:"วิธีฝึก",chatGuideText:'คุณรับบทเป็นพนักงานขาย พูดคุยกับลูกค้าคนนี้เพื่อพยายามปิดการขายให้ได้ สอบถามความต้องการ แก้ไขปัญหาของลูกค้า และรับมือกับข้อโต้แย้ง เมื่อพอใจแล้วกด "สรุปผล" เพื่อดูคะแนนและข้อเสนอแนะ',chat:"แชท",start:"เริ่มต้น",send:"ส่ง",finish:"สรุปผล",debrief:"ผลลัพธ์และข้อเสนอแนะ",won:"ปิดการขายได้",lost:"ปิดการขายไม่ได้",notTried:"ยังไม่ได้ฝึก",score:"คะแนน",pain:"ปัญหาของลูกค้า",why:"เหตุผล",reveal:"รายละเอียดบุคคลต้นแบบที่ถูกซ่อนไว้",generatePersona:"สร้างบุคคลต้นแบบเพิ่มเติม",weakAreas:"จุดอ่อนที่ควรฝึกเพิ่มเติม",mySessions:"ประวัติการฝึกของฉัน",openSaleTask:"ลูกค้ายังไม่ได้ทักเข้ามา คุณต้องเป็นฝ่ายเริ่มบทสนทนาการขายเอง",sellerInitiated:"คุณต้องเริ่มการขายในเชิงรุก",customerInitiated:"ลูกค้าจะติดต่อเข้ามาก่อน"}},Ke=Yt({locale:localStorage.getItem("locale")||"th",t(e){return As[this.locale]&&As[this.locale][e]||As.en[e]||e},set(e){this.locale=e,localStorage.setItem("locale",e)}}),wu=(e,t)=>{const n=e.__vccOpts||e;for(const[s,r]of t)n[s]=r;return n},Su={class:"app"},Au={key:0,class:"topnav"},Ru={class:"tabs"},xu={class:"nav-right"},Tu={class:"txt"},Cu={class:"main"},Pu={__name:"App",setup(e){const t=pu();function n(){Ke.set(Ke.locale==="th"?"en":"th")}function s(){Ce.logout(),t.push("/login")}return Wi(()=>{Ce.token&&!Ce.user&&Ce.load()}),(r,i)=>{const o=_r("router-link"),l=_r("router-view");return on(),Tr("div",Su,[ae(Ce).user?(on(),Tr("nav",Au,[ie(o,{to:"/",class:"brand"},{default:Vt(()=>[Ft(Rt(ae(Ke).t("app")),1)]),_:1}),mt("div",Ru,[ae(Ce).isAdmin?(on(),Ds(o,{key:0,to:"/",class:jt(["tab",{active:r.$route.path==="/"}])},{default:Vt(()=>[Ft(Rt(ae(Ke).t("adminOverview")),1)]),_:1},8,["class"])):Cr("",!0),ie(o,{to:"/my/board",class:jt(["tab",{active:r.$route.path==="/my/board"}])},{default:Vt(()=>[Ft(Rt(ae(Ke).t("myDashboard")),1)]),_:1},8,["class"]),ie(o,{to:"/training",class:jt(["tab",{active:r.$route.path==="/training"}])},{default:Vt(()=>[Ft(Rt(ae(Ke).t("training")),1)]),_:1},8,["class"])]),mt("div",xu,[mt("button",{onClick:n,class:"lang"},Rt(ae(Ke).locale==="th"?"EN":"TH"),1),ie(o,{to:"/settings",class:"icon-btn",title:ae(Ke).t("settings")},{default:Vt(()=>[ie(ae(Eu),{size:20,"stroke-width":1.8})]),_:1},8,["title"]),mt("button",{onClick:s,class:"logout-btn"},[ie(ae(bu),{size:18,"stroke-width":1.8}),i[0]||(i[0]=Ft()),mt("span",Tu,Rt(ae(Ke).t("logout")),1)])])])):Cr("",!0),mt("main",Cu,[(on(),Ds(l,{key:ae(Ke).locale}))])])}}},Ou=wu(Pu,[["__scopeId","data-v-f29a3a4f"]]),Iu="modulepreload",Nu=function(e){return"/"+e},ai={},Se=function(t,n,s){let r=Promise.resolve();if(n&&n.length>0){document.getElementsByTagName("link");const o=document.querySelector("meta[property=csp-nonce]"),l=(o==null?void 0:o.nonce)||(o==null?void 0:o.getAttribute("nonce"));r=Promise.allSettled(n.map(c=>{if(c=Nu(c),c in ai)return;ai[c]=!0;const f=c.endsWith(".css"),u=f?'[rel="stylesheet"]':"";if(document.querySelector(`link[href="${c}"]${u}`))return;const h=document.createElement("link");if(h.rel=f?"stylesheet":Iu,f||(h.as="script"),h.crossOrigin="",h.href=c,l&&h.setAttribute("nonce",l),document.head.appendChild(h),f)return new Promise((g,m)=>{h.addEventListener("load",g),h.addEventListener("error",()=>m(new Error(`Unable to preload CSS for ${c}`)))})}))}function i(o){const l=new Event("vite:preloadError",{cancelable:!0});if(l.payload=o,window.dispatchEvent(l),!l.defaultPrevented)throw o}return r.then(o=>{for(const l of o||[])l.status==="rejected"&&i(l.reason);return t().catch(i)})},Mu=[{path:"/login",component:()=>Se(()=>import("./Login-CsAwuAd_.js"),__vite__mapDeps([0,1])),meta:{public:!0}},{path:"/setup",component:()=>Se(()=>import("./Setup-BrHw5pFL.js"),__vite__mapDeps([2,3,4]))},{path:"/",component:()=>Se(()=>import("./Analytics-DLu98n1n.js"),__vite__mapDeps([5,6,7,8])),meta:{admin:!0}},{path:"/my/board",component:()=>Se(()=>import("./MyBoard-xB9d_Obs.js"),__vite__mapDeps([9,10,11]))},{path:"/training",component:()=>Se(()=>import("./Training-C7WaLvfq.js"),__vite__mapDeps([12,13,14,7,15]))},{path:"/groups/:gid/personas",component:()=>Se(()=>import("./Personas-CT2K1C7x.js"),__vite__mapDeps([16,13,17,18]))},{path:"/groups/:gid/chat/:pid",component:()=>Se(()=>import("./Chat-CeTQMGNO.js"),__vite__mapDeps([19,13,17,20]))},{path:"/settings",component:()=>Se(()=>import("./Settings-DqpxLARx.js"),__vite__mapDeps([21,3,22]))},{path:"/guide",component:()=>Se(()=>import("./Guide-Cm54aYlg.js"),__vite__mapDeps([23,14,10,13,24]))},{path:"/admin/new-group",component:()=>Se(()=>import("./GroupBuilder-DuRDlcKB.js"),__vite__mapDeps([25,17,26])),meta:{admin:!0}},{path:"/admin/groups/:gid/edit",component:()=>Se(()=>import("./GroupEdit-Ci_uwcwx.js"),__vite__mapDeps([27,6,24,28])),meta:{admin:!0}},{path:"/admin/users",component:()=>Se(()=>import("./AdminUsers-RvxZ4Zuo.js"),__vite__mapDeps([29,6,30])),meta:{admin:!0}},{path:"/admin/analytics",component:()=>Se(()=>import("./Analytics-DLu98n1n.js"),__vite__mapDeps([5,6,7,8])),meta:{admin:!0}}],Do=hu({history:$a(),routes:Mu});Do.beforeEach(async e=>e.meta.public?!0:(Ce.user||await Ce.load(),Ce.user?Ce.mustSetup&&e.path!=="/setup"?{path:"/setup"}:e.path==="/"&&!Ce.isAdmin?{path:"/my/board"}:e.meta.admin&&!Ce.isAdmin?{path:"/my/board"}:!0:{path:"/login",query:{redirect:e.fullPath}}));ia(Ou).use(Do).mount("#app");export{Fu as A,Oe as B,jt as C,Uu as D,Zs as E,tt as F,Vu as G,Xc as H,Mn as I,Yt as J,Eu as S,wu as _,Tr as a,mt as b,No as c,Bu as d,Hu as e,Ds as f,Cr as g,Ce as h,Ke as i,pu as j,ju as k,ie as l,Ft as m,Yc as n,on as o,Wi as p,Vt as q,dl as r,Ks as s,Rt as t,ae as u,jr as v,Du as w,Lu as x,Ss as y,_r as z};
+ */const Eu=No("settings",[["path",{d:"M9.671 4.136a2.34 2.34 0 0 1 4.659 0 2.34 2.34 0 0 0 3.319 1.915 2.34 2.34 0 0 1 2.33 4.033 2.34 2.34 0 0 0 0 3.831 2.34 2.34 0 0 1-2.33 4.033 2.34 2.34 0 0 0-3.319 1.915 2.34 2.34 0 0 1-4.659 0 2.34 2.34 0 0 0-3.32-1.915 2.34 2.34 0 0 1-2.33-4.033 2.34 2.34 0 0 0 0-3.831A2.34 2.34 0 0 1 6.35 6.051a2.34 2.34 0 0 0 3.319-1.915",key:"1i5ecw"}],["circle",{cx:"12",cy:"12",r:"3",key:"1v7zrd"}]]),Bs="st_token";function Mo(){return localStorage.getItem(Bs)}function ws(e){e?localStorage.setItem(Bs,e):localStorage.removeItem(Bs)}async function ee(e,t,n,s=!1){const r={},i=Mo();i&&(r.Authorization=`Bearer ${i}`);let o=n;!s&&n!==void 0&&n!==null&&(r["Content-Type"]="application/json",o=JSON.stringify(n));const l=await fetch(t,{method:e,headers:r,body:o});let c=null;try{c=await l.json()}catch{}if(!l.ok){const f=c&&(c.error||c.message)||`HTTP ${l.status}`;throw new Error(f)}return c}const Ss={login:(e,t)=>ee("POST","/api/auth/login",{username:e,password:t}),me:()=>ee("GET","/api/auth/me"),setup:e=>ee("POST","/api/auth/setup",e),updateProfile:e=>ee("PATCH","/api/auth/profile",e),adminCreateUser:e=>ee("POST","/api/admin/users",e),adminListUsers:()=>ee("GET","/api/admin/users"),adminUpdateUser:(e,t)=>ee("PUT",`/api/admin/users/${e}`,t),createGroup:e=>ee("POST","/api/groups",e,!0),listGroups:()=>ee("GET","/api/groups"),getGroup:e=>ee("GET",`/api/groups/${e}`),deleteGroup:e=>ee("DELETE",`/api/groups/${e}`),analyzeGroup:(e,t={})=>ee("POST",`/api/groups/${e}/analyze${t.append?"?append=true":""}`),listPersonas:e=>ee("GET",`/api/groups/${e}/personas`),getPersona:(e,t)=>ee("GET",`/api/groups/${e}/personas/${t}`),updatePersona:(e,t,n)=>ee("PUT",`/api/groups/${e}/personas/${t}`,n),chatStart:(e,t,n,s)=>ee("POST",`/api/chat/${e}/personas/${t}/chat/start`,{scenario:n,locale:s}),chatResume:(e,t)=>ee("GET",`/api/chat/${e}/personas/${t}/chat/resume`),chatSend:(e,t,n)=>ee("POST",`/api/chat/${e}/personas/${t}/chat/send`,{text:n}),chatFinish:(e,t)=>ee("POST",`/api/chat/${e}/personas/${t}/chat/finish`),mySessions:()=>ee("GET","/api/chat/sessions"),myBoard:()=>ee("GET","/api/me/board"),weakAreas:()=>ee("GET","/api/me/weak-areas"),myPersonas:()=>ee("GET","/api/me/personas"),generatePersona:e=>ee("POST","/api/me/personas/generate",e),analytics:(e={})=>{const t=new URLSearchParams;e.from&&t.set("from",e.from),e.to&&t.set("to",e.to);const n=t.toString();return ee("GET",`/api/analytics${n?`?${n}`:""}`)}},Ce=Yt({user:null,token:Mo(),mustSetup:!1,get role(){return this.user?this.user.role:null},get isAdmin(){return this.role==="admin"||this.role==="super_admin"},get isSuperAdmin(){return this.role==="super_admin"},async load(){var e;if(!this.token)return null;try{const t=await Ss.me();return this.user=t.user,this.mustSetup=!!((e=t.user)!=null&&e.must_setup),this.user}catch{return this.user=null,this.mustSetup=!1,ws(null),null}},async login(e,t){const n=await Ss.login(e,t);return this.token=n.token,ws(n.token),this.user=n.user,this.mustSetup=!!n.must_setup,n.user},async finishSetup(e,t,n=!1){const s=await Ss.setup({username:this.user.username||this.user.id,email:e,password:t,accepted_terms:n});return this.user=s.user,this.mustSetup=!1,s.user},logout(){this.user=null,this.token=null,this.mustSetup=!1,ws(null)}}),As={en:{app:"Sales Trainer",login:"Login",logout:"Logout",username:"Username",email:"Email",password:"Password",newPassword:"New password",confirmPassword:"Confirm password",save:"Save",passwordTooShort:"Password must be at least 4 characters",passwordMismatch:"Passwords do not match",setupTitle:"Set up your account",setupSubtitle:"First login for ",loginError:"Invalid credentials",dashboard:"Dashboard",training:"Training",myDashboard:"My dashboard",adminOverview:"Admin overview",guideTitle:"How to use / คู่มือใช้งาน",settings:"Settings",account:"Account",profile:"Profile",readonly:"read-only",name:"Display name",saveProfile:"Save profile",changePassword:"Change password",optional:"optional",saved:"Saved",total:"Total",dateRange:"Date range",apply:"Apply",clear:"Clear",hardestPersonas:"Hardest personas",closeRate:"Close rate",managePersonas:"Manage personas",difficulty:"Difficulty",trained:"Trained",addProduct:"Add product",trainingSubtitle:"Pick a product group, then choose a persona to practice closing the sale.",noTraining:"No training groups available",groups:"Persona Groups",myTraining:"My Training",adminTools:"Admin Tools",users:"Users",analytics:"Analytics",groupBuilder:"Group Builder",create:"Create",analyze:"Analyze",manual:"Manual",edit:"Edit",product:"Product",segment:"Initial customer segment (optional)",description:"Additional description / scenario (optional)",channel:"Channel",facebook:"Facebook",line:"LINE",language:"Language",thai:"Thai",english:"English",personas:"Personas",tierA:"Tier A — Ready to buy",tierB:"Tier B — Unsure",tierC:"Tier C — Not interested but has pain",selectPersona:"Select a persona to practice",chooseScenario:"Choose a scenario",chatGuideTitle:"How to practice",chatGuideText:'You are playing the salesperson. Chat with this customer and try to close the sale. Ask about their needs, solve their pain, and handle their objections. When you are done, press "Finish & get result" to see your score and coaching.',chat:"Chat",start:"Start",send:"Send",finish:"Finish & get result",debrief:"Result & Coaching",won:"Won",lost:"Lost",notTried:"Not tried",score:"Score",pain:"Pain",why:"Reason",reveal:"Revealed persona details",generatePersona:"Generate my persona",weakAreas:"My weak areas",mySessions:"My sessions",openSaleTask:"The customer did NOT message first. You must open the sale.",sellerInitiated:"You must open the sale (outbound)",customerInitiated:"The customer will message you first"},th:{app:"ระบบฝึกทักษะการขาย",login:"เข้าสู่ระบบ",logout:"ออกจากระบบ",username:"ชื่อผู้ใช้",email:"อีเมล",password:"รหัสผ่าน",newPassword:"รหัสผ่านใหม่",confirmPassword:"ยืนยันรหัสผ่าน",save:"บันทึก",passwordTooShort:"รหัสผ่านต้องมีความยาวอย่างน้อย 4 ตัวอักษร",passwordMismatch:"รหัสผ่านที่ยืนยันไม่ตรงกัน",setupTitle:"ตั้งค่าบัญชีของคุณ",setupSubtitle:"เข้าสู่ระบบครั้งแรกสำหรับ ",loginError:"ชื่อผู้ใช้หรือรหัสผ่านไม่ถูกต้อง กรุณาลองอีกครั้ง",dashboard:"หน้าหลัก",training:"การฝึก",myDashboard:"ภาพรวมผลการฝึก",adminOverview:"ภาพรวมผู้ดูแล",settings:"การตั้งค่า",account:"บัญชี",profile:"ข้อมูลส่วนตัว",readonly:"อ่านอย่างเดียว",name:"ชื่อที่แสดง",saveProfile:"บันทึกข้อมูล",changePassword:"เปลี่ยนรหัสผ่าน",optional:"ไม่บังคับ",saved:"บันทึกเรียบร้อย",total:"ทั้งหมด",dateRange:"ช่วงเวลา",apply:"กรอง",clear:"ล้าง",hardestPersonas:"บุคคลต้นแบบที่ขายยากที่สุด",closeRate:"อัตราปิดการขาย",managePersonas:"จัดการบุคคลต้นแบบ",difficulty:"ระดับความยาก",trained:"ฝึกแล้ว",addProduct:"เพิ่มสินค้า",trainingSubtitle:"เลือกกลุ่มสินค้า แล้วเลือกบุคคลต้นแบบเพื่อฝึกปิดการขาย",noTraining:"ยังไม่มีกลุ่มฝึก",groups:"กลุ่มบุคคลต้นแบบลูกค้า (Persona)",myTraining:"ประวัติการฝึก",adminTools:"เครื่องมือผู้ดูแลระบบ",users:"ผู้ใช้งาน",analytics:"สถิติ",groupBuilder:"สร้างกลุ่มบุคคลต้นแบบ",create:"สร้าง",analyze:"วิเคราะห์",manual:"ระบุเอง",edit:"แก้ไข",product:"สินค้า/บริการ/ไอเดีย",segment:"กลุ่มลูกค้าเป้าหมายเบื้องต้น (ไม่บังคับ)",description:"คำอธิบายหรือรายละเอียดเพิ่มเติม (ไม่บังคับ)",channel:"ช่องทางติดต่อ",facebook:"Facebook",line:"LINE",language:"ภาษา",thai:"ไทย",english:"อังกฤษ",personas:"บุคคลต้นแบบ",tierA:"ระดับ A — พร้อมตัดสินใจซื้อ",tierB:"ระดับ B — ยังไม่แน่ใจ",tierC:"ระดับ C — ไม่สนใจแต่มีปัญหา",selectPersona:"เลือกบุคคลต้นแบบเพื่อฝึก",chooseScenario:"เลือกสถานการณ์",chatGuideTitle:"วิธีฝึก",chatGuideText:'คุณรับบทเป็นพนักงานขาย พูดคุยกับลูกค้าคนนี้เพื่อพยายามปิดการขายให้ได้ สอบถามความต้องการ แก้ไขปัญหาของลูกค้า และรับมือกับข้อโต้แย้ง เมื่อพอใจแล้วกด "สรุปผล" เพื่อดูคะแนนและข้อเสนอแนะ',chat:"แชท",start:"เริ่มต้น",send:"ส่ง",finish:"สรุปผล",debrief:"ผลลัพธ์และข้อเสนอแนะ",won:"ปิดการขายได้",lost:"ปิดการขายไม่ได้",notTried:"ยังไม่ได้ฝึก",score:"คะแนน",pain:"ปัญหาของลูกค้า",why:"เหตุผล",reveal:"รายละเอียดบุคคลต้นแบบที่ถูกซ่อนไว้",generatePersona:"สร้างบุคคลต้นแบบเพิ่มเติม",weakAreas:"จุดอ่อนที่ควรฝึกเพิ่มเติม",mySessions:"ประวัติการฝึกของฉัน",openSaleTask:"ลูกค้ายังไม่ได้ทักเข้ามา คุณต้องเป็นฝ่ายเริ่มบทสนทนาการขายเอง",sellerInitiated:"คุณต้องเริ่มการขายในเชิงรุก",customerInitiated:"ลูกค้าจะติดต่อเข้ามาก่อน"}},Ke=Yt({locale:localStorage.getItem("locale")||"th",t(e){return As[this.locale]&&As[this.locale][e]||As.en[e]||e},set(e){this.locale=e,localStorage.setItem("locale",e)}}),wu=(e,t)=>{const n=e.__vccOpts||e;for(const[s,r]of t)n[s]=r;return n},Su={class:"app"},Au={key:0,class:"topnav"},Ru={class:"tabs"},xu={class:"nav-right"},Tu={class:"txt"},Cu={class:"main"},Pu={__name:"App",setup(e){const t=pu();function n(){Ke.set(Ke.locale==="th"?"en":"th")}function s(){Ce.logout(),t.push("/login")}return Wi(()=>{Ce.token&&!Ce.user&&Ce.load()}),(r,i)=>{const o=_r("router-link"),l=_r("router-view");return on(),Tr("div",Su,[ae(Ce).user?(on(),Tr("nav",Au,[ie(o,{to:"/",class:"brand"},{default:Vt(()=>[Ft(Rt(ae(Ke).t("app")),1)]),_:1}),mt("div",Ru,[ae(Ce).isAdmin?(on(),Ds(o,{key:0,to:"/",class:jt(["tab",{active:r.$route.path==="/"}])},{default:Vt(()=>[Ft(Rt(ae(Ke).t("adminOverview")),1)]),_:1},8,["class"])):Cr("",!0),ie(o,{to:"/my/board",class:jt(["tab",{active:r.$route.path==="/my/board"}])},{default:Vt(()=>[Ft(Rt(ae(Ke).t("myDashboard")),1)]),_:1},8,["class"]),ie(o,{to:"/training",class:jt(["tab",{active:r.$route.path==="/training"}])},{default:Vt(()=>[Ft(Rt(ae(Ke).t("training")),1)]),_:1},8,["class"])]),mt("div",xu,[mt("button",{onClick:n,class:"lang"},Rt(ae(Ke).locale==="th"?"EN":"TH"),1),ie(o,{to:"/settings",class:"icon-btn",title:ae(Ke).t("settings")},{default:Vt(()=>[ie(ae(Eu),{size:20,"stroke-width":1.8})]),_:1},8,["title"]),mt("button",{onClick:s,class:"logout-btn"},[ie(ae(bu),{size:18,"stroke-width":1.8}),i[0]||(i[0]=Ft()),mt("span",Tu,Rt(ae(Ke).t("logout")),1)])])])):Cr("",!0),mt("main",Cu,[(on(),Ds(l,{key:ae(Ke).locale}))])])}}},Ou=wu(Pu,[["__scopeId","data-v-f29a3a4f"]]),Iu="modulepreload",Nu=function(e){return"/"+e},ai={},Se=function(t,n,s){let r=Promise.resolve();if(n&&n.length>0){document.getElementsByTagName("link");const o=document.querySelector("meta[property=csp-nonce]"),l=(o==null?void 0:o.nonce)||(o==null?void 0:o.getAttribute("nonce"));r=Promise.allSettled(n.map(c=>{if(c=Nu(c),c in ai)return;ai[c]=!0;const f=c.endsWith(".css"),u=f?'[rel="stylesheet"]':"";if(document.querySelector(`link[href="${c}"]${u}`))return;const h=document.createElement("link");if(h.rel=f?"stylesheet":Iu,f||(h.as="script"),h.crossOrigin="",h.href=c,l&&h.setAttribute("nonce",l),document.head.appendChild(h),f)return new Promise((g,m)=>{h.addEventListener("load",g),h.addEventListener("error",()=>m(new Error(`Unable to preload CSS for ${c}`)))})}))}function i(o){const l=new Event("vite:preloadError",{cancelable:!0});if(l.payload=o,window.dispatchEvent(l),!l.defaultPrevented)throw o}return r.then(o=>{for(const l of o||[])l.status==="rejected"&&i(l.reason);return t().catch(i)})},Mu=[{path:"/login",component:()=>Se(()=>import("./Login-Dk-IRHf2.js"),__vite__mapDeps([0,1])),meta:{public:!0}},{path:"/setup",component:()=>Se(()=>import("./Setup-BWzXOByZ.js"),__vite__mapDeps([2,3,4]))},{path:"/",component:()=>Se(()=>import("./Analytics-Cdjv87WO.js"),__vite__mapDeps([5,6,7,8])),meta:{admin:!0}},{path:"/my/board",component:()=>Se(()=>import("./MyBoard-DTBModgt.js"),__vite__mapDeps([9,10,11]))},{path:"/training",component:()=>Se(()=>import("./Training-6jie9iTF.js"),__vite__mapDeps([12,13,14,7,15]))},{path:"/groups/:gid/personas",component:()=>Se(()=>import("./Personas-CjtqghLF.js"),__vite__mapDeps([16,13,17,18]))},{path:"/groups/:gid/chat/:pid",component:()=>Se(()=>import("./Chat-Dr4nNqcU.js"),__vite__mapDeps([19,13,17,20]))},{path:"/settings",component:()=>Se(()=>import("./Settings-sM2KFjew.js"),__vite__mapDeps([21,3,22]))},{path:"/guide",component:()=>Se(()=>import("./Guide-fA0ESKf-.js"),__vite__mapDeps([23,14,10,13]))},{path:"/admin/new-group",component:()=>Se(()=>import("./GroupBuilder-Odxme5l4.js"),__vite__mapDeps([24,17,25])),meta:{admin:!0}},{path:"/admin/groups/:gid/edit",component:()=>Se(()=>import("./GroupEdit-Bzhg1xqU.js"),__vite__mapDeps([26,6,27])),meta:{admin:!0}},{path:"/admin/users",component:()=>Se(()=>import("./AdminUsers-CnHWEscS.js"),__vite__mapDeps([28,6,29])),meta:{admin:!0}},{path:"/admin/analytics",component:()=>Se(()=>import("./Analytics-Cdjv87WO.js"),__vite__mapDeps([5,6,7,8])),meta:{admin:!0}}],Do=hu({history:$a(),routes:Mu});Do.beforeEach(async e=>e.meta.public?!0:(Ce.user||await Ce.load(),Ce.user?Ce.mustSetup&&e.path!=="/setup"?{path:"/setup"}:e.path==="/"&&!Ce.isAdmin?{path:"/my/board"}:e.meta.admin&&!Ce.isAdmin?{path:"/my/board"}:!0:{path:"/login",query:{redirect:e.fullPath}}));ia(Ou).use(Do).mount("#app");export{Fu as A,Oe as B,jt as C,Uu as D,Zs as E,tt as F,Vu as G,Xc as H,Mn as I,Yt as J,Eu as S,wu as _,Tr as a,mt as b,No as c,Bu as d,Hu as e,Ds as f,Cr as g,Ce as h,Ke as i,pu as j,ju as k,ie as l,Ft as m,Yc as n,on as o,Wi as p,Vt as q,dl as r,Ks as s,Rt as t,ae as u,jr as v,Du as w,Lu as x,Ss as y,_r as z};
diff --git a/frontend/dist/assets/layout-dashboard-DNjo1OkM.js b/frontend/dist/assets/layout-dashboard-DplDGQ9R.js
similarity index 91%
rename from frontend/dist/assets/layout-dashboard-DNjo1OkM.js
rename to frontend/dist/assets/layout-dashboard-DplDGQ9R.js
index 3787700..45e69a8 100644
--- a/frontend/dist/assets/layout-dashboard-DNjo1OkM.js
+++ b/frontend/dist/assets/layout-dashboard-DplDGQ9R.js
@@ -1,4 +1,4 @@
-import{c as t}from"./index-rfeHzF-F.js";/**
+import{c as t}from"./index-yErsUbD6.js";/**
* @license lucide-vue-next v1.0.0 - ISC
*
* This source code is licensed under the ISC license.
diff --git a/frontend/dist/assets/lock-BhsUQe9Y.js b/frontend/dist/assets/lock-CCBV2bwv.js
similarity index 88%
rename from frontend/dist/assets/lock-BhsUQe9Y.js
rename to frontend/dist/assets/lock-CCBV2bwv.js
index 51c2da2..711a29f 100644
--- a/frontend/dist/assets/lock-BhsUQe9Y.js
+++ b/frontend/dist/assets/lock-CCBV2bwv.js
@@ -1,4 +1,4 @@
-import{c as e}from"./index-rfeHzF-F.js";/**
+import{c as e}from"./index-yErsUbD6.js";/**
* @license lucide-vue-next v1.0.0 - ISC
*
* This source code is licensed under the ISC license.
diff --git a/frontend/dist/assets/plus-GGHS2V4y.js b/frontend/dist/assets/plus-Y1qXSgej.js
similarity index 86%
rename from frontend/dist/assets/plus-GGHS2V4y.js
rename to frontend/dist/assets/plus-Y1qXSgej.js
index 7255978..0094a36 100644
--- a/frontend/dist/assets/plus-GGHS2V4y.js
+++ b/frontend/dist/assets/plus-Y1qXSgej.js
@@ -1,4 +1,4 @@
-import{c as e}from"./index-rfeHzF-F.js";/**
+import{c as e}from"./index-yErsUbD6.js";/**
* @license lucide-vue-next v1.0.0 - ISC
*
* This source code is licensed under the ISC license.
diff --git a/frontend/dist/assets/sparkles-DyyL2g7W.js b/frontend/dist/assets/sparkles-DyyL2g7W.js
deleted file mode 100644
index 238bc8d..0000000
--- a/frontend/dist/assets/sparkles-DyyL2g7W.js
+++ /dev/null
@@ -1,6 +0,0 @@
-import{c as a}from"./index-rfeHzF-F.js";/**
- * @license lucide-vue-next v1.0.0 - ISC
- *
- * This source code is licensed under the ISC license.
- * See the LICENSE file in the root directory of this source tree.
- */const l=a("sparkles",[["path",{d:"M11.017 2.814a1 1 0 0 1 1.966 0l1.051 5.558a2 2 0 0 0 1.594 1.594l5.558 1.051a1 1 0 0 1 0 1.966l-5.558 1.051a2 2 0 0 0-1.594 1.594l-1.051 5.558a1 1 0 0 1-1.966 0l-1.051-5.558a2 2 0 0 0-1.594-1.594l-5.558-1.051a1 1 0 0 1 0-1.966l5.558-1.051a2 2 0 0 0 1.594-1.594z",key:"1s2grr"}],["path",{d:"M20 2v4",key:"1rf3ol"}],["path",{d:"M22 4h-4",key:"gwowj6"}],["circle",{cx:"4",cy:"20",r:"2",key:"6kqj1y"}]]);export{l as S};
diff --git a/frontend/dist/assets/target-CLzEfHJ7.js b/frontend/dist/assets/target-DmESUw9c.js
similarity index 90%
rename from frontend/dist/assets/target-CLzEfHJ7.js
rename to frontend/dist/assets/target-DmESUw9c.js
index 3a24e19..5709f8b 100644
--- a/frontend/dist/assets/target-CLzEfHJ7.js
+++ b/frontend/dist/assets/target-DmESUw9c.js
@@ -1,4 +1,4 @@
-import{c}from"./index-rfeHzF-F.js";/**
+import{c}from"./index-yErsUbD6.js";/**
* @license lucide-vue-next v1.0.0 - ISC
*
* This source code is licensed under the ISC license.
diff --git a/frontend/dist/assets/users-BgT8ke9W.js b/frontend/dist/assets/users-BK4yWB6N.js
similarity index 90%
rename from frontend/dist/assets/users-BgT8ke9W.js
rename to frontend/dist/assets/users-BK4yWB6N.js
index 291f529..57d7671 100644
--- a/frontend/dist/assets/users-BgT8ke9W.js
+++ b/frontend/dist/assets/users-BK4yWB6N.js
@@ -1,4 +1,4 @@
-import{c as e}from"./index-rfeHzF-F.js";/**
+import{c as e}from"./index-yErsUbD6.js";/**
* @license lucide-vue-next v1.0.0 - ISC
*
* This source code is licensed under the ISC license.
diff --git a/frontend/dist/index.html b/frontend/dist/index.html
index 50f54ef..d753a1a 100644
--- a/frontend/dist/index.html
+++ b/frontend/dist/index.html
@@ -4,7 +4,7 @@
Sales Trainer
-
+
diff --git a/frontend/src/views/GroupEdit.vue b/frontend/src/views/GroupEdit.vue
index 95da435..99c0b94 100644
--- a/frontend/src/views/GroupEdit.vue
+++ b/frontend/src/views/GroupEdit.vue
@@ -3,11 +3,6 @@
← {{ i18n.t('training') }}