From cb2642a659636555a8b0cc5044a6cfb85618a225 Mon Sep 17 00:00:00 2001 From: Macky Date: Sun, 9 Aug 2026 11:15:13 +0700 Subject: [PATCH] fix(ui): no raw JSON on Training list, pain no longer [object Object], add Chat button on GroupEdit, drop facebook/line from persona MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - list_groups returns lightweight summaries (id/title/status/persona_count + product) — never the full personas array, so Training no longer shows a long JSON blob. - PersonaForm toLines extracts .description/.text from pain/objection objects (fixes '[object Object]'); save re-wraps pains as {description}. - GroupEdit persona cards get a 'แชท' (MessageSquare) button so you can start chatting right where you land after creating (chat was only on Personas page before). - channel: default neutral 'social', removed facebook/line validation + removed from revealable_view (scenario now sets the tone, not fb/line). All 9 backend suites pass. Rebuilt dist. --- backend/app/api/group_routes.py | 16 +++++++++++++++- backend/app/services/store.py | 7 ++----- ...nUsers-BeTxTP9Z.js => AdminUsers-CK2wxE4M.js} | 2 +- ...alytics-DDiLGx9N.js => Analytics-D_wvShAW.js} | 2 +- .../{Chat-LyydvOUA.js => Chat-CpZrRyQZ.js} | 2 +- ...lder-CFI3DZAc.js => GroupBuilder-PtFiTFPC.js} | 2 +- frontend/dist/assets/GroupEdit-BEoo7c6X.js | 9 --------- frontend/dist/assets/GroupEdit-Bujf8Tz9.css | 1 - frontend/dist/assets/GroupEdit-DqUapL_D.js | 14 ++++++++++++++ frontend/dist/assets/GroupEdit-XuXM9ZDv.css | 1 + .../{Guide-D7TwTcNd.js => Guide-CxzIj9kO.js} | 2 +- .../{Login-BVI-qGSk.js => Login-CaF0UGOL.js} | 2 +- .../{MyBoard-DAe3xT7M.js => MyBoard-BBr3kpxa.js} | 2 +- ...Personas-sBoT-XQc.js => Personas-CzGYN8e6.js} | 2 +- ...Settings-BF-9-9N5.js => Settings-IzLUgL3N.js} | 2 +- .../{Setup-BogYTfwo.js => Setup-DOZNu22f.js} | 2 +- ...Training-DovmDhn_.js => Training-Baew-jzp.js} | 2 +- ...w-left-BuPfyTxu.js => arrow-left-C95UZZnG.js} | 2 +- ...ok-open-hzON0xcx.js => book-open-BVimbpEo.js} | 2 +- .../{index-D72MDfqC.js => index-Dyfiqyex.js} | 4 ++-- ...-DnOEx-ds.js => layout-dashboard-BeK4EeBd.js} | 2 +- .../{lock-BM8eMwHW.js => lock-cj79Hj37.js} | 2 +- .../{plus-BI5BRyQZ.js => plus-BRv2cnuI.js} | 2 +- ...sparkles-PhamuQBi.js => sparkles-x6AFhIMl.js} | 2 +- .../{target-BHkg_ns5.js => target-D9jhWfH4.js} | 2 +- .../{users-CHXJwc1N.js => users-B6PgAwnZ.js} | 2 +- frontend/dist/index.html | 2 +- frontend/src/components/PersonaForm.vue | 10 ++++++++-- frontend/src/views/GroupEdit.vue | 9 +++++++-- 29 files changed, 69 insertions(+), 42 deletions(-) rename frontend/dist/assets/{AdminUsers-BeTxTP9Z.js => AdminUsers-CK2wxE4M.js} (98%) rename frontend/dist/assets/{Analytics-DDiLGx9N.js => Analytics-D_wvShAW.js} (97%) rename frontend/dist/assets/{Chat-LyydvOUA.js => Chat-CpZrRyQZ.js} (97%) rename frontend/dist/assets/{GroupBuilder-CFI3DZAc.js => GroupBuilder-PtFiTFPC.js} (98%) delete mode 100644 frontend/dist/assets/GroupEdit-BEoo7c6X.js delete mode 100644 frontend/dist/assets/GroupEdit-Bujf8Tz9.css create mode 100644 frontend/dist/assets/GroupEdit-DqUapL_D.js create mode 100644 frontend/dist/assets/GroupEdit-XuXM9ZDv.css rename frontend/dist/assets/{Guide-D7TwTcNd.js => Guide-CxzIj9kO.js} (95%) rename frontend/dist/assets/{Login-BVI-qGSk.js => Login-CaF0UGOL.js} (98%) rename frontend/dist/assets/{MyBoard-DAe3xT7M.js => MyBoard-BBr3kpxa.js} (94%) rename frontend/dist/assets/{Personas-sBoT-XQc.js => Personas-CzGYN8e6.js} (95%) rename frontend/dist/assets/{Settings-BF-9-9N5.js => Settings-IzLUgL3N.js} (98%) rename frontend/dist/assets/{Setup-BogYTfwo.js => Setup-DOZNu22f.js} (94%) rename frontend/dist/assets/{Training-DovmDhn_.js => Training-Baew-jzp.js} (95%) rename frontend/dist/assets/{arrow-left-BuPfyTxu.js => arrow-left-C95UZZnG.js} (86%) rename frontend/dist/assets/{book-open-hzON0xcx.js => book-open-BVimbpEo.js} (90%) rename frontend/dist/assets/{index-D72MDfqC.js => index-Dyfiqyex.js} (98%) rename frontend/dist/assets/{layout-dashboard-DnOEx-ds.js => layout-dashboard-BeK4EeBd.js} (91%) rename frontend/dist/assets/{lock-BM8eMwHW.js => lock-cj79Hj37.js} (88%) rename frontend/dist/assets/{plus-BI5BRyQZ.js => plus-BRv2cnuI.js} (86%) rename frontend/dist/assets/{sparkles-PhamuQBi.js => sparkles-x6AFhIMl.js} (93%) rename frontend/dist/assets/{target-BHkg_ns5.js => target-D9jhWfH4.js} (90%) rename frontend/dist/assets/{users-CHXJwc1N.js => users-B6PgAwnZ.js} (90%) diff --git a/backend/app/api/group_routes.py b/backend/app/api/group_routes.py index bd893ce..2153456 100644 --- a/backend/app/api/group_routes.py +++ b/backend/app/api/group_routes.py @@ -163,7 +163,21 @@ def list_groups(): for g in visible if not g.get("owner_user_id") or g.get("owner_user_id") == actor["id"] ] - return jsonify({"groups": visible}) + # Lightweight summaries only — never send the full personas/sales_kit/report to a + # list view (huge, heavy, leaks the recipe). Training shows title + persona count. + summaries = [] + for g in visible: + personas = g.get("personas") or [] + summaries.append({ + "id": g.get("id"), + "title": g.get("title", ""), + "status": g.get("status", "draft"), + "channel": g.get("channel"), + "org_id": g.get("org_id"), + "persona_count": len(personas), + "input": {"product": (g.get("input") or {}).get("product", "")}, + }) + return jsonify({"groups": summaries}) @groups_bp.post("//analyze") diff --git a/backend/app/services/store.py b/backend/app/services/store.py index e583261..0c7baf9 100644 --- a/backend/app/services/store.py +++ b/backend/app/services/store.py @@ -23,7 +23,7 @@ def ensure_persona_shape(p: dict[str, Any]) -> dict[str, Any]: "name": p.get("name", ""), "tier": p.get("tier", p.get("intent_tier", "B")), "initiation_mode": p.get("initiation_mode", "customer"), # customer | seller - "channel": p.get("channel", "facebook"), # facebook | line + "channel": p.get("channel", "social"), # internal tone hint (scenario overrides) # revealable "profession": p.get("profession", ""), "age_group": p.get("age_group", ""), @@ -47,13 +47,11 @@ def ensure_persona_shape(p: dict[str, Any]) -> dict[str, Any]: "tolerance": p.get("tolerance", 3), # misses before this persona walks away (temper) "notes": p.get("notes", ""), } - # validate + # validation if base["tier"] not in DEFAULT_TIERS: base["tier"] = "B" if base["initiation_mode"] not in ("customer", "seller"): base["initiation_mode"] = "customer" - if base["channel"] not in ("facebook", "line"): - base["channel"] = "facebook" return base @@ -63,7 +61,6 @@ def revealable_view(p: dict[str, Any]) -> dict[str, Any]: "id": p.get("id"), "name": p.get("name"), "tier": p.get("tier"), - "channel": p.get("channel"), "initiation_mode": p.get("initiation_mode"), "profession": p.get("profession"), "age_group": p.get("age_group"), diff --git a/frontend/dist/assets/AdminUsers-BeTxTP9Z.js b/frontend/dist/assets/AdminUsers-CK2wxE4M.js similarity index 98% rename from frontend/dist/assets/AdminUsers-BeTxTP9Z.js rename to frontend/dist/assets/AdminUsers-CK2wxE4M.js index dbffbcf..e73f8d0 100644 --- a/frontend/dist/assets/AdminUsers-BeTxTP9Z.js +++ b/frontend/dist/assets/AdminUsers-CK2wxE4M.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-D72MDfqC.js";import{U as z}from"./users-CHXJwc1N.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-Dyfiqyex.js";import{U as z}from"./users-B6PgAwnZ.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-DDiLGx9N.js b/frontend/dist/assets/Analytics-D_wvShAW.js similarity index 97% rename from frontend/dist/assets/Analytics-DDiLGx9N.js rename to frontend/dist/assets/Analytics-D_wvShAW.js index 21f619f..e96e1a8 100644 --- a/frontend/dist/assets/Analytics-DDiLGx9N.js +++ b/frontend/dist/assets/Analytics-D_wvShAW.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-D72MDfqC.js";import{U as P}from"./users-CHXJwc1N.js";import{P as S}from"./plus-BI5BRyQZ.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-Dyfiqyex.js";import{U as P}from"./users-B6PgAwnZ.js";import{P as S}from"./plus-BRv2cnuI.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-LyydvOUA.js b/frontend/dist/assets/Chat-CpZrRyQZ.js similarity index 97% rename from frontend/dist/assets/Chat-LyydvOUA.js rename to frontend/dist/assets/Chat-CpZrRyQZ.js index c1ff839..ed8e77a 100644 --- a/frontend/dist/assets/Chat-LyydvOUA.js +++ b/frontend/dist/assets/Chat-CpZrRyQZ.js @@ -1,4 +1,4 @@ -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-D72MDfqC.js";import{T as Y}from"./target-BHkg_ns5.js";import{A as Q}from"./arrow-left-BuPfyTxu.js";/** +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. diff --git a/frontend/dist/assets/GroupBuilder-CFI3DZAc.js b/frontend/dist/assets/GroupBuilder-PtFiTFPC.js similarity index 98% rename from frontend/dist/assets/GroupBuilder-CFI3DZAc.js rename to frontend/dist/assets/GroupBuilder-PtFiTFPC.js index 2f4c5ba..610ee32 100644 --- a/frontend/dist/assets/GroupBuilder-CFI3DZAc.js +++ b/frontend/dist/assets/GroupBuilder-PtFiTFPC.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-D72MDfqC.js";import{A as q}from"./arrow-left-BuPfyTxu.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-Dyfiqyex.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. diff --git a/frontend/dist/assets/GroupEdit-BEoo7c6X.js b/frontend/dist/assets/GroupEdit-BEoo7c6X.js deleted file mode 100644 index b6907b7..0000000 --- a/frontend/dist/assets/GroupEdit-BEoo7c6X.js +++ /dev/null @@ -1,9 +0,0 @@ -import{c as E,_ as M,I as F,o as r,a as m,b as t,t as i,w as o,v as a,H as I,u as c,h as T,g as B,J as D,r as b,p as J,l as S,q,m as C,i as _,f as H,G as R,F as N,x as P,D as K,k as Q,y as A,z as W,C as G}from"./index-D72MDfqC.js";import{U as X}from"./users-CHXJwc1N.js";import{S as Y}from"./sparkles-PhamuQBi.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 Z=E("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"}]]),h={class:"persona-form"},ee={class:"muted"},te={class:"sec"},le={class:"row"},ne={class:"f"},se={class:"f"},oe={class:"f"},ae={class:"row"},ie={class:"f"},ue={class:"f"},re={class:"f"},de={class:"sec"},me={class:"row"},ve={class:"f"},fe={class:"f"},ge={class:"f"},ye={key:0,class:"sec"},be={key:1,class:"sec locked"},ce={key:2,class:"sec"},ke={class:"muted"},pe={class:"row actions"},xe=["disabled"],Ve={__name:"PersonaForm",props:{persona:{type:Object,required:!0}},emits:["save","cancel"],setup(x,{emit:L}){const k=x,V=L,n=D({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:""}),v=b(""),g=b(""),f=b(""),p=b(!1);function w(l){return Array.isArray(l)?l.join(` -`):String(l||"")}function U(l){return l.split(` -`).map(e=>e.trim()).filter(Boolean)}F(()=>k.persona,l=>{Object.assign(n,{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)||""}),v.value=w(l==null?void 0:l.pains),g.value=w(l==null?void 0:l.objections),f.value=w(l==null?void 0:l.negotiation_levers)},{immediate:!0});function z(){V("save",{...n,pains:U(v.value),objections:U(g.value),negotiation_levers:U(f.value)})}return(l,e)=>(r(),m("div",h,[t("h3",null,i(x.persona.name||"New persona"),1),t("p",ee,i(x.persona.profession)+" · "+i(x.persona.personality),1),t("div",te,[e[28]||(e[28]=t("h4",null,"👤 ข้อมูลพื้นฐาน",-1)),t("div",le,[t("div",ne,[e[21]||(e[21]=t("label",null,"ชื่อ",-1)),o(t("input",{"onUpdate:modelValue":e[0]||(e[0]=s=>n.name=s)},null,512),[[a,n.name]])]),t("div",se,[e[22]||(e[22]=t("label",null,"ระดับความยาก (1-5)",-1)),o(t("input",{"onUpdate:modelValue":e[1]||(e[1]=s=>n.difficulty=s),type:"number",min:"1",max:"5"},null,512),[[a,n.difficulty,void 0,{number:!0}]])]),t("div",oe,[e[24]||(e[24]=t("label",null,"ระดับลูกค้า",-1)),o(t("select",{"onUpdate:modelValue":e[2]||(e[2]=s=>n.tier=s)},[...e[23]||(e[23]=[t("option",{value:"A"},"A — พร้อมตัดสินใจซื้อ",-1),t("option",{value:"B"},"B — ยังไม่แน่ใจ",-1),t("option",{value:"C"},"C — ไม่สนใจแต่มีปัญหา",-1)])],512),[[I,n.tier]])])]),t("div",ae,[t("div",ie,[e[25]||(e[25]=t("label",null,"อาชีพ",-1)),o(t("input",{"onUpdate:modelValue":e[3]||(e[3]=s=>n.profession=s)},null,512),[[a,n.profession]])]),t("div",ue,[e[26]||(e[26]=t("label",null,"ช่วงอายุ",-1)),o(t("input",{"onUpdate:modelValue":e[4]||(e[4]=s=>n.age_group=s)},null,512),[[a,n.age_group]])]),t("div",re,[e[27]||(e[27]=t("label",null,"พื้นที่",-1)),o(t("input",{"onUpdate:modelValue":e[5]||(e[5]=s=>n.location=s)},null,512),[[a,n.location]])])])]),t("div",de,[e[32]||(e[32]=t("h4",null,"💼 ข้อมูลลูกค้า",-1)),t("div",me,[t("div",ve,[e[29]||(e[29]=t("label",null,"รายได้/สถานะการเงิน",-1)),o(t("input",{"onUpdate:modelValue":e[6]||(e[6]=s=>n.income=s)},null,512),[[a,n.income]])]),t("div",fe,[e[30]||(e[30]=t("label",null,"งบประมาณ",-1)),o(t("input",{"onUpdate:modelValue":e[7]||(e[7]=s=>n.budget=s)},null,512),[[a,n.budget]])]),t("div",ge,[e[31]||(e[31]=t("label",null,"ไลฟ์สไตล์",-1)),o(t("input",{"onUpdate:modelValue":e[8]||(e[8]=s=>n.lifestyle=s)},null,512),[[a,n.lifestyle]])])]),e[33]||(e[33]=t("label",null,"ภูมิหลัง",-1)),o(t("textarea",{"onUpdate:modelValue":e[9]||(e[9]=s=>n.background=s)},null,512),[[a,n.background]]),e[34]||(e[34]=t("label",null,"บุคลิก / วิธีพูดคุย",-1)),o(t("textarea",{"onUpdate:modelValue":e[10]||(e[10]=s=>n.personality=s)},null,512),[[a,n.personality]]),e[35]||(e[35]=t("label",null,"สไตล์การสื่อสาร",-1)),o(t("textarea",{"onUpdate:modelValue":e[11]||(e[11]=s=>n.communication_style=s)},null,512),[[a,n.communication_style]])]),c(T).isSuperAdmin?(r(),m("div",ye,[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]=s=>n.goal=s)},null,512),[[a,n.goal]]),e[38]||(e[38]=t("label",null,"กรอบเวลาในการตัดสินใจ",-1)),o(t("input",{"onUpdate:modelValue":e[13]||(e[13]=s=>n.decision_timeline=s)},null,512),[[a,n.decision_timeline]]),e[39]||(e[39]=t("label",null,"Pain (ปัญหา) แบบ 1 ต่อบรรทัด",-1)),o(t("textarea",{"onUpdate:modelValue":e[14]||(e[14]=s=>v.value=s),placeholder:`เช่น ต้นทุนสูงเกินไป -ระบบล้าสมัย`},null,512),[[a,v.value]]),e[40]||(e[40]=t("label",null,"ข้อโต้แย้ง (Objections) 1 ต่อบรรทัด",-1)),o(t("textarea",{"onUpdate:modelValue":e[15]||(e[15]=s=>g.value=s)},null,512),[[a,g.value]]),e[41]||(e[41]=t("label",null,"สิ่งที่ใช้ต่อรอง (เช่น ส่วนลด, ฟรีติดตั้ง)",-1)),o(t("textarea",{"onUpdate:modelValue":e[16]||(e[16]=s=>f.value=s)},null,512),[[a,f.value]]),e[42]||(e[42]=t("label",null,"ช่องทางสำหรับลูกค้าเปิดบทสนทนา (opener)",-1)),o(t("textarea",{"onUpdate:modelValue":e[17]||(e[17]=s=>n.opener=s)},null,512),[[a,n.opener]])])):(r(),m("div",be,[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]=s=>n.goal=s)},null,512),[[a,n.goal]]),e[46]||(e[46]=t("label",null,"กรอบเวลาในการตัดสินใจ",-1)),o(t("input",{"onUpdate:modelValue":e[19]||(e[19]=s=>n.decision_timeline=s)},null,512),[[a,n.decision_timeline]])])),c(T).isSuperAdmin&&n.special?(r(),m("div",ce,[t("label",ke,"พิเศษ: "+i(n.special),1)])):B("",!0),t("div",pe,[t("button",{class:"primary",onClick:z,disabled:p.value},i(p.value?"กำลังบันทึก…":"💾 บันทึกบุคคลต้นแบบ"),9,xe),t("button",{onClick:e[20]||(e[20]=s=>l.$emit("cancel"))},"ปิด")])]))}},we=M(Ve,[["__scopeId","data-v-f420bfd6"]]),Ue={class:"row",style:{"align-items":"center","margin-bottom":"8px"}},$e={style:{margin:"0"}},Ce={class:"row",style:{"margin-left":"auto",gap:"10px"}},Be=["disabled"],Se={key:0,class:"spinner"},_e={key:0,class:"muted"},Ae={key:1,class:"error",style:{margin:"12px 0"}},ze={key:2,class:"card empty-state"},je={class:"grid"},Ne={class:"row",style:{"justify-content":"space-between","align-items":"center"}},Pe={class:"diff"},Le={class:"muted"},Te={class:"muted"},Ge=["onClick"],Me={class:"modal"},Oe={__name:"GroupEdit",setup(x){const k=Q().params.gid,V=b(null),n=b([]),v=b(!1),g=b(""),f=b(null);async function p(){const u=await A.getGroup(k);V.value=u.group,n.value=(await A.listPersonas(k)).personas}function w(u){return n.value.filter(d=>d.tier===u)}function U(u){return _.t(u==="A"?"tierA":u==="B"?"tierB":"tierC")}function z(u){return{A:"tier-a",B:"tier-b",C:"tier-c"}[u]||""}async function l(){v.value=!0,g.value="";try{const u=await A.analyzeGroup(k,{append:!0});n.value=u.personas,await p()}catch(u){g.value=u.message}finally{v.value=!1}}function e(u){f.value=JSON.parse(JSON.stringify(u))}async function s(u){v.value=!0;try{await A.updatePersona(k,f.value.id,u),f.value=null,await p()}catch(d){g.value=d.message}finally{v.value=!1}}return J(p),(u,d)=>{const O=W("router-link");return r(),m("div",null,[S(O,{to:"/training",class:"btn-back"},{default:q(()=>[C("← "+i(c(_).t("training")),1)]),_:1}),t("div",Ue,[t("h2",$e,[S(c(X),{size:22,"stroke-width":1.8,style:{"vertical-align":"-4px"}}),C(" "+i(c(_).t("managePersonas")),1)]),t("div",Ce,[t("button",{onClick:l,disabled:v.value,class:"primary"},[v.value?(r(),m("span",Se)):(r(),H(c(Y),{key:1,size:18,"stroke-width":1.8})),C(" "+i(v.value?"กำลังสร้าง…":"สร้างบุคคลต้นแบบเพิ่มเติม"),1)],8,Be)])]),V.value?(r(),m("p",_e,[d[2]||(d[2]=C("สินค้า: ",-1)),t("strong",null,i(V.value.title),1)])):B("",!0),g.value?(r(),m("div",Ae,i(g.value),1)):B("",!0),d[4]||(d[4]=R('
วิธีใช้หน้านี้
  1. ระบบสร้างบุคคลต้นแบบ (ลูกค้าจำลอง) ให้อัตโนมัติแล้ว หลังกดสร้าง
  2. แต่ละการ์ดคือลูกค้าจำลอง 1 คน — กด ✏️ แก้ไข เพื่อปรับรายละเอียดให้ตรงใจ
  3. อยากได้ลูกค้าจำลองเพิ่ม กด สร้างบุคคลต้นแบบเพิ่มเติม
  4. วิธีเริ่มฝึก: ไปที่แท็บ การฝึก → เลือกสินค้านี้ → เลือกลูกค้าจำลอง 1 คน → กด แชท → เลือกสถานการณ์ → เริ่มคุย
