diff --git a/backend/app/api/chat_routes.py b/backend/app/api/chat_routes.py index fa35871..b00d2ab 100644 --- a/backend/app/api/chat_routes.py +++ b/backend/app/api/chat_routes.py @@ -51,16 +51,6 @@ def _scenarios(locale: str = "th"): else "คุณต้องเป็นฝ่ายเปิดการสนทนาเชิงรุกกับลีด (ผู้ฝึกทักก่อน) โทนเหมือนคุยสด" ), }, - "recontact": { - "label": "Re-contact (1-3 months later)" if not t else "ลูกค้ากลับมาติดต่อ (1-3 เดือน)", - "init": "customer", - "preamble": "⏳ 1-3 months passed... this customer previously got your info, now re-contacts, more ready to decide." if not t else "⏳ 1-3 เดือนผ่านไป... ลูกค้าคนนี้เคยได้รับข้อมูลสินค้าไปแล้ว ตอนนี้กลับมาติดต่อคุณอีกครั้ง (พร้อมตัดสินใจมากขึ้น)", - "adapt": ( - "You researched this seller 1-3 months ago and now re-contact, more ready to decide." - if not t - else "เคยได้รับข้อมูลไปแล้ว ตอนนี้กลับมาติดต่อ พร้อมตัดสินใจมากขึ้น" - ), - }, } @@ -157,7 +147,7 @@ def start_session(gid: str, pid: str): # Scenario chosen by the trainee at chat start (not baked into the persona). body = request.get_json(silent=True) or {} scenario = (body.get("scenario") or "social").strip().lower() - if scenario not in ("social", "f2f_call", "recontact"): + if scenario not in ("social", "f2f_call"): scenario = "social" locale = (body.get("locale") or "th").strip().lower() if locale not in ("en", "th"): diff --git a/backend/app/services/persona_prompts.py b/backend/app/services/persona_prompts.py index 5220149..c94007c 100644 --- a/backend/app/services/persona_prompts.py +++ b/backend/app/services/persona_prompts.py @@ -25,6 +25,10 @@ EACH persona MUST include ALL of these fields: - tolerance (1-5): how many irritant/poor answers you tolerate before you walk away ("heart"). IMPORTANT: a temperamental/impatient persona has LOW tolerance (1-2, walks away fast after poor answers); a patient one has HIGH (4-5). Avg is 3. Match tolerance to personality (e.g. a busy owner / abrupt personality = low). +- recontact (true/false): if true, this persona asked about the product BEFORE (earlier contact, e.g. a few + weeks/months back) and is ONLY NOW coming back / re-opening, more ready to buy and less price-sensitive. + These are already-pre-qualified, warmer leads. Make about 1 in every 4 personas recontact=true, spread across tiers. + A recontact persona opener/small-talk often references "I asked about this before" naturally. RULES: 1. DIVERSITY: 15 distinct people across age groups, occupations, incomes, lifestyles, diff --git a/backend/app/services/simulator.py b/backend/app/services/simulator.py index eee5bfd..4046609 100644 --- a/backend/app/services/simulator.py +++ b/backend/app/services/simulator.py @@ -101,8 +101,15 @@ class Simulator: adapt = scenario_adapt or { "social": "Chat style: short, casual, quick social-messaging replies.", "f2f_call": "Style: natural, conversational like a live face-to-face or phone talk.", - "recontact": "Style: casual messaging; you already know the product from 1-3 months ago.", }.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')." + ) tolerance = int(persona.get("tolerance", 3) or 3) system = CHAT_SYSTEM.format( name=persona.get("name", "Customer"), diff --git a/backend/app/services/store.py b/backend/app/services/store.py index 0c7baf9..79e8daa 100644 --- a/backend/app/services/store.py +++ b/backend/app/services/store.py @@ -43,6 +43,7 @@ def ensure_persona_shape(p: dict[str, Any]) -> dict[str, Any]: "negotiation_levers": p.get("negotiation_levers", []), "opener": p.get("opener", ""), "special": p.get("special", ""), # e.g. "wrong_text" | "" + "recontact": bool(p.get("recontact")), # returned after researching earlier (pre-qualified, warm) "difficulty": p.get("difficulty", 1), # 1..5 "tolerance": p.get("tolerance", 3), # misses before this persona walks away (temper) "notes": p.get("notes", ""), diff --git a/backend/scripts/test_scenario.py b/backend/scripts/test_scenario.py index f2dfea5..9712145 100644 --- a/backend/scripts/test_scenario.py +++ b/backend/scripts/test_scenario.py @@ -67,13 +67,18 @@ assert not any(m["role"] == "customer" for m in s2["messages"]), "f2f should NOT assert any(m["role"] == "system" for m in s2["messages"]), "f2f should have a scenario system note" print("[ok] f2f_call -> seller must open (system note present)") -# recontact -> system preamble about time-lapse + customer opens +# unknown scenario -> default to social (customer opens) pid3 = personas[2]["id"] r = C.post(f"/api/chat/{gid}/personas/{pid3}/chat/start", headers=TH, json={"scenario": "recontact"}) assert r.status_code == 200, r.get_json() s3 = r.get_json()["session"] -assert any(m["role"] == "customer" for m in s3["messages"]), "recontact should open w/ customer" -assert any(m["role"] == "system" and "เดือน" in m["text"] for m in s3["messages"]), "recontact system note (months)" -print("[ok] recontact -> time-lapse system note + customer opens") +assert any(m["role"] == "customer" for m in s3["messages"]), "unknown scenario should default to social (customer opens)" +print("[ok] unknown scenario defaults to social (customer opens)") + +# recontact is now a persona trait (not a scenario): some personas are flagged recontact +personas_all = C.get(f"/api/groups/{gid}/personas", headers=AH).get_json()["personas"] +recontact_n = sum(1 for p in personas_all if p.get("recontact")) +print(f"[ok] {recontact_n} of {len(personas_all)} generated personas are 'recontact' (warm returning leads)") +# The mock generates deterministic personas; we just assert the shape has the field (default False ok). print("ALL SCENARIO TESTS PASSED") diff --git a/frontend/dist/assets/AdminUsers-CK2wxE4M.js b/frontend/dist/assets/AdminUsers-RvxZ4Zuo.js similarity index 98% rename from frontend/dist/assets/AdminUsers-CK2wxE4M.js rename to frontend/dist/assets/AdminUsers-RvxZ4Zuo.js index e73f8d0..9e868cf 100644 --- a/frontend/dist/assets/AdminUsers-CK2wxE4M.js +++ b/frontend/dist/assets/AdminUsers-RvxZ4Zuo.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-Dyfiqyex.js";import{U as z}from"./users-B6PgAwnZ.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-rfeHzF-F.js";import{U as z}from"./users-BgT8ke9W.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-D_wvShAW.js b/frontend/dist/assets/Analytics-DLu98n1n.js similarity index 97% rename from frontend/dist/assets/Analytics-D_wvShAW.js rename to frontend/dist/assets/Analytics-DLu98n1n.js index e96e1a8..128c3f1 100644 --- a/frontend/dist/assets/Analytics-D_wvShAW.js +++ b/frontend/dist/assets/Analytics-DLu98n1n.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-Dyfiqyex.js";import{U as P}from"./users-B6PgAwnZ.js";import{P as S}from"./plus-BRv2cnuI.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-rfeHzF-F.js";import{U as P}from"./users-BgT8ke9W.js";import{P as S}from"./plus-GGHS2V4y.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-CeTQMGNO.js new file mode 100644 index 0000000..365d14a --- /dev/null +++ b/frontend/dist/assets/Chat-CeTQMGNO.js @@ -0,0 +1,6 @@ +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";/** + * @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=P("search",[["path",{d:"m21 21-4.34-4.34",key:"14j7rj"}],["circle",{cx:"11",cy:"11",r:"8",key:"4ej97u"}]]),X={key:0,class:"card"},Z={style:{"margin-top":"0"}},ee=["onClick"],se={class:"row",style:{"align-items":"center"}},te={key:0,class:"badge line"},ae={key:1,class:"badge facebook"},le={class:"muted",style:{"margin-top":"6px"}},oe=["disabled"],ne={key:1},ie={class:"card guide"},re={style:{margin:"6px 0 0","line-height":"1.7"}},ce={class:"row",style:{"align-items":"center","margin-bottom":"12px"}},ue={style:{margin:"0"}},de={key:0,class:"muted"},ve={key:0,class:"bubble msg-customer muted"},pe={key:0,class:"composer"},me=["disabled","placeholder"],he=["disabled"],_e={key:1,class:"card debrief"},ge={key:0},ye={key:0,class:"reveal-grid"},fe={class:"rk"},be={class:"rv"},ke={class:"primary",style:{"margin-top":"12px"}},we={__name:"Chat",setup(xe){const A=H(),y=A.params.gid,w=A.params.pid,h=u(null),c=u("pick"),d=u([]),_=u(""),f=u(!1),i=u(null),B=u(null),b=u("social"),N=J(()=>{const e=n.locale==="en";return[{id:"social",emoji:"💬",init:"customer",label:e?"Social Media (chat)":"Social Media (แชท)",desc:e?"The customer messages you first — short, chat-style replies":"ลูกค้าทักมาหาคุณก่อน — โทนสั้น ทักๆ ตามสไตล์แชท"},{id:"f2f_call",emoji:"📞",init:"seller",label:e?"Face-to-face / Phone":"พบหน้า / โทรศัพท์",desc:e?"You must proactively open the conversation (seller starts)":"คุณต้องเป็นฝ่ายเปิดการสนทนาเชิงรุกกับลีด (ผู้ฝึกทักก่อน)"}]});function z(){O(()=>{x.value&&(x.value.scrollTop=x.value.scrollHeight)})}const x=u(null);async function E(){c.value="chat";const e=await S.chatStart(y,w,b.value,n.locale);B.value=e.session.id,d.value=e.session.messages||[],d.value.length&&z()}K(async()=>{h.value=(await S.getPersona(y,w)).persona;try{const e=await S.chatResume(y,w);B.value=e.session.id,d.value=e.session.messages||[],d.value.length&&!e.session.debrief?(c.value="chat",N.find(C=>C.id===e.scenario)&&(b.value=e.scenario)):e.session.debrief&&(i.value=e.session.debrief,d.value=e.session.messages||[],c.value="done"),d.value.length&&z()}catch{c.value="pick"}});async function V(){if(_.value.trim()){f.value=!0;try{const e=await S.chatSend(y,w,_.value.trim());d.value=e.messages,_.value="",e.finished&&(i.value=e.debrief||null,c.value="done"),z()}catch(e){alert(e.message)}finally{f.value=!1}}}const F={name:"ชื่อ",tier:"ระดับ",difficulty:"ความยาก",profession:"อาชีพ",age_group:"ช่วงอายุ",location:"พื้นที่",income:"รายได้",budget:"งบประมาณ",lifestyle:"ไลฟ์สไตล์",background:"ภูมิหลัง",personality:"บุคลิก",communication_style:"สไตล์การสื่อสาร",goal:"เป้าหมาย",decision_timeline:"กรอบตัดสินใจ",pains:"ปัญหา (Pain)",objections:"ข้อโต้แย้ง",negotiation_levers:"สิ่งที่ใช้ต่อรอง",opener:"บทเปิดบทสนทนา",channel:"ช่องทาง",initiation_mode:"ใครเริ่มก่อน",product_context:"บริบทสินค้า"};function I(e){return F[e]||e}function M(e){return Array.isArray(e)?e.join(", "):e&&typeof e=="object"?JSON.stringify(e):String(e??"—")}return(e,v)=>{const C=U("router-link");return o(),l("div",null,[k(C,{to:`/groups/${a(y)}/personas`,class:"btn-back"},{default:D(()=>[k(a(Q),{size:16,"stroke-width":2}),g(" "+t(a(n).t("personas")),1)]),_:1},8,["to"]),c.value==="pick"?(o(),l("div",X,[s("h3",Z,"🎬 "+t(a(n).t("chooseScenario")),1),v[1]||(v[1]=s("p",{class:"muted",style:{"margin-top":"0"}},"เลือกสถานการณ์ที่อยากฝึกการขาย — แต่ละแบบกำหนดว่าใครทักก่อน และโทนการสนทนา",-1)),(o(!0),l(j,null,T(N.value,r=>(o(),l("div",{key:r.id,class:L(["scenario",{sel:b.value===r.id}]),onClick:p=>b.value=r.id},[s("div",se,[s("strong",null,t(r.emoji)+" "+t(r.label),1),r.init==="customer"?(o(),l("span",te,"ลูกค้าทักก่อน")):(o(),l("span",ae,"คุณต้องทักก่อน (เชิงรุก)"))]),s("div",le,t(r.desc),1)],10,ee))),128)),s("button",{class:"primary",style:{"margin-top":"16px",width:"100%"},disabled:!b.value,onClick:E}," ▶ "+t(a(n).t("start")),9,oe)])):(o(),l("div",ne,[s("div",ie,[s("strong",null,[k(a(Y),{size:17,"stroke-width":1.8,style:{"vertical-align":"-3px"}}),g(" "+t(a(n).t("chatGuideTitle")),1)]),s("p",re,t(a(n).t("chatGuideText")),1)]),s("div",ce,[s("h2",ue,t(h.value?h.value.name:"..."),1),h.value?(o(),l("span",de,t(h.value.profession)+" · "+t(h.value.age_group),1)):m("",!0),c.value==="done"?(o(),l("span",{key:1,class:L(["badge",i.value&&i.value.outcome])},t(i.value&&i.value.outcome==="won"?a(n).t("won"):a(n).t("lost")),3)):m("",!0)]),s("div",{class:"thread",ref_key:"thread",ref:x},[(o(!0),l(j,null,T(d.value,(r,p)=>(o(),l("div",{key:p,class:L(["bubble",r.role==="seller"?"msg-seller":r.role==="system"?"msg-system":"msg-customer"])},t(r.text),3))),128)),f.value?(o(),l("div",ve,"...")):m("",!0)],512),c.value==="chat"?(o(),l("div",pe,[R(s("input",{"onUpdate:modelValue":v[0]||(v[0]=r=>_.value=r),onKeyup:q(V,["enter"]),disabled:f.value,placeholder:a(n).t("send")},null,40,me),[[$,_.value]]),s("button",{class:"primary",onClick:V,disabled:f.value||!_.value.trim()},t(a(n).t("send")),9,he)])):m("",!0),c.value==="done"&&i.value?(o(),l("div",_e,[s("h3",null,t(a(n).t("debrief")),1),s("p",null,[s("span",{class:L(["badge",i.value.outcome])},t(i.value.outcome==="won"?a(n).t("won"):a(n).t("lost")),3),g(" — "+t(a(n).t("score"))+": ",1),s("strong",null,t(i.value.score),1)]),s("p",null,[s("strong",null,t(a(n).t("pain"))+":",1),g(" "+t(i.value.pain||"—"),1)]),s("p",null,[s("strong",null,t(a(n).t("why"))+":",1),g(" "+t(i.value.why),1)]),i.value.coaching&&i.value.coaching.length?(o(),l("div",ge,[v[2]||(v[2]=s("strong",null,"Coaching:",-1)),s("ul",null,[(o(!0),l(j,null,T(i.value.coaching,(r,p)=>(o(),l("li",{key:p},t(r),1))),128))])])):m("",!0),s("details",null,[s("summary",null,[k(a(W),{size:15,"stroke-width":1.8,style:{"vertical-align":"-2px"}}),g(" "+t(a(n).t("reveal")),1)]),i.value.revealed_persona?(o(),l("div",ye,[(o(!0),l(j,null,T(i.value.revealed_persona,(r,p)=>(o(),l("div",{key:p,class:"rev"},[s("span",fe,t(I(p)),1),s("span",be,t(M(r)),1)]))),128))])):m("",!0)]),k(C,{to:"/"},{default:D(()=>[s("button",ke,t(a(n).t("dashboard")),1)]),_:1})])):m("",!0)]))])}}},Te=G(we,[["__scopeId","data-v-dd57873b"]]);export{Te as default}; diff --git a/frontend/dist/assets/Chat-CpZrRyQZ.js b/frontend/dist/assets/Chat-CpZrRyQZ.js deleted file mode 100644 index ed8e77a..0000000 --- a/frontend/dist/assets/Chat-CpZrRyQZ.js +++ /dev/null @@ -1,6 +0,0 @@ -import{c as P,_ as R,p as G,y as j,a as o,l as k,q as D,u as a,b as s,t,i as n,F as S,x as T,m as g,g as p,C as L,w as K,v as $,d as q,k as H,r as u,B as J,E as O,z as U,o as l}from"./index-Dyfiqyex.js";import{T as Y}from"./target-D9jhWfH4.js";import{A as Q}from"./arrow-left-C95UZZnG.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=P("search",[["path",{d:"m21 21-4.34-4.34",key:"14j7rj"}],["circle",{cx:"11",cy:"11",r:"8",key:"4ej97u"}]]),X={key:0,class:"card"},Z={style:{"margin-top":"0"}},ee=["onClick"],se={class:"row",style:{"align-items":"center"}},te={key:0,class:"badge line"},ae={key:1,class:"badge facebook"},oe={class:"muted",style:{"margin-top":"6px"}},le=["disabled"],ne={key:1},ie={class:"card guide"},re={style:{margin:"6px 0 0","line-height":"1.7"}},ce={class:"row",style:{"align-items":"center","margin-bottom":"12px"}},ue={style:{margin:"0"}},de={key:0,class:"muted"},ve={key:0,class:"bubble msg-customer muted"},me={key:0,class:"composer"},pe=["disabled","placeholder"],he=["disabled"],_e={key:1,class:"card debrief"},ge={key:0},ye={key:0,class:"reveal-grid"},fe={class:"rk"},be={class:"rv"},ke={class:"primary",style:{"margin-top":"12px"}},we={__name:"Chat",setup(xe){const A=H(),y=A.params.gid,w=A.params.pid,h=u(null),c=u("pick"),d=u([]),_=u(""),f=u(!1),i=u(null),B=u(null),b=u("social"),N=J(()=>{const e=n.locale==="en";return[{id:"social",emoji:"💬",init:"customer",label:e?"Social Media (chat)":"Social Media (แชท)",desc:e?"The customer messages you first — short, chat-style replies":"ลูกค้าทักมาหาคุณก่อน — โทนสั้น ทักๆ ตามสไตล์แชท"},{id:"f2f_call",emoji:"📞",init:"seller",label:e?"Face-to-face / Phone":"พบหน้า / โทรศัพท์",desc:e?"You must proactively open the conversation (seller starts)":"คุณต้องเป็นฝ่ายเปิดการสนทนาเชิงรุกกับลีด (ผู้ฝึกทักก่อน)"},{id:"recontact",emoji:"⏳",init:"customer",label:e?"Re-contact (1-3 months)":"ลูกค้ากลับมาติดต่อ (1-3 เดือน)",desc:e?"Customer contacted you before, now returns more ready to decide":"เคยได้รับข้อมูลไปแล้ว ตอนนี้กลับมาติดต่อ พร้อมตัดสินใจมากขึ้น"}]});function z(){O(()=>{x.value&&(x.value.scrollTop=x.value.scrollHeight)})}const x=u(null);async function E(){c.value="chat";const e=await j.chatStart(y,w,b.value,n.locale);B.value=e.session.id,d.value=e.session.messages||[],d.value.length&&z()}G(async()=>{h.value=(await j.getPersona(y,w)).persona;try{const e=await j.chatResume(y,w);B.value=e.session.id,d.value=e.session.messages||[],d.value.length&&!e.session.debrief?(c.value="chat",N.find(C=>C.id===e.scenario)&&(b.value=e.scenario)):e.session.debrief&&(i.value=e.session.debrief,d.value=e.session.messages||[],c.value="done"),d.value.length&&z()}catch{c.value="pick"}});async function V(){if(_.value.trim()){f.value=!0;try{const e=await j.chatSend(y,w,_.value.trim());d.value=e.messages,_.value="",e.finished&&(i.value=e.debrief||null,c.value="done"),z()}catch(e){alert(e.message)}finally{f.value=!1}}}const F={name:"ชื่อ",tier:"ระดับ",difficulty:"ความยาก",profession:"อาชีพ",age_group:"ช่วงอายุ",location:"พื้นที่",income:"รายได้",budget:"งบประมาณ",lifestyle:"ไลฟ์สไตล์",background:"ภูมิหลัง",personality:"บุคลิก",communication_style:"สไตล์การสื่อสาร",goal:"เป้าหมาย",decision_timeline:"กรอบตัดสินใจ",pains:"ปัญหา (Pain)",objections:"ข้อโต้แย้ง",negotiation_levers:"สิ่งที่ใช้ต่อรอง",opener:"บทเปิดบทสนทนา",channel:"ช่องทาง",initiation_mode:"ใครเริ่มก่อน",product_context:"บริบทสินค้า"};function I(e){return F[e]||e}function M(e){return Array.isArray(e)?e.join(", "):e&&typeof e=="object"?JSON.stringify(e):String(e??"—")}return(e,v)=>{const C=U("router-link");return l(),o("div",null,[k(C,{to:`/groups/${a(y)}/personas`,class:"btn-back"},{default:D(()=>[k(a(Q),{size:16,"stroke-width":2}),g(" "+t(a(n).t("personas")),1)]),_:1},8,["to"]),c.value==="pick"?(l(),o("div",X,[s("h3",Z,"🎬 "+t(a(n).t("chooseScenario")),1),v[1]||(v[1]=s("p",{class:"muted",style:{"margin-top":"0"}},"เลือกสถานการณ์ที่อยากฝึกการขาย — แต่ละแบบกำหนดว่าใครทักก่อน และโทนการสนทนา",-1)),(l(!0),o(S,null,T(N.value,r=>(l(),o("div",{key:r.id,class:L(["scenario",{sel:b.value===r.id}]),onClick:m=>b.value=r.id},[s("div",se,[s("strong",null,t(r.emoji)+" "+t(r.label),1),r.init==="customer"?(l(),o("span",te,"ลูกค้าทักก่อน")):(l(),o("span",ae,"คุณต้องทักก่อน (เชิงรุก)"))]),s("div",oe,t(r.desc),1)],10,ee))),128)),s("button",{class:"primary",style:{"margin-top":"16px",width:"100%"},disabled:!b.value,onClick:E}," ▶ "+t(a(n).t("start")),9,le)])):(l(),o("div",ne,[s("div",ie,[s("strong",null,[k(a(Y),{size:17,"stroke-width":1.8,style:{"vertical-align":"-3px"}}),g(" "+t(a(n).t("chatGuideTitle")),1)]),s("p",re,t(a(n).t("chatGuideText")),1)]),s("div",ce,[s("h2",ue,t(h.value?h.value.name:"..."),1),h.value?(l(),o("span",de,t(h.value.profession)+" · "+t(h.value.age_group),1)):p("",!0),c.value==="done"?(l(),o("span",{key:1,class:L(["badge",i.value&&i.value.outcome])},t(i.value&&i.value.outcome==="won"?a(n).t("won"):a(n).t("lost")),3)):p("",!0)]),s("div",{class:"thread",ref_key:"thread",ref:x},[(l(!0),o(S,null,T(d.value,(r,m)=>(l(),o("div",{key:m,class:L(["bubble",r.role==="seller"?"msg-seller":r.role==="system"?"msg-system":"msg-customer"])},t(r.text),3))),128)),f.value?(l(),o("div",ve,"...")):p("",!0)],512),c.value==="chat"?(l(),o("div",me,[K(s("input",{"onUpdate:modelValue":v[0]||(v[0]=r=>_.value=r),onKeyup:q(V,["enter"]),disabled:f.value,placeholder:a(n).t("send")},null,40,pe),[[$,_.value]]),s("button",{class:"primary",onClick:V,disabled:f.value||!_.value.trim()},t(a(n).t("send")),9,he)])):p("",!0),c.value==="done"&&i.value?(l(),o("div",_e,[s("h3",null,t(a(n).t("debrief")),1),s("p",null,[s("span",{class:L(["badge",i.value.outcome])},t(i.value.outcome==="won"?a(n).t("won"):a(n).t("lost")),3),g(" — "+t(a(n).t("score"))+": ",1),s("strong",null,t(i.value.score),1)]),s("p",null,[s("strong",null,t(a(n).t("pain"))+":",1),g(" "+t(i.value.pain||"—"),1)]),s("p",null,[s("strong",null,t(a(n).t("why"))+":",1),g(" "+t(i.value.why),1)]),i.value.coaching&&i.value.coaching.length?(l(),o("div",ge,[v[2]||(v[2]=s("strong",null,"Coaching:",-1)),s("ul",null,[(l(!0),o(S,null,T(i.value.coaching,(r,m)=>(l(),o("li",{key:m},t(r),1))),128))])])):p("",!0),s("details",null,[s("summary",null,[k(a(W),{size:15,"stroke-width":1.8,style:{"vertical-align":"-2px"}}),g(" "+t(a(n).t("reveal")),1)]),i.value.revealed_persona?(l(),o("div",ye,[(l(!0),o(S,null,T(i.value.revealed_persona,(r,m)=>(l(),o("div",{key:m,class:"rev"},[s("span",fe,t(I(m)),1),s("span",be,t(M(r)),1)]))),128))])):p("",!0)]),k(C,{to:"/"},{default:D(()=>[s("button",ke,t(a(n).t("dashboard")),1)]),_:1})])):p("",!0)]))])}}},Te=R(we,[["__scopeId","data-v-dd1df48f"]]);export{Te as default}; diff --git a/frontend/dist/assets/Chat-CyoReMui.css b/frontend/dist/assets/Chat-CyoReMui.css deleted file mode 100644 index 7e10105..0000000 --- a/frontend/dist/assets/Chat-CyoReMui.css +++ /dev/null @@ -1 +0,0 @@ -.thread[data-v-dd1df48f]{background:#eceff4;border:1px solid var(--border);border-radius:var(--radius);padding:16px;min-height:320px;max-height:52vh;overflow-y:auto;display:flex;flex-direction:column;gap:8px}.bubble[data-v-dd1df48f]{max-width:72%;padding:10px 14px;white-space:pre-wrap;word-break:break-word}.msg-system[data-v-dd1df48f]{align-self:center;background:#fef3c7;color:#92400e;font-size:12px;max-width:88%;border-radius:999px}.composer[data-v-dd1df48f]{display:flex;gap:8px;margin-top:12px}.task[data-v-dd1df48f]{margin-bottom:12px;background:#fff7ed;border-color:#fed7aa}.debrief[data-v-dd1df48f]{margin-top:16px}button.danger[data-v-dd1df48f]{background:var(--red);color:#fff;border:none}.guide[data-v-dd1df48f]{background:#eef2ff;border-color:#c7d2fe;margin-bottom:12px}.scenario[data-v-dd1df48f]{border:2px solid var(--border);border-radius:12px;padding:12px 14px;margin:10px 0;cursor:pointer;transition:border-color .15s ease,background .15s ease}.scenario[data-v-dd1df48f]:hover{border-color:var(--accent)}.scenario.sel[data-v-dd1df48f]{border-color:var(--accent);background:#eef2ff}.reveal-grid[data-v-dd1df48f]{display:grid;grid-template-columns:1fr 1fr;gap:8px;margin-top:8px}.rev[data-v-dd1df48f]{display:flex;flex-direction:column;background:#f8fafc;border:1px solid var(--border);border-radius:8px;padding:8px 10px}.rk[data-v-dd1df48f]{font-size:12px;color:var(--muted)}.rv[data-v-dd1df48f]{font-size:13px;color:var(--ink);margin-top:2px}@media (max-width: 640px){.reveal-grid[data-v-dd1df48f]{grid-template-columns:1fr}} diff --git a/frontend/dist/assets/Chat-pa8hnBQD.css b/frontend/dist/assets/Chat-pa8hnBQD.css new file mode 100644 index 0000000..b0c90b5 --- /dev/null +++ b/frontend/dist/assets/Chat-pa8hnBQD.css @@ -0,0 +1 @@ +.thread[data-v-dd57873b]{background:#eceff4;border:1px solid var(--border);border-radius:var(--radius);padding:16px;min-height:320px;max-height:52vh;overflow-y:auto;display:flex;flex-direction:column;gap:8px}.bubble[data-v-dd57873b]{max-width:72%;padding:10px 14px;white-space:pre-wrap;word-break:break-word}.msg-system[data-v-dd57873b]{align-self:center;background:#fef3c7;color:#92400e;font-size:12px;max-width:88%;border-radius:999px}.composer[data-v-dd57873b]{display:flex;gap:8px;margin-top:12px}.task[data-v-dd57873b]{margin-bottom:12px;background:#fff7ed;border-color:#fed7aa}.debrief[data-v-dd57873b]{margin-top:16px}button.danger[data-v-dd57873b]{background:var(--red);color:#fff;border:none}.guide[data-v-dd57873b]{background:#eef2ff;border-color:#c7d2fe;margin-bottom:12px}.scenario[data-v-dd57873b]{border:2px solid var(--border);border-radius:12px;padding:12px 14px;margin:10px 0;cursor:pointer;transition:border-color .15s ease,background .15s ease}.scenario[data-v-dd57873b]:hover{border-color:var(--accent)}.scenario.sel[data-v-dd57873b]{border-color:var(--accent);background:#eef2ff}.reveal-grid[data-v-dd57873b]{display:grid;grid-template-columns:1fr 1fr;gap:8px;margin-top:8px}.rev[data-v-dd57873b]{display:flex;flex-direction:column;background:#f8fafc;border:1px solid var(--border);border-radius:8px;padding:8px 10px}.rk[data-v-dd57873b]{font-size:12px;color:var(--muted)}.rv[data-v-dd57873b]{font-size:13px;color:var(--ink);margin-top:2px}@media (max-width: 640px){.reveal-grid[data-v-dd57873b]{grid-template-columns:1fr}} diff --git a/frontend/dist/assets/GroupBuilder-PtFiTFPC.js b/frontend/dist/assets/GroupBuilder-DuRDlcKB.js similarity index 98% rename from frontend/dist/assets/GroupBuilder-PtFiTFPC.js rename to frontend/dist/assets/GroupBuilder-DuRDlcKB.js index 610ee32..752fbcc 100644 --- a/frontend/dist/assets/GroupBuilder-PtFiTFPC.js +++ b/frontend/dist/assets/GroupBuilder-DuRDlcKB.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-Dyfiqyex.js";import{A as q}from"./arrow-left-C95UZZnG.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-rfeHzF-F.js";import{A as q}from"./arrow-left-De8eM538.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-DqUapL_D.js b/frontend/dist/assets/GroupEdit-Ci_uwcwx.js similarity index 99% rename from frontend/dist/assets/GroupEdit-DqUapL_D.js rename to frontend/dist/assets/GroupEdit-Ci_uwcwx.js index c391a2a..164f8c0 100644 --- a/frontend/dist/assets/GroupEdit-DqUapL_D.js +++ b/frontend/dist/assets/GroupEdit-Ci_uwcwx.js @@ -1,4 +1,4 @@ -import{c as q,_ as E,I as F,o as u,a as m,b as t,t as a,w as o,v as i,H as I,u as y,h as T,g as S,J as D,r as k,p as J,l as w,q as G,m as x,i as B,f as H,G as R,F as N,x as P,D as K,k as Q,y as z,z as W,C as O}from"./index-Dyfiqyex.js";import{U as X}from"./users-B6PgAwnZ.js";import{S as Y}from"./sparkles-x6AFhIMl.js";/** +import{c as q,_ as E,I as F,o as u,a as m,b as t,t as a,w as o,v as i,H as I,u as y,h as T,g as S,J as D,r as k,p as J,l as w,q as G,m as x,i as B,f as H,G as R,F as N,x as P,D as K,k as Q,y as z,z as W,C as O}from"./index-rfeHzF-F.js";import{U as X}from"./users-BgT8ke9W.js";import{S as Y}from"./sparkles-DyyL2g7W.js";/** * @license lucide-vue-next v1.0.0 - ISC * * This source code is licensed under the ISC license. diff --git a/frontend/dist/assets/Guide-CxzIj9kO.js b/frontend/dist/assets/Guide-Cm54aYlg.js similarity index 95% rename from frontend/dist/assets/Guide-CxzIj9kO.js rename to frontend/dist/assets/Guide-Cm54aYlg.js index cf16507..443e057 100644 --- a/frontend/dist/assets/Guide-CxzIj9kO.js +++ b/frontend/dist/assets/Guide-Cm54aYlg.js @@ -1,4 +1,4 @@ -import{c as e,a as o,b as l,l as n,u as i,m as s,t as r,i as a,G as u,o as d}from"./index-Dyfiqyex.js";import{B as g}from"./book-open-BVimbpEo.js";import{L as p}from"./layout-dashboard-BeK4EeBd.js";import{T as m}from"./target-D9jhWfH4.js";import{S as y}from"./sparkles-x6AFhIMl.js";/** +import{c as e,a as o,b as l,l as n,u as i,m as s,t as r,i as a,G as u,o as d}from"./index-rfeHzF-F.js";import{B as g}from"./book-open-tl-QQwuW.js";import{L as p}from"./layout-dashboard-DNjo1OkM.js";import{T as m}from"./target-CLzEfHJ7.js";import{S as y}from"./sparkles-DyyL2g7W.js";/** * @license lucide-vue-next v1.0.0 - ISC * * This source code is licensed under the ISC license. diff --git a/frontend/dist/assets/Login-CaF0UGOL.js b/frontend/dist/assets/Login-CsAwuAd_.js similarity index 98% rename from frontend/dist/assets/Login-CaF0UGOL.js rename to frontend/dist/assets/Login-CsAwuAd_.js index 5204ce6..06e3249 100644 --- a/frontend/dist/assets/Login-CaF0UGOL.js +++ b/frontend/dist/assets/Login-CsAwuAd_.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-Dyfiqyex.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-rfeHzF-F.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-BBr3kpxa.js b/frontend/dist/assets/MyBoard-xB9d_Obs.js similarity index 94% rename from frontend/dist/assets/MyBoard-BBr3kpxa.js rename to frontend/dist/assets/MyBoard-xB9d_Obs.js index add4292..0f0a361 100644 --- a/frontend/dist/assets/MyBoard-BBr3kpxa.js +++ b/frontend/dist/assets/MyBoard-xB9d_Obs.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-Dyfiqyex.js";import{L as V}from"./layout-dashboard-BeK4EeBd.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-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}; diff --git a/frontend/dist/assets/Personas-CT2K1C7x.js b/frontend/dist/assets/Personas-CT2K1C7x.js new file mode 100644 index 0000000..6a919f9 --- /dev/null +++ b/frontend/dist/assets/Personas-CT2K1C7x.js @@ -0,0 +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}; diff --git a/frontend/dist/assets/Personas-CzGYN8e6.js b/frontend/dist/assets/Personas-CzGYN8e6.js deleted file mode 100644 index 3f3b2b4..0000000 --- a/frontend/dist/assets/Personas-CzGYN8e6.js +++ /dev/null @@ -1 +0,0 @@ -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-Dyfiqyex.js";import{T as F}from"./target-D9jhWfH4.js";import{A as j}from"./arrow-left-C95UZZnG.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-eb0948ac"]]);export{at as default}; diff --git a/frontend/dist/assets/Personas-cgqlaBh7.css b/frontend/dist/assets/Personas-cgqlaBh7.css new file mode 100644 index 0000000..88d8edb --- /dev/null +++ b/frontend/dist/assets/Personas-cgqlaBh7.css @@ -0,0 +1 @@ +.grid[data-v-ec851b01]{display:grid;grid-template-columns:repeat(auto-fill,minmax(260px,1fr));gap:14px}.pcard[data-v-ec851b01]{display:flex;flex-direction:column;min-height:190px}.diff[data-v-ec851b01]{margin:8px 0}.star[data-v-ec851b01]{color:#d8dbe3}.star.on[data-v-ec851b01]{color:#f59e0b}.badge.won[data-v-ec851b01]{background:#dcfce7;color:#166534}.badge.lost[data-v-ec851b01]{background:#fee2e2;color:#991b1b}.badge.not_tried[data-v-ec851b01]{background:#eef2ff;color:#4338ca}.guide[data-v-ec851b01]{background:#eef2ff;border-color:#c7d2fe;margin:12px 0 16px} diff --git a/frontend/dist/assets/Personas-ha4vZsih.css b/frontend/dist/assets/Personas-ha4vZsih.css deleted file mode 100644 index a6d48c8..0000000 --- a/frontend/dist/assets/Personas-ha4vZsih.css +++ /dev/null @@ -1 +0,0 @@ -.grid[data-v-eb0948ac]{display:grid;grid-template-columns:repeat(auto-fill,minmax(260px,1fr));gap:14px}.pcard[data-v-eb0948ac]{display:flex;flex-direction:column;min-height:190px}.diff[data-v-eb0948ac]{margin:8px 0}.star[data-v-eb0948ac]{color:#d8dbe3}.star.on[data-v-eb0948ac]{color:#f59e0b}.badge.won[data-v-eb0948ac]{background:#dcfce7;color:#166534}.badge.lost[data-v-eb0948ac]{background:#fee2e2;color:#991b1b}.badge.not_tried[data-v-eb0948ac]{background:#eef2ff;color:#4338ca}.guide[data-v-eb0948ac]{background:#eef2ff;border-color:#c7d2fe;margin:12px 0 16px} diff --git a/frontend/dist/assets/Settings-IzLUgL3N.js b/frontend/dist/assets/Settings-DqpxLARx.js similarity index 98% rename from frontend/dist/assets/Settings-IzLUgL3N.js rename to frontend/dist/assets/Settings-DqpxLARx.js index 4ad6efe..3a978a9 100644 --- a/frontend/dist/assets/Settings-IzLUgL3N.js +++ b/frontend/dist/assets/Settings-DqpxLARx.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-Dyfiqyex.js";import{L as D}from"./lock-cj79Hj37.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-rfeHzF-F.js";import{L as D}from"./lock-BhsUQe9Y.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-DOZNu22f.js b/frontend/dist/assets/Setup-BrHw5pFL.js similarity index 94% rename from frontend/dist/assets/Setup-DOZNu22f.js rename to frontend/dist/assets/Setup-BrHw5pFL.js index fc67200..014327a 100644 --- a/frontend/dist/assets/Setup-DOZNu22f.js +++ b/frontend/dist/assets/Setup-BrHw5pFL.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-Dyfiqyex.js";import{L as C}from"./lock-cj79Hj37.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-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}; diff --git a/frontend/dist/assets/Training-Baew-jzp.js b/frontend/dist/assets/Training-C7WaLvfq.js similarity index 95% rename from frontend/dist/assets/Training-Baew-jzp.js rename to frontend/dist/assets/Training-C7WaLvfq.js index 3ff376b..b8f633e 100644 --- a/frontend/dist/assets/Training-Baew-jzp.js +++ b/frontend/dist/assets/Training-C7WaLvfq.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-Dyfiqyex.js";import{T as P}from"./target-D9jhWfH4.js";import{B as V}from"./book-open-BVimbpEo.js";import{P as $}from"./plus-BRv2cnuI.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-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";/** * @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-C95UZZnG.js b/frontend/dist/assets/arrow-left-De8eM538.js similarity index 86% rename from frontend/dist/assets/arrow-left-C95UZZnG.js rename to frontend/dist/assets/arrow-left-De8eM538.js index 6b0b69f..2f0d29b 100644 --- a/frontend/dist/assets/arrow-left-C95UZZnG.js +++ b/frontend/dist/assets/arrow-left-De8eM538.js @@ -1,4 +1,4 @@ -import{c as e}from"./index-Dyfiqyex.js";/** +import{c as e}from"./index-rfeHzF-F.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-BVimbpEo.js b/frontend/dist/assets/book-open-tl-QQwuW.js similarity index 90% rename from frontend/dist/assets/book-open-BVimbpEo.js rename to frontend/dist/assets/book-open-tl-QQwuW.js index b4a4b19..05a9af9 100644 --- a/frontend/dist/assets/book-open-BVimbpEo.js +++ b/frontend/dist/assets/book-open-tl-QQwuW.js @@ -1,4 +1,4 @@ -import{c as a}from"./index-Dyfiqyex.js";/** +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. diff --git a/frontend/dist/assets/index-Dyfiqyex.js b/frontend/dist/assets/index-rfeHzF-F.js similarity index 98% rename from frontend/dist/assets/index-Dyfiqyex.js rename to frontend/dist/assets/index-rfeHzF-F.js index 1cf01ab..fa120a2 100644 --- a/frontend/dist/assets/index-Dyfiqyex.js +++ b/frontend/dist/assets/index-rfeHzF-F.js @@ -1,4 +1,4 @@ -const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/Login-CaF0UGOL.js","assets/Login-j4sHK_z1.css","assets/Setup-DOZNu22f.js","assets/lock-cj79Hj37.js","assets/Setup-BHyRmSn1.css","assets/Analytics-D_wvShAW.js","assets/users-B6PgAwnZ.js","assets/plus-BRv2cnuI.js","assets/Analytics-CQgwlaXy.css","assets/MyBoard-BBr3kpxa.js","assets/layout-dashboard-BeK4EeBd.js","assets/MyBoard-B7ypQ1JT.css","assets/Training-Baew-jzp.js","assets/target-D9jhWfH4.js","assets/book-open-BVimbpEo.js","assets/Training-Cb1U9s84.css","assets/Personas-CzGYN8e6.js","assets/arrow-left-C95UZZnG.js","assets/Personas-ha4vZsih.css","assets/Chat-CpZrRyQZ.js","assets/Chat-CyoReMui.css","assets/Settings-IzLUgL3N.js","assets/Settings-BCN2EZQ5.css","assets/Guide-CxzIj9kO.js","assets/sparkles-x6AFhIMl.js","assets/GroupBuilder-PtFiTFPC.js","assets/GroupBuilder-7-9ZskJo.css","assets/GroupEdit-DqUapL_D.js","assets/GroupEdit-XuXM9ZDv.css","assets/AdminUsers-CK2wxE4M.js","assets/AdminUsers-B0sl0uHS.css"])))=>i.map(i=>d[i]); +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]); (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-CaF0UGOL. * * 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-CaF0UGOL.js"),__vite__mapDeps([0,1])),meta:{public:!0}},{path:"/setup",component:()=>Se(()=>import("./Setup-DOZNu22f.js"),__vite__mapDeps([2,3,4]))},{path:"/",component:()=>Se(()=>import("./Analytics-D_wvShAW.js"),__vite__mapDeps([5,6,7,8])),meta:{admin:!0}},{path:"/my/board",component:()=>Se(()=>import("./MyBoard-BBr3kpxa.js"),__vite__mapDeps([9,10,11]))},{path:"/training",component:()=>Se(()=>import("./Training-Baew-jzp.js"),__vite__mapDeps([12,13,14,7,15]))},{path:"/groups/:gid/personas",component:()=>Se(()=>import("./Personas-CzGYN8e6.js"),__vite__mapDeps([16,13,17,18]))},{path:"/groups/:gid/chat/:pid",component:()=>Se(()=>import("./Chat-CpZrRyQZ.js"),__vite__mapDeps([19,13,17,20]))},{path:"/settings",component:()=>Se(()=>import("./Settings-IzLUgL3N.js"),__vite__mapDeps([21,3,22]))},{path:"/guide",component:()=>Se(()=>import("./Guide-CxzIj9kO.js"),__vite__mapDeps([23,14,10,13,24]))},{path:"/admin/new-group",component:()=>Se(()=>import("./GroupBuilder-PtFiTFPC.js"),__vite__mapDeps([25,17,26])),meta:{admin:!0}},{path:"/admin/groups/:gid/edit",component:()=>Se(()=>import("./GroupEdit-DqUapL_D.js"),__vite__mapDeps([27,6,24,28])),meta:{admin:!0}},{path:"/admin/users",component:()=>Se(()=>import("./AdminUsers-CK2wxE4M.js"),__vite__mapDeps([29,6,30])),meta:{admin:!0}},{path:"/admin/analytics",component:()=>Se(()=>import("./Analytics-D_wvShAW.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-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}; diff --git a/frontend/dist/assets/layout-dashboard-BeK4EeBd.js b/frontend/dist/assets/layout-dashboard-DNjo1OkM.js similarity index 91% rename from frontend/dist/assets/layout-dashboard-BeK4EeBd.js rename to frontend/dist/assets/layout-dashboard-DNjo1OkM.js index 10e39f0..3787700 100644 --- a/frontend/dist/assets/layout-dashboard-BeK4EeBd.js +++ b/frontend/dist/assets/layout-dashboard-DNjo1OkM.js @@ -1,4 +1,4 @@ -import{c as t}from"./index-Dyfiqyex.js";/** +import{c as t}from"./index-rfeHzF-F.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-cj79Hj37.js b/frontend/dist/assets/lock-BhsUQe9Y.js similarity index 88% rename from frontend/dist/assets/lock-cj79Hj37.js rename to frontend/dist/assets/lock-BhsUQe9Y.js index fec49ba..51c2da2 100644 --- a/frontend/dist/assets/lock-cj79Hj37.js +++ b/frontend/dist/assets/lock-BhsUQe9Y.js @@ -1,4 +1,4 @@ -import{c as e}from"./index-Dyfiqyex.js";/** +import{c as e}from"./index-rfeHzF-F.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-BRv2cnuI.js b/frontend/dist/assets/plus-GGHS2V4y.js similarity index 86% rename from frontend/dist/assets/plus-BRv2cnuI.js rename to frontend/dist/assets/plus-GGHS2V4y.js index 493e8c1..7255978 100644 --- a/frontend/dist/assets/plus-BRv2cnuI.js +++ b/frontend/dist/assets/plus-GGHS2V4y.js @@ -1,4 +1,4 @@ -import{c as e}from"./index-Dyfiqyex.js";/** +import{c as e}from"./index-rfeHzF-F.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-x6AFhIMl.js b/frontend/dist/assets/sparkles-DyyL2g7W.js similarity index 93% rename from frontend/dist/assets/sparkles-x6AFhIMl.js rename to frontend/dist/assets/sparkles-DyyL2g7W.js index 966ebc1..238bc8d 100644 --- a/frontend/dist/assets/sparkles-x6AFhIMl.js +++ b/frontend/dist/assets/sparkles-DyyL2g7W.js @@ -1,4 +1,4 @@ -import{c as a}from"./index-Dyfiqyex.js";/** +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. diff --git a/frontend/dist/assets/target-D9jhWfH4.js b/frontend/dist/assets/target-CLzEfHJ7.js similarity index 90% rename from frontend/dist/assets/target-D9jhWfH4.js rename to frontend/dist/assets/target-CLzEfHJ7.js index 25f57fd..3a24e19 100644 --- a/frontend/dist/assets/target-D9jhWfH4.js +++ b/frontend/dist/assets/target-CLzEfHJ7.js @@ -1,4 +1,4 @@ -import{c}from"./index-Dyfiqyex.js";/** +import{c}from"./index-rfeHzF-F.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-B6PgAwnZ.js b/frontend/dist/assets/users-BgT8ke9W.js similarity index 90% rename from frontend/dist/assets/users-B6PgAwnZ.js rename to frontend/dist/assets/users-BgT8ke9W.js index b009df6..291f529 100644 --- a/frontend/dist/assets/users-B6PgAwnZ.js +++ b/frontend/dist/assets/users-BgT8ke9W.js @@ -1,4 +1,4 @@ -import{c as e}from"./index-Dyfiqyex.js";/** +import{c as e}from"./index-rfeHzF-F.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 1bca291..50f54ef 100644 --- a/frontend/dist/index.html +++ b/frontend/dist/index.html @@ -4,7 +4,7 @@ Sales Trainer - + diff --git a/frontend/src/views/Chat.vue b/frontend/src/views/Chat.vue index 0936b55..9206184 100644 --- a/frontend/src/views/Chat.vue +++ b/frontend/src/views/Chat.vue @@ -109,7 +109,6 @@ const scenarios = computed(() => { return [ { id: 'social', emoji: '💬', init: 'customer', label: en ? 'Social Media (chat)' : 'Social Media (แชท)', desc: en ? 'The customer messages you first — short, chat-style replies' : 'ลูกค้าทักมาหาคุณก่อน — โทนสั้น ทักๆ ตามสไตล์แชท' }, { id: 'f2f_call', emoji: '📞', init: 'seller', label: en ? 'Face-to-face / Phone' : 'พบหน้า / โทรศัพท์', desc: en ? 'You must proactively open the conversation (seller starts)' : 'คุณต้องเป็นฝ่ายเปิดการสนทนาเชิงรุกกับลีด (ผู้ฝึกทักก่อน)' }, - { id: 'recontact', emoji: '⏳', init: 'customer', label: en ? 'Re-contact (1-3 months)' : 'ลูกค้ากลับมาติดต่อ (1-3 เดือน)', desc: en ? 'Customer contacted you before, now returns more ready to decide' : 'เคยได้รับข้อมูลไปแล้ว ตอนนี้กลับมาติดต่อ พร้อมตัดสินใจมากขึ้น' }, ] }) diff --git a/frontend/src/views/Personas.vue b/frontend/src/views/Personas.vue index 5a9de56..50b494f 100644 --- a/frontend/src/views/Personas.vue +++ b/frontend/src/views/Personas.vue @@ -10,7 +10,7 @@
  1. เลือกลูกค้าจำลอง (บุคคลต้นแบบ) คนหนึ่งที่อยากฝึกด้วย
  2. ระดับ A ง่ายสุด → ระดับ C ยากสุด (ดูจากดาว ★ ความยาก)
  3. -
  4. กด แชท → เลือกสถานการณ์ (โซเชียล / พบหน้า-โทร / กลับมาติดต่อ) → เริ่มคุยกับลูกค้า
  5. +
  6. กด แชท → เลือกสถานการณ์ (โซเชียล / พบหน้า-โทร) → เริ่มคุยกับลูกค้า
  7. ลูกค้าจะตัดสินใจเองว่าซื้อหรือไม่ซื้อ (ฝึกได้คนละครั้งเท่านั้น)