',1)),n.value.length===0&&!v.value?(r(),m("div",ze,[...d[3]||(d[3]=[t("strong",null,"ยังไม่มีบุคคลต้นแบบ",-1),t("span",null,'กด "สร้างบุคคลต้นแบบเพิ่มเติม" เพื่อให้ระบบสร้างลูกค้าจำลองให้',-1)])])):B("",!0),(r(),m(N,null,P(["A","B","C"],$=>t("div",{key:$,style:{"margin-bottom":"20px"}},[t("h4",null,i(U($)),1),t("div",je,[(r(!0),m(N,null,P(w($),y=>(r(),m("div",{key:y.id,class:"card pcard"},[t("div",Ne,[t("strong",null,i(y.name),1),t("span",{class:G(["badge",z(y.tier)])},i(y.tier),3)]),t("div",Pe,[(r(),m(N,null,P(5,j=>t("span",{key:j,class:G(["star",{on:j<=(y.difficulty||1)}])},"★",2)),64))]),t("div",Le,i(y.profession)+" · "+i(y.age_group)+" · "+i(y.location),1),t("div",Te,i(y.personality),1),t("button",{class:"edit-btn",onClick:j=>e(y)},[S(c(Z),{size:15,"stroke-width":1.8}),C(" "+i(c(_).t("edit")),1)],8,Ge)]))),128))])])),64)),f.value?(r(),m("div",{key:3,class:"modal-backdrop",onClick:d[1]||(d[1]=K($=>f.value=null,["self"]))},[t("div",Me,[S(we,{persona:f.value,onSave:s,onCancel:d[0]||(d[0]=$=>f.value=null)},null,8,["persona"])])])):B("",!0)])}}},De=M(Oe,[["__scopeId","data-v-fcca87f1"]]);export{De as default}; diff --git a/frontend/dist/assets/GroupEdit-Bujf8Tz9.css b/frontend/dist/assets/GroupEdit-Bujf8Tz9.css deleted file mode 100644 index 82981f3..0000000 --- a/frontend/dist/assets/GroupEdit-Bujf8Tz9.css +++ /dev/null @@ -1 +0,0 @@ -.sec[data-v-f420bfd6]{margin-top:20px}.sec h4[data-v-f420bfd6]{margin:0 0 10px;padding-bottom:6px;border-bottom:1px solid var(--border)}.row[data-v-f420bfd6]{display:flex;gap:12px;flex-wrap:wrap}.f[data-v-f420bfd6]{flex:1;min-width:140px}label[data-v-f420bfd6]{font-size:13px;color:var(--muted);display:block;margin:10px 0 4px}input[data-v-f420bfd6],select[data-v-f420bfd6],textarea[data-v-f420bfd6]{width:100%}.actions[data-v-f420bfd6]{margin-top:20px}.locked[data-v-f420bfd6]{background:#fffaf5;border:1px dashed #d6c7a1;border-radius:12px;padding:12px 14px}.locked h4[data-v-f420bfd6]{color:#b45309}.grid[data-v-fcca87f1]{display:grid;grid-template-columns:repeat(auto-fill,minmax(240px,1fr));gap:14px}.pcard[data-v-fcca87f1]{display:flex;flex-direction:column}.edit-btn[data-v-fcca87f1]{margin-top:auto;display:inline-flex;align-items:center;gap:6px}.star[data-v-fcca87f1]{color:#d8dbe3}.star.on[data-v-fcca87f1]{color:#f59e0b}.badge.tier-a[data-v-fcca87f1]{background:#dcfce7;color:#166534}.badge.tier-b[data-v-fcca87f1]{background:#fef9c3;color:#854d0e}.badge.tier-c[data-v-fcca87f1]{background:#fee2e2;color:#991b1b}.guide[data-v-fcca87f1]{background:#eef2ff;border-color:#c7d2fe;margin-bottom:16px}.modal-backdrop[data-v-fcca87f1]{position:fixed;top:0;right:0;bottom:0;left:0;background:#0f172a80;display:flex;justify-content:center;align-items:flex-start;padding:40px 16px;z-index:50;overflow:auto}.modal[data-v-fcca87f1]{background:#fff;border-radius:14px;padding:24px;width:100%;max-width:720px;box-shadow:0 20px 50px #00000040} diff --git a/frontend/dist/assets/GroupEdit-DqUapL_D.js b/frontend/dist/assets/GroupEdit-DqUapL_D.js new file mode 100644 index 0000000..c391a2a --- /dev/null +++ b/frontend/dist/assets/GroupEdit-DqUapL_D.js @@ -0,0 +1,14 @@ +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";/** + * @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 Z=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 h=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"}]]),ee={class:"persona-form"},te={class:"muted"},le={class:"sec"},ne={class:"row"},se={class:"f"},oe={class:"f"},ie={class:"f"},ae={class:"row"},re={class:"f"},ue={class:"f"},de={class:"f"},me={class:"sec"},ve={class:"row"},fe={class:"f"},ge={class:"f"},ye={class:"f"},be={key:0,class:"sec"},ke={key:1,class:"sec locked"},ce={key:2,class:"sec"},pe={class:"muted"},we={class:"row actions"},xe=["disabled"],Ve={__name:"PersonaForm",props:{persona:{type:Object,required:!0}},emits:["save","cancel"],setup(V,{emit:L}){const c=V,$=L,n=D({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:""}),v=k(""),b=k(""),f=k(""),p=k(!1);function U(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 C(l){return l.split(` +`).map(e=>e.trim()).filter(Boolean)}F(()=>c.persona,l=>{Object.assign(n,{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)||""}),v.value=U(l==null?void 0:l.pains),b.value=U(l==null?void 0:l.objections),f.value=U(l==null?void 0:l.negotiation_levers)},{immediate:!0});function A(){$("save",{...n,pains:C(v.value).map(l=>({description:l})),objections:C(b.value),negotiation_levers:C(f.value)})}return(l,e)=>(u(),m("div",ee,[t("h3",null,a(V.persona.name||"New persona"),1),t("p",te,a(V.persona.profession)+" · "+a(V.persona.personality),1),t("div",le,[e[28]||(e[28]=t("h4",null,"👤 ข้อมูลพื้นฐาน",-1)),t("div",ne,[t("div",se,[e[21]||(e[21]=t("label",null,"ชื่อ",-1)),o(t("input",{"onUpdate:modelValue":e[0]||(e[0]=s=>n.name=s)},null,512),[[i,n.name]])]),t("div",oe,[e[22]||(e[22]=t("label",null,"ระดับความยาก (1-5)",-1)),o(t("input",{"onUpdate:modelValue":e[1]||(e[1]=s=>n.difficulty=s),type:"number",min:"1",max:"5"},null,512),[[i,n.difficulty,void 0,{number:!0}]])]),t("div",ie,[e[24]||(e[24]=t("label",null,"ระดับลูกค้า",-1)),o(t("select",{"onUpdate:modelValue":e[2]||(e[2]=s=>n.tier=s)},[...e[23]||(e[23]=[t("option",{value:"A"},"A — พร้อมตัดสินใจซื้อ",-1),t("option",{value:"B"},"B — ยังไม่แน่ใจ",-1),t("option",{value:"C"},"C — ไม่สนใจแต่มีปัญหา",-1)])],512),[[I,n.tier]])])]),t("div",ae,[t("div",re,[e[25]||(e[25]=t("label",null,"อาชีพ",-1)),o(t("input",{"onUpdate:modelValue":e[3]||(e[3]=s=>n.profession=s)},null,512),[[i,n.profession]])]),t("div",ue,[e[26]||(e[26]=t("label",null,"ช่วงอายุ",-1)),o(t("input",{"onUpdate:modelValue":e[4]||(e[4]=s=>n.age_group=s)},null,512),[[i,n.age_group]])]),t("div",de,[e[27]||(e[27]=t("label",null,"พื้นที่",-1)),o(t("input",{"onUpdate:modelValue":e[5]||(e[5]=s=>n.location=s)},null,512),[[i,n.location]])])])]),t("div",me,[e[32]||(e[32]=t("h4",null,"💼 ข้อมูลลูกค้า",-1)),t("div",ve,[t("div",fe,[e[29]||(e[29]=t("label",null,"รายได้/สถานะการเงิน",-1)),o(t("input",{"onUpdate:modelValue":e[6]||(e[6]=s=>n.income=s)},null,512),[[i,n.income]])]),t("div",ge,[e[30]||(e[30]=t("label",null,"งบประมาณ",-1)),o(t("input",{"onUpdate:modelValue":e[7]||(e[7]=s=>n.budget=s)},null,512),[[i,n.budget]])]),t("div",ye,[e[31]||(e[31]=t("label",null,"ไลฟ์สไตล์",-1)),o(t("input",{"onUpdate:modelValue":e[8]||(e[8]=s=>n.lifestyle=s)},null,512),[[i,n.lifestyle]])])]),e[33]||(e[33]=t("label",null,"ภูมิหลัง",-1)),o(t("textarea",{"onUpdate:modelValue":e[9]||(e[9]=s=>n.background=s)},null,512),[[i,n.background]]),e[34]||(e[34]=t("label",null,"บุคลิก / วิธีพูดคุย",-1)),o(t("textarea",{"onUpdate:modelValue":e[10]||(e[10]=s=>n.personality=s)},null,512),[[i,n.personality]]),e[35]||(e[35]=t("label",null,"สไตล์การสื่อสาร",-1)),o(t("textarea",{"onUpdate:modelValue":e[11]||(e[11]=s=>n.communication_style=s)},null,512),[[i,n.communication_style]])]),y(T).isSuperAdmin?(u(),m("div",be,[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]=s=>n.goal=s)},null,512),[[i,n.goal]]),e[38]||(e[38]=t("label",null,"กรอบเวลาในการตัดสินใจ",-1)),o(t("input",{"onUpdate:modelValue":e[13]||(e[13]=s=>n.decision_timeline=s)},null,512),[[i,n.decision_timeline]]),e[39]||(e[39]=t("label",null,"Pain (ปัญหา) แบบ 1 ต่อบรรทัด",-1)),o(t("textarea",{"onUpdate:modelValue":e[14]||(e[14]=s=>v.value=s),placeholder:`เช่น ต้นทุนสูงเกินไป +ระบบล้าสมัย`},null,512),[[i,v.value]]),e[40]||(e[40]=t("label",null,"ข้อโต้แย้ง (Objections) 1 ต่อบรรทัด",-1)),o(t("textarea",{"onUpdate:modelValue":e[15]||(e[15]=s=>b.value=s)},null,512),[[i,b.value]]),e[41]||(e[41]=t("label",null,"สิ่งที่ใช้ต่อรอง (เช่น ส่วนลด, ฟรีติดตั้ง)",-1)),o(t("textarea",{"onUpdate:modelValue":e[16]||(e[16]=s=>f.value=s)},null,512),[[i,f.value]]),e[42]||(e[42]=t("label",null,"ช่องทางสำหรับลูกค้าเปิดบทสนทนา (opener)",-1)),o(t("textarea",{"onUpdate:modelValue":e[17]||(e[17]=s=>n.opener=s)},null,512),[[i,n.opener]])])):(u(),m("div",ke,[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]=s=>n.goal=s)},null,512),[[i,n.goal]]),e[46]||(e[46]=t("label",null,"กรอบเวลาในการตัดสินใจ",-1)),o(t("input",{"onUpdate:modelValue":e[19]||(e[19]=s=>n.decision_timeline=s)},null,512),[[i,n.decision_timeline]])])),y(T).isSuperAdmin&&n.special?(u(),m("div",ce,[t("label",pe,"พิเศษ: "+a(n.special),1)])):S("",!0),t("div",we,[t("button",{class:"primary",onClick:A,disabled:p.value},a(p.value?"กำลังบันทึก…":"💾 บันทึกบุคคลต้นแบบ"),9,xe),t("button",{onClick:e[20]||(e[20]=s=>l.$emit("cancel"))},"ปิด")])]))}},$e=E(Ve,[["__scopeId","data-v-59429170"]]),Ue={class:"row",style:{"align-items":"center","margin-bottom":"8px"}},Ce={style:{margin:"0"}},_e={class:"row",style:{"margin-left":"auto",gap:"10px"}},Be=["disabled"],Se={key:0,class:"spinner"},ze={key:0,class:"muted"},Ae={key:1,class:"error",style:{margin:"12px 0"}},je={key:2,class:"card empty-state"},Ne={class:"grid"},Pe={class:"row",style:{"justify-content":"space-between","align-items":"center"}},Le={class:"diff"},Me={class:"muted"},Te={class:"muted"},Ge={class:"row",style:{gap:"8px","margin-top":"auto"}},Oe={class:"primary",style:{width:"100%"}},qe=["onClick"],Ee={class:"modal"},Fe={__name:"GroupEdit",setup(V){const c=Q().params.gid,$=k(null),n=k([]),v=k(!1),b=k(""),f=k(null);async function p(){const r=await z.getGroup(c);$.value=r.group,n.value=(await z.listPersonas(c)).personas}function U(r){return n.value.filter(d=>d.tier===r)}function C(r){return B.t(r==="A"?"tierA":r==="B"?"tierB":"tierC")}function A(r){return{A:"tier-a",B:"tier-b",C:"tier-c"}[r]||""}async function l(){v.value=!0,b.value="";try{const r=await z.analyzeGroup(c,{append:!0});n.value=r.personas,await p()}catch(r){b.value=r.message}finally{v.value=!1}}function e(r){f.value=JSON.parse(JSON.stringify(r))}async function s(r){v.value=!0;try{await z.updatePersona(c,f.value.id,r),f.value=null,await p()}catch(d){b.value=d.message}finally{v.value=!1}}return J(p),(r,d)=>{const M=W("router-link");return u(),m("div",null,[w(M,{to:"/training",class:"btn-back"},{default:G(()=>[x("← "+a(y(B).t("training")),1)]),_:1}),t("div",Ue,[t("h2",Ce,[w(y(X),{size:22,"stroke-width":1.8,style:{"vertical-align":"-4px"}}),x(" "+a(y(B).t("managePersonas")),1)]),t("div",_e,[t("button",{onClick:l,disabled:v.value,class:"primary"},[v.value?(u(),m("span",Se)):(u(),H(y(Y),{key:1,size:18,"stroke-width":1.8})),x(" "+a(v.value?"กำลังสร้าง…":"สร้างบุคคลต้นแบบเพิ่มเติม"),1)],8,Be)])]),$.value?(u(),m("p",ze,[d[2]||(d[2]=x("สินค้า: ",-1)),t("strong",null,a($.value.title),1)])):S("",!0),b.value?(u(),m("div",Ae,a(b.value),1)):S("",!0),d[4]||(d[4]=R('
วิธีใช้หน้านี้
  1. ระบบสร้างบุคคลต้นแบบ (ลูกค้าจำลอง) ให้อัตโนมัติแล้ว หลังกดสร้าง
  2. แต่ละการ์ดคือลูกค้าจำลอง 1 คน — กด ✏️ แก้ไข เพื่อปรับรายละเอียดให้ตรงใจ
  3. อยากได้ลูกค้าจำลองเพิ่ม กด สร้างบุคคลต้นแบบเพิ่มเติม
  4. วิธีเริ่มฝึก: ไปที่แท็บ การฝึก → เลือกสินค้านี้ → เลือกลูกค้าจำลอง 1 คน → กด แชท → เลือกสถานการณ์ → เริ่มคุย
',1)),n.value.length===0&&!v.value?(u(),m("div",je,[...d[3]||(d[3]=[t("strong",null,"ยังไม่มีบุคคลต้นแบบ",-1),t("span",null,'กด "สร้างบุคคลต้นแบบเพิ่มเติม" เพื่อให้ระบบสร้างลูกค้าจำลองให้',-1)])])):S("",!0),(u(),m(N,null,P(["A","B","C"],_=>t("div",{key:_,style:{"margin-bottom":"20px"}},[t("h4",null,a(C(_)),1),t("div",Ne,[(u(!0),m(N,null,P(U(_),g=>(u(),m("div",{key:g.id,class:"card pcard"},[t("div",Pe,[t("strong",null,a(g.name),1),t("span",{class:O(["badge",A(g.tier)])},a(g.tier),3)]),t("div",Le,[(u(),m(N,null,P(5,j=>t("span",{key:j,class:O(["star",{on:j<=(g.difficulty||1)}])},"★",2)),64))]),t("div",Me,a(g.profession)+" · "+a(g.age_group)+" · "+a(g.location),1),t("div",Te,a(g.personality),1),t("div",Ge,[w(M,{to:`/groups/${y(c)}/chat/${g.id}`,style:{flex:"1","text-decoration":"none"}},{default:G(()=>[t("button",Oe,[w(y(Z),{size:16,"stroke-width":1.8}),x(" "+a(y(B).t("chat")),1)])]),_:1},8,["to"]),t("button",{class:"edit-btn",onClick:j=>e(g)},[w(y(h),{size:15,"stroke-width":1.8}),x(" "+a(y(B).t("edit")),1)],8,qe)])]))),128))])])),64)),f.value?(u(),m("div",{key:3,class:"modal-backdrop",onClick:d[1]||(d[1]=K(_=>f.value=null,["self"]))},[t("div",Ee,[w($e,{persona:f.value,onSave:s,onCancel:d[0]||(d[0]=_=>f.value=null)},null,8,["persona"])])])):S("",!0)])}}},He=E(Fe,[["__scopeId","data-v-255f109f"]]);export{He as default}; diff --git a/frontend/dist/assets/GroupEdit-XuXM9ZDv.css b/frontend/dist/assets/GroupEdit-XuXM9ZDv.css new file mode 100644 index 0000000..6087b76 --- /dev/null +++ b/frontend/dist/assets/GroupEdit-XuXM9ZDv.css @@ -0,0 +1 @@ +.sec[data-v-59429170]{margin-top:20px}.sec h4[data-v-59429170]{margin:0 0 10px;padding-bottom:6px;border-bottom:1px solid var(--border)}.row[data-v-59429170]{display:flex;gap:12px;flex-wrap:wrap}.f[data-v-59429170]{flex:1;min-width:140px}label[data-v-59429170]{font-size:13px;color:var(--muted);display:block;margin:10px 0 4px}input[data-v-59429170],select[data-v-59429170],textarea[data-v-59429170]{width:100%}.actions[data-v-59429170]{margin-top:20px}.locked[data-v-59429170]{background:#fffaf5;border:1px dashed #d6c7a1;border-radius:12px;padding:12px 14px}.locked h4[data-v-59429170]{color:#b45309}.grid[data-v-255f109f]{display:grid;grid-template-columns:repeat(auto-fill,minmax(240px,1fr));gap:14px}.pcard[data-v-255f109f]{display:flex;flex-direction:column}.edit-btn[data-v-255f109f]{margin-top:auto;display:inline-flex;align-items:center;gap:6px}.star[data-v-255f109f]{color:#d8dbe3}.star.on[data-v-255f109f]{color:#f59e0b}.badge.tier-a[data-v-255f109f]{background:#dcfce7;color:#166534}.badge.tier-b[data-v-255f109f]{background:#fef9c3;color:#854d0e}.badge.tier-c[data-v-255f109f]{background:#fee2e2;color:#991b1b}.guide[data-v-255f109f]{background:#eef2ff;border-color:#c7d2fe;margin-bottom:16px}.modal-backdrop[data-v-255f109f]{position:fixed;top:0;right:0;bottom:0;left:0;background:#0f172a80;display:flex;justify-content:center;align-items:flex-start;padding:40px 16px;z-index:50;overflow:auto}.modal[data-v-255f109f]{background:#fff;border-radius:14px;padding:24px;width:100%;max-width:720px;box-shadow:0 20px 50px #00000040} diff --git a/frontend/dist/assets/Guide-D7TwTcNd.js b/frontend/dist/assets/Guide-CxzIj9kO.js similarity index 95% rename from frontend/dist/assets/Guide-D7TwTcNd.js rename to frontend/dist/assets/Guide-CxzIj9kO.js index c3accae..cf16507 100644 --- a/frontend/dist/assets/Guide-D7TwTcNd.js +++ b/frontend/dist/assets/Guide-CxzIj9kO.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-D72MDfqC.js";import{B as g}from"./book-open-hzON0xcx.js";import{L as p}from"./layout-dashboard-DnOEx-ds.js";import{T as m}from"./target-BHkg_ns5.js";import{S as y}from"./sparkles-PhamuQBi.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-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";/** * @license lucide-vue-next v1.0.0 - ISC * * This source code is licensed under the ISC license. diff --git a/frontend/dist/assets/Login-BVI-qGSk.js b/frontend/dist/assets/Login-CaF0UGOL.js similarity index 98% rename from frontend/dist/assets/Login-BVI-qGSk.js rename to frontend/dist/assets/Login-CaF0UGOL.js index 5d8e3ab..5204ce6 100644 --- a/frontend/dist/assets/Login-BVI-qGSk.js +++ b/frontend/dist/assets/Login-CaF0UGOL.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-D72MDfqC.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-Dyfiqyex.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-DAe3xT7M.js b/frontend/dist/assets/MyBoard-BBr3kpxa.js similarity index 94% rename from frontend/dist/assets/MyBoard-DAe3xT7M.js rename to frontend/dist/assets/MyBoard-BBr3kpxa.js index 64dae6c..add4292 100644 --- a/frontend/dist/assets/MyBoard-DAe3xT7M.js +++ b/frontend/dist/assets/MyBoard-BBr3kpxa.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-D72MDfqC.js";import{L as V}from"./layout-dashboard-DnOEx-ds.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-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}; diff --git a/frontend/dist/assets/Personas-sBoT-XQc.js b/frontend/dist/assets/Personas-CzGYN8e6.js similarity index 95% rename from frontend/dist/assets/Personas-sBoT-XQc.js rename to frontend/dist/assets/Personas-CzGYN8e6.js index 3d3f5da..3f3b2b4 100644 --- a/frontend/dist/assets/Personas-sBoT-XQc.js +++ b/frontend/dist/assets/Personas-CzGYN8e6.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-D72MDfqC.js";import{T as F}from"./target-BHkg_ns5.js";import{A as j}from"./arrow-left-BuPfyTxu.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}; +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/Settings-BF-9-9N5.js b/frontend/dist/assets/Settings-IzLUgL3N.js similarity index 98% rename from frontend/dist/assets/Settings-BF-9-9N5.js rename to frontend/dist/assets/Settings-IzLUgL3N.js index cb48083..4ad6efe 100644 --- a/frontend/dist/assets/Settings-BF-9-9N5.js +++ b/frontend/dist/assets/Settings-IzLUgL3N.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-D72MDfqC.js";import{L as D}from"./lock-BM8eMwHW.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-Dyfiqyex.js";import{L as D}from"./lock-cj79Hj37.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-BogYTfwo.js b/frontend/dist/assets/Setup-DOZNu22f.js similarity index 94% rename from frontend/dist/assets/Setup-BogYTfwo.js rename to frontend/dist/assets/Setup-DOZNu22f.js index fc5d4c7..fc67200 100644 --- a/frontend/dist/assets/Setup-BogYTfwo.js +++ b/frontend/dist/assets/Setup-DOZNu22f.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-D72MDfqC.js";import{L as C}from"./lock-BM8eMwHW.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-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}; diff --git a/frontend/dist/assets/Training-DovmDhn_.js b/frontend/dist/assets/Training-Baew-jzp.js similarity index 95% rename from frontend/dist/assets/Training-DovmDhn_.js rename to frontend/dist/assets/Training-Baew-jzp.js index 457de52..3ff376b 100644 --- a/frontend/dist/assets/Training-DovmDhn_.js +++ b/frontend/dist/assets/Training-Baew-jzp.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-D72MDfqC.js";import{T as P}from"./target-BHkg_ns5.js";import{B as V}from"./book-open-hzON0xcx.js";import{P as $}from"./plus-BI5BRyQZ.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-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";/** * @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-BuPfyTxu.js b/frontend/dist/assets/arrow-left-C95UZZnG.js similarity index 86% rename from frontend/dist/assets/arrow-left-BuPfyTxu.js rename to frontend/dist/assets/arrow-left-C95UZZnG.js index 2a005d9..6b0b69f 100644 --- a/frontend/dist/assets/arrow-left-BuPfyTxu.js +++ b/frontend/dist/assets/arrow-left-C95UZZnG.js @@ -1,4 +1,4 @@ -import{c as e}from"./index-D72MDfqC.js";/** +import{c as e}from"./index-Dyfiqyex.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-hzON0xcx.js b/frontend/dist/assets/book-open-BVimbpEo.js similarity index 90% rename from frontend/dist/assets/book-open-hzON0xcx.js rename to frontend/dist/assets/book-open-BVimbpEo.js index 6e4f9bc..b4a4b19 100644 --- a/frontend/dist/assets/book-open-hzON0xcx.js +++ b/frontend/dist/assets/book-open-BVimbpEo.js @@ -1,4 +1,4 @@ -import{c as a}from"./index-D72MDfqC.js";/** +import{c as a}from"./index-Dyfiqyex.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-D72MDfqC.js b/frontend/dist/assets/index-Dyfiqyex.js similarity index 98% rename from frontend/dist/assets/index-D72MDfqC.js rename to frontend/dist/assets/index-Dyfiqyex.js index 3044924..1cf01ab 100644 --- a/frontend/dist/assets/index-D72MDfqC.js +++ b/frontend/dist/assets/index-Dyfiqyex.js @@ -1,4 +1,4 @@ -const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/Login-BVI-qGSk.js","assets/Login-j4sHK_z1.css","assets/Setup-BogYTfwo.js","assets/lock-BM8eMwHW.js","assets/Setup-BHyRmSn1.css","assets/Analytics-DDiLGx9N.js","assets/users-CHXJwc1N.js","assets/plus-BI5BRyQZ.js","assets/Analytics-CQgwlaXy.css","assets/MyBoard-DAe3xT7M.js","assets/layout-dashboard-DnOEx-ds.js","assets/MyBoard-B7ypQ1JT.css","assets/Training-DovmDhn_.js","assets/target-BHkg_ns5.js","assets/book-open-hzON0xcx.js","assets/Training-Cb1U9s84.css","assets/Personas-sBoT-XQc.js","assets/arrow-left-BuPfyTxu.js","assets/Personas-ha4vZsih.css","assets/Chat-LyydvOUA.js","assets/Chat-CyoReMui.css","assets/Settings-BF-9-9N5.js","assets/Settings-BCN2EZQ5.css","assets/Guide-D7TwTcNd.js","assets/sparkles-PhamuQBi.js","assets/GroupBuilder-CFI3DZAc.js","assets/GroupBuilder-7-9ZskJo.css","assets/GroupEdit-BEoo7c6X.js","assets/GroupEdit-Bujf8Tz9.css","assets/AdminUsers-BeTxTP9Z.js","assets/AdminUsers-B0sl0uHS.css"])))=>i.map(i=>d[i]); +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]); (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-BVI-qGSk. * * 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-BVI-qGSk.js"),__vite__mapDeps([0,1])),meta:{public:!0}},{path:"/setup",component:()=>Se(()=>import("./Setup-BogYTfwo.js"),__vite__mapDeps([2,3,4]))},{path:"/",component:()=>Se(()=>import("./Analytics-DDiLGx9N.js"),__vite__mapDeps([5,6,7,8])),meta:{admin:!0}},{path:"/my/board",component:()=>Se(()=>import("./MyBoard-DAe3xT7M.js"),__vite__mapDeps([9,10,11]))},{path:"/training",component:()=>Se(()=>import("./Training-DovmDhn_.js"),__vite__mapDeps([12,13,14,7,15]))},{path:"/groups/:gid/personas",component:()=>Se(()=>import("./Personas-sBoT-XQc.js"),__vite__mapDeps([16,13,17,18]))},{path:"/groups/:gid/chat/:pid",component:()=>Se(()=>import("./Chat-LyydvOUA.js"),__vite__mapDeps([19,13,17,20]))},{path:"/settings",component:()=>Se(()=>import("./Settings-BF-9-9N5.js"),__vite__mapDeps([21,3,22]))},{path:"/guide",component:()=>Se(()=>import("./Guide-D7TwTcNd.js"),__vite__mapDeps([23,14,10,13,24]))},{path:"/admin/new-group",component:()=>Se(()=>import("./GroupBuilder-CFI3DZAc.js"),__vite__mapDeps([25,17,26])),meta:{admin:!0}},{path:"/admin/groups/:gid/edit",component:()=>Se(()=>import("./GroupEdit-BEoo7c6X.js"),__vite__mapDeps([27,6,24,28])),meta:{admin:!0}},{path:"/admin/users",component:()=>Se(()=>import("./AdminUsers-BeTxTP9Z.js"),__vite__mapDeps([29,6,30])),meta:{admin:!0}},{path:"/admin/analytics",component:()=>Se(()=>import("./Analytics-DDiLGx9N.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-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}; diff --git a/frontend/dist/assets/layout-dashboard-DnOEx-ds.js b/frontend/dist/assets/layout-dashboard-BeK4EeBd.js similarity index 91% rename from frontend/dist/assets/layout-dashboard-DnOEx-ds.js rename to frontend/dist/assets/layout-dashboard-BeK4EeBd.js index 79245a1..10e39f0 100644 --- a/frontend/dist/assets/layout-dashboard-DnOEx-ds.js +++ b/frontend/dist/assets/layout-dashboard-BeK4EeBd.js @@ -1,4 +1,4 @@ -import{c as t}from"./index-D72MDfqC.js";/** +import{c as t}from"./index-Dyfiqyex.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-BM8eMwHW.js b/frontend/dist/assets/lock-cj79Hj37.js similarity index 88% rename from frontend/dist/assets/lock-BM8eMwHW.js rename to frontend/dist/assets/lock-cj79Hj37.js index 4ea29ae..fec49ba 100644 --- a/frontend/dist/assets/lock-BM8eMwHW.js +++ b/frontend/dist/assets/lock-cj79Hj37.js @@ -1,4 +1,4 @@ -import{c as e}from"./index-D72MDfqC.js";/** +import{c as e}from"./index-Dyfiqyex.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-BI5BRyQZ.js b/frontend/dist/assets/plus-BRv2cnuI.js similarity index 86% rename from frontend/dist/assets/plus-BI5BRyQZ.js rename to frontend/dist/assets/plus-BRv2cnuI.js index 65370f5..493e8c1 100644 --- a/frontend/dist/assets/plus-BI5BRyQZ.js +++ b/frontend/dist/assets/plus-BRv2cnuI.js @@ -1,4 +1,4 @@ -import{c as e}from"./index-D72MDfqC.js";/** +import{c as e}from"./index-Dyfiqyex.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-PhamuQBi.js b/frontend/dist/assets/sparkles-x6AFhIMl.js similarity index 93% rename from frontend/dist/assets/sparkles-PhamuQBi.js rename to frontend/dist/assets/sparkles-x6AFhIMl.js index 0726752..966ebc1 100644 --- a/frontend/dist/assets/sparkles-PhamuQBi.js +++ b/frontend/dist/assets/sparkles-x6AFhIMl.js @@ -1,4 +1,4 @@ -import{c as a}from"./index-D72MDfqC.js";/** +import{c as a}from"./index-Dyfiqyex.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-BHkg_ns5.js b/frontend/dist/assets/target-D9jhWfH4.js similarity index 90% rename from frontend/dist/assets/target-BHkg_ns5.js rename to frontend/dist/assets/target-D9jhWfH4.js index 169525f..25f57fd 100644 --- a/frontend/dist/assets/target-BHkg_ns5.js +++ b/frontend/dist/assets/target-D9jhWfH4.js @@ -1,4 +1,4 @@ -import{c}from"./index-D72MDfqC.js";/** +import{c}from"./index-Dyfiqyex.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-CHXJwc1N.js b/frontend/dist/assets/users-B6PgAwnZ.js similarity index 90% rename from frontend/dist/assets/users-CHXJwc1N.js rename to frontend/dist/assets/users-B6PgAwnZ.js index 9257ba9..b009df6 100644 --- a/frontend/dist/assets/users-CHXJwc1N.js +++ b/frontend/dist/assets/users-B6PgAwnZ.js @@ -1,4 +1,4 @@ -import{c as e}from"./index-D72MDfqC.js";/** +import{c as e}from"./index-Dyfiqyex.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 7465f68..1bca291 100644 --- a/frontend/dist/index.html +++ b/frontend/dist/index.html @@ -4,7 +4,7 @@ Sales Trainer - + diff --git a/frontend/src/components/PersonaForm.vue b/frontend/src/components/PersonaForm.vue index 11a0538..f85d237 100644 --- a/frontend/src/components/PersonaForm.vue +++ b/frontend/src/components/PersonaForm.vue @@ -87,7 +87,13 @@ const objectionsText = ref('') const negLeversText = ref('') const busy = ref(false) -function toLines(a) { return Array.isArray(a) ? a.join('\n') : String(a || '') } +function toLines(a) { + if (!a) return '' + if (Array.isArray(a)) { + return a.map((x) => (typeof x === 'string' ? x : (x && x.description) || (x && x.text) || '')).filter(Boolean).join('\n') + } + return String(a) +} function fromLines(s) { return s.split('\n').map(x => x.trim()).filter(Boolean) } watch(() => props.persona, (p) => { @@ -109,7 +115,7 @@ watch(() => props.persona, (p) => { function save() { emit('save', { ...d, - pains: fromLines(painsText.value), + pains: fromLines(painsText.value).map((t) => ({ description: t })), objections: fromLines(objectionsText.value), negotiation_levers: fromLines(negLeversText.value), }) diff --git a/frontend/src/views/GroupEdit.vue b/frontend/src/views/GroupEdit.vue index 8997cb3..95da435 100644 --- a/frontend/src/views/GroupEdit.vue +++ b/frontend/src/views/GroupEdit.vue @@ -43,7 +43,12 @@
{{ p.profession }} · {{ p.age_group }} · {{ p.location }}
{{ p.personality }}
- +
+ + + + +
@@ -60,7 +65,7 @@