From 5b167b4546d378b8f7892b4290c7b443938c6198 Mon Sep 17 00:00:00 2001 From: Macky Date: Sun, 9 Aug 2026 10:20:10 +0700 Subject: [PATCH] feat(training): delete group button (admin only) on Training page - Backend DELETE /api/groups/ (admin, own-org enforced via _get_owned_group); cascades removal of the group's sessions. GroupStore.delete added. - Training.vue: each group card gets a trash (Trash2 line icon) delete button shown only to admins (auth.isAdmin); confirm dialog; @click.stop so it doesn't navigate. Card restructured so the delete button is not inside the clickable router-link. - test_user_journey: trainee cannot delete (403), admin deletes, group gone (404). All suites pass. Rebuilt dist. --- backend/app/api/group_routes.py | 22 ++++++++ backend/app/services/groups.py | 4 ++ backend/scripts/test_user_journey.py | 10 ++++ ...ers-B98Kq2kn.js => AdminUsers-BZpNytKz.js} | 4 +- ...tics-C8V4bNnH.js => Analytics-C9ppufAi.js} | 2 +- .../{Chat-5M8zA86W.js => Chat-pM87eouK.js} | 4 +- ...r-0QeIeG8Q.js => GroupBuilder-DP_16_kW.js} | 8 +-- ...Edit-yRcPSwSE.js => GroupEdit-46rsA4b7.js} | 8 +-- .../{Guide-B9uxl_Jn.js => Guide-D116suSn.js} | 2 +- .../{Login-O3bvqwVm.js => Login-BxCw0ggt.js} | 2 +- ...yBoard-Daz4fzbD.js => MyBoard-Bw1xIyZv.js} | 2 +- ...sonas-B92S9P5I.js => Personas-OK6zEMMZ.js} | 2 +- ...tings-C4qI85q5.js => Settings-C96o5eMo.js} | 2 +- .../{Setup-H6LANAbr.js => Setup-C4CPiNJG.js} | 2 +- frontend/dist/assets/Training-BYBkfggr.css | 1 - frontend/dist/assets/Training-Ctr9xSAG.js | 6 +++ frontend/dist/assets/Training-DTTvOhJR.js | 1 - frontend/dist/assets/Training-onRzuNB5.css | 1 + ...eft-6ACDPyW9.js => arrow-left-DF5NIPgR.js} | 2 +- ...open-o2ZLc9m6.js => book-open-BPDJkLfy.js} | 2 +- .../{index-DNAVmyB5.js => index-CvsGOA4u.js} | 4 +- ...9BywAR.js => layout-dashboard-k0A07o6y.js} | 2 +- .../{lock-fipmYN8_.js => lock-Lq5iEyDR.js} | 2 +- .../{plus-BGdYQyzB.js => plus-BSRzoT5b.js} | 2 +- ...rkles-0MI5vUsR.js => sparkles-BfSk-aam.js} | 2 +- ...{target-BYJn0LyF.js => target-BHq78myU.js} | 2 +- .../{users-DG0hBplh.js => users-Cs9RtsVY.js} | 2 +- frontend/dist/index.html | 2 +- frontend/src/api/index.js | 1 + frontend/src/views/Training.vue | 51 +++++++++++++++---- 30 files changed, 114 insertions(+), 43 deletions(-) rename frontend/dist/assets/{AdminUsers-B98Kq2kn.js => AdminUsers-BZpNytKz.js} (85%) rename frontend/dist/assets/{Analytics-C8V4bNnH.js => Analytics-C9ppufAi.js} (97%) rename frontend/dist/assets/{Chat-5M8zA86W.js => Chat-pM87eouK.js} (85%) rename frontend/dist/assets/{GroupBuilder-0QeIeG8Q.js => GroupBuilder-DP_16_kW.js} (91%) rename frontend/dist/assets/{GroupEdit-yRcPSwSE.js => GroupEdit-46rsA4b7.js} (91%) rename frontend/dist/assets/{Guide-B9uxl_Jn.js => Guide-D116suSn.js} (94%) rename frontend/dist/assets/{Login-O3bvqwVm.js => Login-BxCw0ggt.js} (98%) rename frontend/dist/assets/{MyBoard-Daz4fzbD.js => MyBoard-Bw1xIyZv.js} (94%) rename frontend/dist/assets/{Personas-B92S9P5I.js => Personas-OK6zEMMZ.js} (95%) rename frontend/dist/assets/{Settings-C4qI85q5.js => Settings-C96o5eMo.js} (98%) rename frontend/dist/assets/{Setup-H6LANAbr.js => Setup-C4CPiNJG.js} (94%) delete mode 100644 frontend/dist/assets/Training-BYBkfggr.css create mode 100644 frontend/dist/assets/Training-Ctr9xSAG.js delete mode 100644 frontend/dist/assets/Training-DTTvOhJR.js create mode 100644 frontend/dist/assets/Training-onRzuNB5.css rename frontend/dist/assets/{arrow-left-6ACDPyW9.js => arrow-left-DF5NIPgR.js} (86%) rename frontend/dist/assets/{book-open-o2ZLc9m6.js => book-open-BPDJkLfy.js} (90%) rename frontend/dist/assets/{index-DNAVmyB5.js => index-CvsGOA4u.js} (86%) rename frontend/dist/assets/{layout-dashboard-dl9BywAR.js => layout-dashboard-k0A07o6y.js} (91%) rename frontend/dist/assets/{lock-fipmYN8_.js => lock-Lq5iEyDR.js} (88%) rename frontend/dist/assets/{plus-BGdYQyzB.js => plus-BSRzoT5b.js} (86%) rename frontend/dist/assets/{sparkles-0MI5vUsR.js => sparkles-BfSk-aam.js} (93%) rename frontend/dist/assets/{target-BYJn0LyF.js => target-BHq78myU.js} (90%) rename frontend/dist/assets/{users-DG0hBplh.js => users-Cs9RtsVY.js} (90%) diff --git a/backend/app/api/group_routes.py b/backend/app/api/group_routes.py index a3ed874..bd893ce 100644 --- a/backend/app/api/group_routes.py +++ b/backend/app/api/group_routes.py @@ -324,6 +324,28 @@ def update_persona(gid: str, pid: str): return jsonify({"persona": strip_secret_fields(full)}) +@groups_bp.delete("/") +@require_auth +@require_roles("admin") +def delete_group(gid: str): + """Delete a persona group (admin only, own org).""" + s = _stores() + group = _get_owned_group(s, gid) # also enforces tenant org (super_admin global) + s["groups"].delete(gid) + # cascade: also remove finished/active sessions for this group's personas + try: + sess = s.get("session_store") + if sess and hasattr(sess, "sessions"): + for r in sess.sessions.all(): + if r.get("group_id") == gid: + key = r.get("id") or r.get("sid") + if key: + sess.sessions.delete(key) + except Exception: + pass + return jsonify({"ok": True, "deleted": gid}) + + @groups_bp.post("//reanalyze") @require_auth @require_roles("admin") diff --git a/backend/app/services/groups.py b/backend/app/services/groups.py index 28578fb..b9d7e7a 100644 --- a/backend/app/services/groups.py +++ b/backend/app/services/groups.py @@ -95,3 +95,7 @@ class GroupStore: if not found: raise ValueError("persona not found") return self.groups.replace(gid, group) + + def delete(self, gid: str) -> None: + """Hard-delete a group (personas/report included).""" + self.groups.delete(gid) diff --git a/backend/scripts/test_user_journey.py b/backend/scripts/test_user_journey.py index 496111c..092e6bb 100644 --- a/backend/scripts/test_user_journey.py +++ b/backend/scripts/test_user_journey.py @@ -93,4 +93,14 @@ after = len(r.get_json()["personas"]) assert after > before, f"append should add personas (before={before}, after={after})" print(f"[ok] append adds personas ({before} -> {after}); existing kept") +# 12. delete group: trainee cannot, admin can +r = C.delete(f"/api/groups/{gid}", headers=TH) +assert r.status_code == 403, ("trainee must not delete group", r.status_code) +print("[ok] trainee cannot delete group (403)") +r = C.delete(f"/api/groups/{gid}", headers=AH) +assert r.status_code == 200, r.get_json() +gone = C.get(f"/api/groups/{gid}", headers=AH) +assert gone.status_code == 404, "deleted group should be gone" +print("[ok] admin deletes group; group no longer reachable") + print("ALL USER-JOURNEY FLOW TESTS PASSED") diff --git a/frontend/dist/assets/AdminUsers-B98Kq2kn.js b/frontend/dist/assets/AdminUsers-BZpNytKz.js similarity index 85% rename from frontend/dist/assets/AdminUsers-B98Kq2kn.js rename to frontend/dist/assets/AdminUsers-BZpNytKz.js index 0b79842..faf17e4 100644 --- a/frontend/dist/assets/AdminUsers-B98Kq2kn.js +++ b/frontend/dist/assets/AdminUsers-BZpNytKz.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,G 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-DNAVmyB5.js";import{U as z}from"./users-DG0hBplh.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-CvsGOA4u.js";import{U as z}from"./users-Cs9RtsVY.js";/** * @license lucide-vue-next v1.0.0 - ISC * * This source code is licensed under the ISC license. @@ -8,4 +8,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 a * * This source code is licensed under the ISC license. * See the LICENSE file in the root directory of this source tree. - */const N=k("user-plus",[["path",{d:"M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2",key:"1yyitq"}],["circle",{cx:"9",cy:"7",r:"4",key:"nufk8"}],["line",{x1:"19",x2:"19",y1:"8",y2:"14",key:"1bvyxn"}],["line",{x1:"22",x2:"16",y1:"11",y2:"11",key:"1shjgl"}]]),B={class:"card guide"},I={class:"card",style:{"margin-bottom":"16px"}},A={style:{"margin-top":"0"}},D={class:"row"},F={class:"row",style:{"margin-top":"10px"}},S=["disabled"],T={key:0,class:"error"},j={key:0,class:"card empty-state"},q={style:{flex:"1"}},E={class:"badge"},G={__name:"AdminUsers",setup(H){const c=y([]),d=y(""),t=y({username:"",name:"",password:"",role:"user"});async function g(){c.value=(await w.adminListUsers()).users}async function _(){d.value="";try{await w.adminCreateUser({...t.value}),t.value={username:"",name:"",password:"",role:"user"},await g()}catch(x){d.value=x.message}}return b(g),(x,s)=>(i(),r("div",null,[e("h2",null,[m(o(z),{size:22,"stroke-width":1.8,style:{"vertical-align":"-4px"}}),l(" "+n(o(u).t("users")),1)]),e("div",B,[e("strong",null,[m(o(L),{size:17,"stroke-width":1.8,style:{"vertical-align":"-3px"}}),s[4]||(s[4]=l(" วิธีเพิ่มผู้ใช้งาน",-1))]),s[5]||(s[5]=e("ol",{style:{margin:"8px 0 0","padding-left":"20px","line-height":"1.8"}},[e("li",null,[l("กรอก "),e("strong",null,"ชื่อผู้ใช้ (สำหรับเข้าสู่ระบบ)"),l(", ชื่อ, และรหัสผ่านเริ่มต้น")]),e("li",null,[l("เลือกสิทธิ์: "),e("strong",null,"user"),l(" = ผู้เข้าฝึกขาย, "),e("strong",null,"admin"),l(" = จัดการระบบ")]),e("li",null,[l("กด "),e("strong",null,"สร้าง"),l(" — นำชื่อผู้ใช้ + รหัสผ่านไปแจ้งให้ผู้ใช้นั้นเข้าสู่ระบบได้เลย")])],-1))]),e("div",I,[e("h4",A,[m(o(N),{size:18,"stroke-width":1.8,style:{"vertical-align":"-3px"}}),l(" + "+n(o(u).t("create"))+" "+n(o(u).t("users").toLowerCase()),1)]),e("div",D,[p(e("input",{"onUpdate:modelValue":s[0]||(s[0]=a=>t.value.username=a),placeholder:"ชื่อผู้ใช้ (เข้าสู่ระบบ)",style:{flex:"1"}},null,512),[[v,t.value.username]]),p(e("input",{"onUpdate:modelValue":s[1]||(s[1]=a=>t.value.name=a),placeholder:"ชื่อจริง",style:{flex:"1"}},null,512),[[v,t.value.name]]),p(e("input",{"onUpdate:modelValue":s[2]||(s[2]=a=>t.value.password=a),type:"password",placeholder:"รหัสผ่านเริ่มต้น",style:{flex:"1"}},null,512),[[v,t.value.password]])]),e("div",F,[p(e("select",{"onUpdate:modelValue":s[3]||(s[3]=a=>t.value.role=a),style:{flex:"1"}},[...s[6]||(s[6]=[e("option",{value:"user"},"🙂 user — ผู้เข้าฝึก",-1),e("option",{value:"admin"},"🛠 admin — ผู้ดูแลระบบ",-1)])],512),[[U,t.value.role]]),e("button",{class:"primary",onClick:_,disabled:!t.value.username||!t.value.password},"+ "+n(o(u).t("create")),9,S)]),d.value?(i(),r("div",T,n(d.value),1)):f("",!0)]),c.value.length===0?(i(),r("div",j,[...s[7]||(s[7]=[e("strong",null,"ยังไม่มีผู้ใช้งาน",-1),e("span",null,"สร้างผู้ใช้งานคนแรกด้วยฟอร์มด้านบน",-1)])])):f("",!0),(i(!0),r(V,null,C(c.value,a=>(i(),r("div",{class:"card",key:a.id,style:{"margin-bottom":"8px",display:"flex","align-items":"center",gap:"12px"}},[e("strong",q,n(a.name)+" ("+n(a.username)+")",1),e("span",E,n(a.role),1),e("span",{class:M(["badge",a.active?"won":"lost"])},n(a.active?"active":"inactive"),3)]))),128))]))}},K=h(G,[["__scopeId","data-v-924406be"]]);export{K as default}; + */const N=k("user-plus",[["path",{d:"M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2",key:"1yyitq"}],["circle",{cx:"9",cy:"7",r:"4",key:"nufk8"}],["line",{x1:"19",x2:"19",y1:"8",y2:"14",key:"1bvyxn"}],["line",{x1:"22",x2:"16",y1:"11",y2:"11",key:"1shjgl"}]]),B={class:"card guide"},I={class:"card",style:{"margin-bottom":"16px"}},A={style:{"margin-top":"0"}},D={class:"row"},F={class:"row",style:{"margin-top":"10px"}},H=["disabled"],S={key:0,class:"error"},T={key:0,class:"card empty-state"},j={style:{flex:"1"}},q={class:"badge"},E={__name:"AdminUsers",setup(P){const c=y([]),d=y(""),t=y({username:"",name:"",password:"",role:"user"});async function g(){c.value=(await w.adminListUsers()).users}async function _(){d.value="";try{await w.adminCreateUser({...t.value}),t.value={username:"",name:"",password:"",role:"user"},await g()}catch(x){d.value=x.message}}return b(g),(x,s)=>(i(),r("div",null,[e("h2",null,[m(o(z),{size:22,"stroke-width":1.8,style:{"vertical-align":"-4px"}}),l(" "+n(o(u).t("users")),1)]),e("div",B,[e("strong",null,[m(o(L),{size:17,"stroke-width":1.8,style:{"vertical-align":"-3px"}}),s[4]||(s[4]=l(" วิธีเพิ่มผู้ใช้งาน",-1))]),s[5]||(s[5]=e("ol",{style:{margin:"8px 0 0","padding-left":"20px","line-height":"1.8"}},[e("li",null,[l("กรอก "),e("strong",null,"ชื่อผู้ใช้ (สำหรับเข้าสู่ระบบ)"),l(", ชื่อ, และรหัสผ่านเริ่มต้น")]),e("li",null,[l("เลือกสิทธิ์: "),e("strong",null,"user"),l(" = ผู้เข้าฝึกขาย, "),e("strong",null,"admin"),l(" = จัดการระบบ")]),e("li",null,[l("กด "),e("strong",null,"สร้าง"),l(" — นำชื่อผู้ใช้ + รหัสผ่านไปแจ้งให้ผู้ใช้นั้นเข้าสู่ระบบได้เลย")])],-1))]),e("div",I,[e("h4",A,[m(o(N),{size:18,"stroke-width":1.8,style:{"vertical-align":"-3px"}}),l(" + "+n(o(u).t("create"))+" "+n(o(u).t("users").toLowerCase()),1)]),e("div",D,[p(e("input",{"onUpdate:modelValue":s[0]||(s[0]=a=>t.value.username=a),placeholder:"ชื่อผู้ใช้ (เข้าสู่ระบบ)",style:{flex:"1"}},null,512),[[v,t.value.username]]),p(e("input",{"onUpdate:modelValue":s[1]||(s[1]=a=>t.value.name=a),placeholder:"ชื่อจริง",style:{flex:"1"}},null,512),[[v,t.value.name]]),p(e("input",{"onUpdate:modelValue":s[2]||(s[2]=a=>t.value.password=a),type:"password",placeholder:"รหัสผ่านเริ่มต้น",style:{flex:"1"}},null,512),[[v,t.value.password]])]),e("div",F,[p(e("select",{"onUpdate:modelValue":s[3]||(s[3]=a=>t.value.role=a),style:{flex:"1"}},[...s[6]||(s[6]=[e("option",{value:"user"},"🙂 user — ผู้เข้าฝึก",-1),e("option",{value:"admin"},"🛠 admin — ผู้ดูแลระบบ",-1)])],512),[[U,t.value.role]]),e("button",{class:"primary",onClick:_,disabled:!t.value.username||!t.value.password},"+ "+n(o(u).t("create")),9,H)]),d.value?(i(),r("div",S,n(d.value),1)):f("",!0)]),c.value.length===0?(i(),r("div",T,[...s[7]||(s[7]=[e("strong",null,"ยังไม่มีผู้ใช้งาน",-1),e("span",null,"สร้างผู้ใช้งานคนแรกด้วยฟอร์มด้านบน",-1)])])):f("",!0),(i(!0),r(V,null,C(c.value,a=>(i(),r("div",{class:"card",key:a.id,style:{"margin-bottom":"8px",display:"flex","align-items":"center",gap:"12px"}},[e("strong",j,n(a.name)+" ("+n(a.username)+")",1),e("span",q,n(a.role),1),e("span",{class:M(["badge",a.active?"won":"lost"])},n(a.active?"active":"inactive"),3)]))),128))]))}},K=h(E,[["__scopeId","data-v-924406be"]]);export{K as default}; diff --git a/frontend/dist/assets/Analytics-C8V4bNnH.js b/frontend/dist/assets/Analytics-C9ppufAi.js similarity index 97% rename from frontend/dist/assets/Analytics-C8V4bNnH.js rename to frontend/dist/assets/Analytics-C9ppufAi.js index dd19aa1..b05b30c 100644 --- a/frontend/dist/assets/Analytics-C8V4bNnH.js +++ b/frontend/dist/assets/Analytics-C9ppufAi.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-DNAVmyB5.js";import{U as P}from"./users-DG0hBplh.js";import{P as S}from"./plus-BGdYQyzB.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-CvsGOA4u.js";import{U as P}from"./users-Cs9RtsVY.js";import{P as S}from"./plus-BSRzoT5b.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-5M8zA86W.js b/frontend/dist/assets/Chat-pM87eouK.js similarity index 85% rename from frontend/dist/assets/Chat-5M8zA86W.js rename to frontend/dist/assets/Chat-pM87eouK.js index 769cce9..473939b 100644 --- a/frontend/dist/assets/Chat-5M8zA86W.js +++ b/frontend/dist/assets/Chat-pM87eouK.js @@ -1,6 +1,6 @@ -import{c as P,_ as R,p as G,y as S,a as o,l as k,q as V,u as a,b as s,t,i as n,F as T,x as L,m as g,C as w,g as h,w as K,v as $,d as q,k as H,r as d,B as J,D as O,z as U,o as l}from"./index-DNAVmyB5.js";import{T as Y}from"./target-BYJn0LyF.js";import{A as Q}from"./arrow-left-6ACDPyW9.js";/** +import{c as P,_ as R,p as G,y as S,a as o,l as k,q as D,u as a,b as s,t,i as n,F as T,x as L,m as g,C as w,g as h,w as K,v as $,d as q,k as H,r as d,B as J,E as O,z as U,o as l}from"./index-CvsGOA4u.js";import{T as Y}from"./target-BHq78myU.js";import{A as Q}from"./arrow-left-DF5NIPgR.js";/** * @license lucide-vue-next v1.0.0 - ISC * * This source code is licensed under the ISC license. * See the LICENSE file in the root directory of this source tree. - */const W=P("search",[["path",{d:"m21 21-4.34-4.34",key:"14j7rj"}],["circle",{cx:"11",cy:"11",r:"8",key:"4ej97u"}]]),X={key:0,class:"card"},Z={style:{"margin-top":"0"}},ee=["onClick"],se={class:"row",style:{"align-items":"center"}},te={key:0,class:"badge line"},ae={key:1,class:"badge facebook"},oe={class:"muted",style:{"margin-top":"6px"}},le=["disabled"],ne={key:1},ie={class:"card guide"},re={style:{margin:"6px 0 0","line-height":"1.7"}},ce={class:"row",style:{"align-items":"center","margin-bottom":"12px"}},ue={style:{margin:"0"}},de={key:0,class:"muted"},ve={key:0,class:"bubble msg-customer muted"},me={key:0,class:"composer"},pe=["disabled","placeholder"],he=["disabled"],_e={key:1,class:"card debrief"},ge={key:0},ye={key:0,class:"reveal-grid"},fe={class:"rk"},be={class:"rv"},ke={class:"primary",style:{"margin-top":"12px"}},we={__name:"Chat",setup(xe){const A=H(),y=A.params.gid,x=A.params.pid,c=d(null),u=d("pick"),v=d([]),_=d(""),f=d(!1),i=d(null),B=d(null),b=d("social"),D=J(()=>{const e=n.locale==="en";return[{id:"social",emoji:"💬",init:"customer",label:e?"Social Media (chat)":"Social Media (แชท)",desc:e?"The customer messages you first — short, chat-style replies":"ลูกค้าทักมาหาคุณก่อน — โทนสั้น ทักๆ ตามสไตล์แชท"},{id:"f2f_call",emoji:"📞",init:"seller",label:e?"Face-to-face / Phone":"พบหน้า / โทรศัพท์",desc:e?"You must proactively open the conversation (seller starts)":"คุณต้องเป็นฝ่ายเปิดการสนทนาเชิงรุกกับลีด (ผู้ฝึกทักก่อน)"},{id:"recontact",emoji:"⏳",init:"customer",label:e?"Re-contact (1-3 months)":"ลูกค้ากลับมาติดต่อ (1-3 เดือน)",desc:e?"Customer contacted you before, now returns more ready to decide":"เคยได้รับข้อมูลไปแล้ว ตอนนี้กลับมาติดต่อ พร้อมตัดสินใจมากขึ้น"}]});function z(){O(()=>{C.value&&(C.value.scrollTop=C.value.scrollHeight)})}const C=d(null);async function F(){u.value="chat";const e=await S.chatStart(y,x,b.value,n.locale);B.value=e.session.id,v.value=e.session.messages||[],v.value.length&&z()}G(async()=>{c.value=(await S.getPersona(y,x)).persona;try{const e=await S.chatResume(y,x);B.value=e.session.id,v.value=e.session.messages||[],v.value.length&&!e.session.debrief?(u.value="chat",D.find(j=>j.id===e.scenario)&&(b.value=e.scenario)):e.session.debrief&&(i.value=e.session.debrief,v.value=e.session.messages||[],u.value="done"),v.value.length&&z()}catch{u.value="pick"}});async function N(){if(_.value.trim()){f.value=!0;try{const e=await S.chatSend(y,x,_.value.trim());v.value=e.messages,_.value="",e.finished&&(i.value=e.debrief||null,u.value="done"),z()}catch(e){alert(e.message)}finally{f.value=!1}}}const I={name:"ชื่อ",tier:"ระดับ",difficulty:"ความยาก",profession:"อาชีพ",age_group:"ช่วงอายุ",location:"พื้นที่",income:"รายได้",budget:"งบประมาณ",lifestyle:"ไลฟ์สไตล์",background:"ภูมิหลัง",personality:"บุคลิก",communication_style:"สไตล์การสื่อสาร",goal:"เป้าหมาย",decision_timeline:"กรอบตัดสินใจ",pains:"ปัญหา (Pain)",objections:"ข้อโต้แย้ง",negotiation_levers:"สิ่งที่ใช้ต่อรอง",opener:"บทเปิดบทสนทนา",channel:"ช่องทาง",initiation_mode:"ใครเริ่มก่อน",product_context:"บริบทสินค้า"};function M(e){return I[e]||e}function E(e){return Array.isArray(e)?e.join(", "):e&&typeof e=="object"?JSON.stringify(e):String(e??"—")}return(e,m)=>{const j=U("router-link");return l(),o("div",null,[k(j,{to:`/groups/${a(y)}/personas`,class:"btn-back"},{default:V(()=>[k(a(Q),{size:16,"stroke-width":2}),g(" "+t(a(n).t("personas")),1)]),_:1},8,["to"]),u.value==="pick"?(l(),o("div",X,[s("h3",Z,"🎬 "+t(a(n).t("chooseScenario")),1),m[1]||(m[1]=s("p",{class:"muted",style:{"margin-top":"0"}},"เลือกสถานการณ์ที่อยากฝึกการขาย — แต่ละแบบกำหนดว่าใครทักก่อน และโทนการสนทนา",-1)),(l(!0),o(T,null,L(D.value,r=>(l(),o("div",{key:r.id,class:w(["scenario",{sel:b.value===r.id}]),onClick:p=>b.value=r.id},[s("div",se,[s("strong",null,t(r.emoji)+" "+t(r.label),1),r.init==="customer"?(l(),o("span",te,"ลูกค้าทักก่อน")):(l(),o("span",ae,"คุณต้องทักก่อน (เชิงรุก)"))]),s("div",oe,t(r.desc),1)],10,ee))),128)),s("button",{class:"primary",style:{"margin-top":"16px",width:"100%"},disabled:!b.value,onClick:F}," ▶ "+t(a(n).t("start")),9,le)])):(l(),o("div",ne,[s("div",ie,[s("strong",null,[k(a(Y),{size:17,"stroke-width":1.8,style:{"vertical-align":"-3px"}}),g(" "+t(a(n).t("chatGuideTitle")),1)]),s("p",re,t(a(n).t("chatGuideText")),1)]),s("div",ce,[s("h2",ue,t(c.value?c.value.name:"..."),1),s("span",{class:w(["badge",c.value&&c.value.channel])},t(c.value?c.value.channel:""),3),c.value?(l(),o("span",de,t(c.value.profession)+" · "+t(c.value.age_group),1)):h("",!0),u.value==="done"?(l(),o("span",{key:1,class:w(["badge",i.value&&i.value.outcome])},t(i.value&&i.value.outcome==="won"?a(n).t("won"):a(n).t("lost")),3)):h("",!0)]),s("div",{class:"thread",ref_key:"thread",ref:C},[(l(!0),o(T,null,L(v.value,(r,p)=>(l(),o("div",{key:p,class:w(["bubble",r.role==="seller"?"msg-seller":r.role==="system"?"msg-system":"msg-customer"])},t(r.text),3))),128)),f.value?(l(),o("div",ve,"...")):h("",!0)],512),u.value==="chat"?(l(),o("div",me,[K(s("input",{"onUpdate:modelValue":m[0]||(m[0]=r=>_.value=r),onKeyup:q(N,["enter"]),disabled:f.value,placeholder:a(n).t("send")},null,40,pe),[[$,_.value]]),s("button",{class:"primary",onClick:N,disabled:f.value||!_.value.trim()},t(a(n).t("send")),9,he)])):h("",!0),u.value==="done"&&i.value?(l(),o("div",_e,[s("h3",null,t(a(n).t("debrief")),1),s("p",null,[s("span",{class:w(["badge",i.value.outcome])},t(i.value.outcome==="won"?a(n).t("won"):a(n).t("lost")),3),g(" — "+t(a(n).t("score"))+": ",1),s("strong",null,t(i.value.score),1)]),s("p",null,[s("strong",null,t(a(n).t("pain"))+":",1),g(" "+t(i.value.pain||"—"),1)]),s("p",null,[s("strong",null,t(a(n).t("why"))+":",1),g(" "+t(i.value.why),1)]),i.value.coaching&&i.value.coaching.length?(l(),o("div",ge,[m[2]||(m[2]=s("strong",null,"Coaching:",-1)),s("ul",null,[(l(!0),o(T,null,L(i.value.coaching,(r,p)=>(l(),o("li",{key:p},t(r),1))),128))])])):h("",!0),s("details",null,[s("summary",null,[k(a(W),{size:15,"stroke-width":1.8,style:{"vertical-align":"-2px"}}),g(" "+t(a(n).t("reveal")),1)]),i.value.revealed_persona?(l(),o("div",ye,[(l(!0),o(T,null,L(i.value.revealed_persona,(r,p)=>(l(),o("div",{key:p,class:"rev"},[s("span",fe,t(M(p)),1),s("span",be,t(E(r)),1)]))),128))])):h("",!0)]),k(j,{to:"/"},{default:V(()=>[s("button",ke,t(a(n).t("dashboard")),1)]),_:1})])):h("",!0)]))])}}},Te=R(we,[["__scopeId","data-v-19840301"]]);export{Te as default}; + */const W=P("search",[["path",{d:"m21 21-4.34-4.34",key:"14j7rj"}],["circle",{cx:"11",cy:"11",r:"8",key:"4ej97u"}]]),X={key:0,class:"card"},Z={style:{"margin-top":"0"}},ee=["onClick"],se={class:"row",style:{"align-items":"center"}},te={key:0,class:"badge line"},ae={key:1,class:"badge facebook"},oe={class:"muted",style:{"margin-top":"6px"}},le=["disabled"],ne={key:1},ie={class:"card guide"},re={style:{margin:"6px 0 0","line-height":"1.7"}},ce={class:"row",style:{"align-items":"center","margin-bottom":"12px"}},ue={style:{margin:"0"}},de={key:0,class:"muted"},ve={key:0,class:"bubble msg-customer muted"},me={key:0,class:"composer"},pe=["disabled","placeholder"],he=["disabled"],_e={key:1,class:"card debrief"},ge={key:0},ye={key:0,class:"reveal-grid"},fe={class:"rk"},be={class:"rv"},ke={class:"primary",style:{"margin-top":"12px"}},we={__name:"Chat",setup(xe){const A=H(),y=A.params.gid,x=A.params.pid,c=d(null),u=d("pick"),v=d([]),_=d(""),f=d(!1),i=d(null),B=d(null),b=d("social"),N=J(()=>{const e=n.locale==="en";return[{id:"social",emoji:"💬",init:"customer",label:e?"Social Media (chat)":"Social Media (แชท)",desc:e?"The customer messages you first — short, chat-style replies":"ลูกค้าทักมาหาคุณก่อน — โทนสั้น ทักๆ ตามสไตล์แชท"},{id:"f2f_call",emoji:"📞",init:"seller",label:e?"Face-to-face / Phone":"พบหน้า / โทรศัพท์",desc:e?"You must proactively open the conversation (seller starts)":"คุณต้องเป็นฝ่ายเปิดการสนทนาเชิงรุกกับลีด (ผู้ฝึกทักก่อน)"},{id:"recontact",emoji:"⏳",init:"customer",label:e?"Re-contact (1-3 months)":"ลูกค้ากลับมาติดต่อ (1-3 เดือน)",desc:e?"Customer contacted you before, now returns more ready to decide":"เคยได้รับข้อมูลไปแล้ว ตอนนี้กลับมาติดต่อ พร้อมตัดสินใจมากขึ้น"}]});function z(){O(()=>{C.value&&(C.value.scrollTop=C.value.scrollHeight)})}const C=d(null);async function E(){u.value="chat";const e=await S.chatStart(y,x,b.value,n.locale);B.value=e.session.id,v.value=e.session.messages||[],v.value.length&&z()}G(async()=>{c.value=(await S.getPersona(y,x)).persona;try{const e=await S.chatResume(y,x);B.value=e.session.id,v.value=e.session.messages||[],v.value.length&&!e.session.debrief?(u.value="chat",N.find(j=>j.id===e.scenario)&&(b.value=e.scenario)):e.session.debrief&&(i.value=e.session.debrief,v.value=e.session.messages||[],u.value="done"),v.value.length&&z()}catch{u.value="pick"}});async function V(){if(_.value.trim()){f.value=!0;try{const e=await S.chatSend(y,x,_.value.trim());v.value=e.messages,_.value="",e.finished&&(i.value=e.debrief||null,u.value="done"),z()}catch(e){alert(e.message)}finally{f.value=!1}}}const F={name:"ชื่อ",tier:"ระดับ",difficulty:"ความยาก",profession:"อาชีพ",age_group:"ช่วงอายุ",location:"พื้นที่",income:"รายได้",budget:"งบประมาณ",lifestyle:"ไลฟ์สไตล์",background:"ภูมิหลัง",personality:"บุคลิก",communication_style:"สไตล์การสื่อสาร",goal:"เป้าหมาย",decision_timeline:"กรอบตัดสินใจ",pains:"ปัญหา (Pain)",objections:"ข้อโต้แย้ง",negotiation_levers:"สิ่งที่ใช้ต่อรอง",opener:"บทเปิดบทสนทนา",channel:"ช่องทาง",initiation_mode:"ใครเริ่มก่อน",product_context:"บริบทสินค้า"};function I(e){return F[e]||e}function M(e){return Array.isArray(e)?e.join(", "):e&&typeof e=="object"?JSON.stringify(e):String(e??"—")}return(e,m)=>{const j=U("router-link");return l(),o("div",null,[k(j,{to:`/groups/${a(y)}/personas`,class:"btn-back"},{default:D(()=>[k(a(Q),{size:16,"stroke-width":2}),g(" "+t(a(n).t("personas")),1)]),_:1},8,["to"]),u.value==="pick"?(l(),o("div",X,[s("h3",Z,"🎬 "+t(a(n).t("chooseScenario")),1),m[1]||(m[1]=s("p",{class:"muted",style:{"margin-top":"0"}},"เลือกสถานการณ์ที่อยากฝึกการขาย — แต่ละแบบกำหนดว่าใครทักก่อน และโทนการสนทนา",-1)),(l(!0),o(T,null,L(N.value,r=>(l(),o("div",{key:r.id,class:w(["scenario",{sel:b.value===r.id}]),onClick:p=>b.value=r.id},[s("div",se,[s("strong",null,t(r.emoji)+" "+t(r.label),1),r.init==="customer"?(l(),o("span",te,"ลูกค้าทักก่อน")):(l(),o("span",ae,"คุณต้องทักก่อน (เชิงรุก)"))]),s("div",oe,t(r.desc),1)],10,ee))),128)),s("button",{class:"primary",style:{"margin-top":"16px",width:"100%"},disabled:!b.value,onClick:E}," ▶ "+t(a(n).t("start")),9,le)])):(l(),o("div",ne,[s("div",ie,[s("strong",null,[k(a(Y),{size:17,"stroke-width":1.8,style:{"vertical-align":"-3px"}}),g(" "+t(a(n).t("chatGuideTitle")),1)]),s("p",re,t(a(n).t("chatGuideText")),1)]),s("div",ce,[s("h2",ue,t(c.value?c.value.name:"..."),1),s("span",{class:w(["badge",c.value&&c.value.channel])},t(c.value?c.value.channel:""),3),c.value?(l(),o("span",de,t(c.value.profession)+" · "+t(c.value.age_group),1)):h("",!0),u.value==="done"?(l(),o("span",{key:1,class:w(["badge",i.value&&i.value.outcome])},t(i.value&&i.value.outcome==="won"?a(n).t("won"):a(n).t("lost")),3)):h("",!0)]),s("div",{class:"thread",ref_key:"thread",ref:C},[(l(!0),o(T,null,L(v.value,(r,p)=>(l(),o("div",{key:p,class:w(["bubble",r.role==="seller"?"msg-seller":r.role==="system"?"msg-system":"msg-customer"])},t(r.text),3))),128)),f.value?(l(),o("div",ve,"...")):h("",!0)],512),u.value==="chat"?(l(),o("div",me,[K(s("input",{"onUpdate:modelValue":m[0]||(m[0]=r=>_.value=r),onKeyup:q(V,["enter"]),disabled:f.value,placeholder:a(n).t("send")},null,40,pe),[[$,_.value]]),s("button",{class:"primary",onClick:V,disabled:f.value||!_.value.trim()},t(a(n).t("send")),9,he)])):h("",!0),u.value==="done"&&i.value?(l(),o("div",_e,[s("h3",null,t(a(n).t("debrief")),1),s("p",null,[s("span",{class:w(["badge",i.value.outcome])},t(i.value.outcome==="won"?a(n).t("won"):a(n).t("lost")),3),g(" — "+t(a(n).t("score"))+": ",1),s("strong",null,t(i.value.score),1)]),s("p",null,[s("strong",null,t(a(n).t("pain"))+":",1),g(" "+t(i.value.pain||"—"),1)]),s("p",null,[s("strong",null,t(a(n).t("why"))+":",1),g(" "+t(i.value.why),1)]),i.value.coaching&&i.value.coaching.length?(l(),o("div",ge,[m[2]||(m[2]=s("strong",null,"Coaching:",-1)),s("ul",null,[(l(!0),o(T,null,L(i.value.coaching,(r,p)=>(l(),o("li",{key:p},t(r),1))),128))])])):h("",!0),s("details",null,[s("summary",null,[k(a(W),{size:15,"stroke-width":1.8,style:{"vertical-align":"-2px"}}),g(" "+t(a(n).t("reveal")),1)]),i.value.revealed_persona?(l(),o("div",ye,[(l(!0),o(T,null,L(i.value.revealed_persona,(r,p)=>(l(),o("div",{key:p,class:"rev"},[s("span",fe,t(I(p)),1),s("span",be,t(M(r)),1)]))),128))])):h("",!0)]),k(j,{to:"/"},{default:D(()=>[s("button",ke,t(a(n).t("dashboard")),1)]),_:1})])):h("",!0)]))])}}},Te=R(we,[["__scopeId","data-v-19840301"]]);export{Te as default}; diff --git a/frontend/dist/assets/GroupBuilder-0QeIeG8Q.js b/frontend/dist/assets/GroupBuilder-DP_16_kW.js similarity index 91% rename from frontend/dist/assets/GroupBuilder-0QeIeG8Q.js rename to frontend/dist/assets/GroupBuilder-DP_16_kW.js index 93af510..3c69ed0 100644 --- a/frontend/dist/assets/GroupBuilder-0QeIeG8Q.js +++ b/frontend/dist/assets/GroupBuilder-DP_16_kW.js @@ -1,14 +1,14 @@ -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,G as V,g as C,z as A,r as m,y as x,j as B,o as w}from"./index-DNAVmyB5.js";import{A as q}from"./arrow-left-6ACDPyW9.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-CvsGOA4u.js";import{A as q}from"./arrow-left-DF5NIPgR.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 G=v("box",[["path",{d:"M21 8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16Z",key:"hh9hay"}],["path",{d:"m3.3 7 8.7 5 8.7-5",key:"g66t2b"}],["path",{d:"M12 22V12",key:"d0xqtd"}]]);/** + */const E=v("box",[["path",{d:"M21 8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16Z",key:"hh9hay"}],["path",{d:"m3.3 7 8.7 5 8.7-5",key:"g66t2b"}],["path",{d:"M12 22V12",key:"d0xqtd"}]]);/** * @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 E=v("circle-plus",[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["path",{d:"M8 12h8",key:"1wcyev"}],["path",{d:"M12 8v8",key:"napkw2"}]]);/** + */const G=v("circle-plus",[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["path",{d:"M8 12h8",key:"1wcyev"}],["path",{d:"M12 8v8",key:"napkw2"}]]);/** * @license lucide-vue-next v1.0.0 - ISC * * This source code is licensed under the ISC license. @@ -18,4 +18,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 * * This source code is licensed under the ISC license. * See the LICENSE file in the root directory of this source tree. - */const P=v("paperclip",[["path",{d:"m16 6-8.414 8.586a2 2 0 0 0 2.829 2.829l8.414-8.586a4 4 0 1 0-5.657-5.657l-8.379 8.551a6 6 0 1 0 8.485 8.485l8.379-8.551",key:"1miecu"}]]),S={class:"card guide"},U={class:"card"},D={style:{"margin-top":"0"}},I={value:"th"},L={value:"en"},j={key:0,class:"error"},F=["disabled"],T={__name:"GroupBuilder",setup($){const f=B(),a=m({product:"",segment:"",description:"",language:"th"}),h=m([]),d=m(""),c=m(!1);function b(s){h.value=Array.from(s.target.files||[])}async function _(){c.value=!0,d.value="";try{const s=new FormData;s.append("product",a.value.product),s.append("segment",a.value.segment),s.append("description",a.value.description),s.append("language",a.value.language),h.value.forEach(n=>s.append("files",n));const p=(await x.createGroup(s)).group.id;try{await x.analyzeGroup(p)}catch(n){d.value=n.message,f.push(`/admin/groups/${p}/edit`);return}f.push(`/admin/groups/${p}/edit`)}catch(s){d.value=s.message}finally{c.value=!1}}return(s,t)=>{const p=A("router-link");return w(),k("div",null,[u(p,{to:"/training",class:"btn-back"},{default:z(()=>[u(l(q),{size:16,"stroke-width":2}),o(" "+r(l(i).t("training")),1)]),_:1}),e("div",S,[e("strong",null,[u(l(N),{size:18,"stroke-width":1.8,style:{"vertical-align":"-3px"}}),t[4]||(t[4]=o(" วิธีสร้างกลุ่มบุคคลต้นแบบ",-1))]),t[5]||(t[5]=e("ol",{style:{margin:"8px 0 0","padding-left":"20px","line-height":"1.8"}},[e("li",null,[o("กรอก "),e("strong",null,"สินค้า / บริการ / ไอเดีย"),o(" ที่อยากให้ทีมฝึกนำเสนอ (ช่องด้านล่าง)")]),e("li",null,"(ไม่บังคับ) ระบุกลุ่มเป้าหมาย หรืออัปโหลดไฟล์เอกสาร .pdf/.md/.txt"),e("li",null,[o("กด "),e("strong",null,"สร้าง"),o(" — ระบบจะสร้างบุคคลต้นแบบ (ลูกค้าจำลอง) ให้ทันที")]),e("li",null,[o("ถ้าอยากได้บุคคลต้นแบบเพิ่ม ไปที่หน้านั้นแล้วกด "),e("strong",null,"สร้างบุคคลต้นแบบเพิ่มเติม")])],-1))]),e("div",U,[e("h2",D,[u(l(G),{size:22,"stroke-width":1.8,style:{"vertical-align":"-4px"}}),o(" "+r(l(i).t("addProduct")),1)]),e("label",null,[o(r(l(i).t("product"))+" ",1),t[6]||(t[6]=e("span",{class:"req"},"*",-1))]),g(e("textarea",{"onUpdate:modelValue":t[0]||(t[0]=n=>a.value.product=n),rows:"3",placeholder:"สินค้า / บริการ / ไอเดีย — เช่น ระบบ POS สำหรับร้านอาหาร, บริการให้คำปรึกษา AI, แนวคิดเปิดร้านกาแฟ…"},null,512),[[y,a.value.product]]),e("label",null,r(l(i).t("segment")),1),g(e("input",{"onUpdate:modelValue":t[1]||(t[1]=n=>a.value.segment=n),placeholder:"เช่น SMEs ร้านอาหารในกรุงเทพ"},null,512),[[y,a.value.segment]]),e("label",null,r(l(i).t("description")),1),g(e("textarea",{"onUpdate:modelValue":t[2]||(t[2]=n=>a.value.description=n),rows:"3",placeholder:"รายละเอียดเพิ่มเติม เช่น จุดเด่น ราคา หรือกลุ่มคนที่เราจะนำเสนอ (ไม่บังคับ)"},null,512),[[y,a.value.description]]),e("label",null,r(l(i).t("language")),1),g(e("select",{"onUpdate:modelValue":t[3]||(t[3]=n=>a.value.language=n)},[e("option",I,r(l(i).t("thai")),1),e("option",L,r(l(i).t("english")),1)],512),[[V,a.value.language]]),e("label",null,[u(l(P),{size:15,"stroke-width":1.8,style:{"vertical-align":"-2px"}}),t[7]||(t[7]=o(" ไฟล์เอกสาร (.pdf/.md/.txt) — ใช้เป็นข้อมูลให้ระบบได้",-1))]),e("input",{type:"file",multiple:"",accept:".pdf,.md,.txt",onChange:b},null,32),d.value?(w(),k("div",j,r(d.value),1)):C("",!0),e("button",{class:"primary",style:{"margin-top":"16px",display:"inline-flex","align-items":"center",gap:"6px"},disabled:c.value||!a.value.product&&!h.value.length,onClick:_},[u(l(E),{size:18,"stroke-width":2}),o(" "+r(c.value?"กำลังสร้างบุคคลต้นแบบ…":l(i).t("create")),1)],8,F)])])}}},Z=M(T,[["__scopeId","data-v-b566e48c"]]);export{Z as default}; + */const P=v("paperclip",[["path",{d:"m16 6-8.414 8.586a2 2 0 0 0 2.829 2.829l8.414-8.586a4 4 0 1 0-5.657-5.657l-8.379 8.551a6 6 0 1 0 8.485 8.485l8.379-8.551",key:"1miecu"}]]),S={class:"card guide"},U={class:"card"},D={style:{"margin-top":"0"}},I={value:"th"},L={value:"en"},j={key:0,class:"error"},F=["disabled"],T={__name:"GroupBuilder",setup($){const f=B(),a=m({product:"",segment:"",description:"",language:"th"}),h=m([]),d=m(""),c=m(!1);function b(s){h.value=Array.from(s.target.files||[])}async function _(){c.value=!0,d.value="";try{const s=new FormData;s.append("product",a.value.product),s.append("segment",a.value.segment),s.append("description",a.value.description),s.append("language",a.value.language),h.value.forEach(n=>s.append("files",n));const p=(await x.createGroup(s)).group.id;try{await x.analyzeGroup(p)}catch(n){d.value=n.message,f.push(`/admin/groups/${p}/edit`);return}f.push(`/admin/groups/${p}/edit`)}catch(s){d.value=s.message}finally{c.value=!1}}return(s,t)=>{const p=A("router-link");return w(),k("div",null,[u(p,{to:"/training",class:"btn-back"},{default:z(()=>[u(l(q),{size:16,"stroke-width":2}),o(" "+r(l(i).t("training")),1)]),_:1}),e("div",S,[e("strong",null,[u(l(N),{size:18,"stroke-width":1.8,style:{"vertical-align":"-3px"}}),t[4]||(t[4]=o(" วิธีสร้างกลุ่มบุคคลต้นแบบ",-1))]),t[5]||(t[5]=e("ol",{style:{margin:"8px 0 0","padding-left":"20px","line-height":"1.8"}},[e("li",null,[o("กรอก "),e("strong",null,"สินค้า / บริการ / ไอเดีย"),o(" ที่อยากให้ทีมฝึกนำเสนอ (ช่องด้านล่าง)")]),e("li",null,"(ไม่บังคับ) ระบุกลุ่มเป้าหมาย หรืออัปโหลดไฟล์เอกสาร .pdf/.md/.txt"),e("li",null,[o("กด "),e("strong",null,"สร้าง"),o(" — ระบบจะสร้างบุคคลต้นแบบ (ลูกค้าจำลอง) ให้ทันที")]),e("li",null,[o("ถ้าอยากได้บุคคลต้นแบบเพิ่ม ไปที่หน้านั้นแล้วกด "),e("strong",null,"สร้างบุคคลต้นแบบเพิ่มเติม")])],-1))]),e("div",U,[e("h2",D,[u(l(E),{size:22,"stroke-width":1.8,style:{"vertical-align":"-4px"}}),o(" "+r(l(i).t("addProduct")),1)]),e("label",null,[o(r(l(i).t("product"))+" ",1),t[6]||(t[6]=e("span",{class:"req"},"*",-1))]),g(e("textarea",{"onUpdate:modelValue":t[0]||(t[0]=n=>a.value.product=n),rows:"3",placeholder:"สินค้า / บริการ / ไอเดีย — เช่น ระบบ POS สำหรับร้านอาหาร, บริการให้คำปรึกษา AI, แนวคิดเปิดร้านกาแฟ…"},null,512),[[y,a.value.product]]),e("label",null,r(l(i).t("segment")),1),g(e("input",{"onUpdate:modelValue":t[1]||(t[1]=n=>a.value.segment=n),placeholder:"เช่น SMEs ร้านอาหารในกรุงเทพ"},null,512),[[y,a.value.segment]]),e("label",null,r(l(i).t("description")),1),g(e("textarea",{"onUpdate:modelValue":t[2]||(t[2]=n=>a.value.description=n),rows:"3",placeholder:"รายละเอียดเพิ่มเติม เช่น จุดเด่น ราคา หรือกลุ่มคนที่เราจะนำเสนอ (ไม่บังคับ)"},null,512),[[y,a.value.description]]),e("label",null,r(l(i).t("language")),1),g(e("select",{"onUpdate:modelValue":t[3]||(t[3]=n=>a.value.language=n)},[e("option",I,r(l(i).t("thai")),1),e("option",L,r(l(i).t("english")),1)],512),[[V,a.value.language]]),e("label",null,[u(l(P),{size:15,"stroke-width":1.8,style:{"vertical-align":"-2px"}}),t[7]||(t[7]=o(" ไฟล์เอกสาร (.pdf/.md/.txt) — ใช้เป็นข้อมูลให้ระบบได้",-1))]),e("input",{type:"file",multiple:"",accept:".pdf,.md,.txt",onChange:b},null,32),d.value?(w(),k("div",j,r(d.value),1)):C("",!0),e("button",{class:"primary",style:{"margin-top":"16px",display:"inline-flex","align-items":"center",gap:"6px"},disabled:c.value||!a.value.product&&!h.value.length,onClick:_},[u(l(G),{size:18,"stroke-width":2}),o(" "+r(c.value?"กำลังสร้างบุคคลต้นแบบ…":l(i).t("create")),1)],8,F)])])}}},R=M(T,[["__scopeId","data-v-b566e48c"]]);export{R as default}; diff --git a/frontend/dist/assets/GroupEdit-yRcPSwSE.js b/frontend/dist/assets/GroupEdit-46rsA4b7.js similarity index 91% rename from frontend/dist/assets/GroupEdit-yRcPSwSE.js rename to frontend/dist/assets/GroupEdit-46rsA4b7.js index d9a3a34..69f9d9b 100644 --- a/frontend/dist/assets/GroupEdit-yRcPSwSE.js +++ b/frontend/dist/assets/GroupEdit-46rsA4b7.js @@ -1,9 +1,9 @@ -import{c as M,_ as G,H as O,o as d,a as m,b as l,t as a,w as o,v as i,G as L,u as k,h as _,g as B,I as J,r as y,p as q,l as S,q as D,m as C,i as A,f as H,E as R,F as P,x as E,J as K,k as Q,y as z,z as W,C as F}from"./index-DNAVmyB5.js";import{U as X}from"./users-DG0hBplh.js";import{S as Y}from"./sparkles-0MI5vUsR.js";/** +import{c as M,_ as G,I as O,o as d,a as m,b as l,t as a,w as o,v as i,H as L,u as k,h as E,g as B,J as D,r as y,p as J,l as S,q,m as C,i as A,f as H,G as R,F as P,x as T,D as K,k as Q,y as z,z as W,C as F}from"./index-CvsGOA4u.js";import{U as X}from"./users-Cs9RtsVY.js";import{S as Y}from"./sparkles-BfSk-aam.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=M("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"},le={class:"sec"},te={class:"row"},ne={class:"f"},se={class:"f"},oe={class:"f"},ie={class:"row"},ae={class:"f"},ue={class:"f"},de={class:"f"},re={class:"sec"},me={class:"row"},ve={class:"f"},be={class:"f"},fe={class:"f"},ge={key:0,class:"sec"},ye={key:1,class:"sec locked"},ke={class:"sec"},pe={class:"row"},ce={class:"f"},xe={class:"f"},Ve={key:0,class:"muted"},we={class:"row actions"},Ue=["disabled"],$e={__name:"PersonaForm",props:{persona:{type:Object,required:!0}},emits:["save","cancel"],setup(x,{emit:T}){const p=x,V=T,n=J({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=y(""),g=y(""),b=y(""),c=y(!1);function w(t){return Array.isArray(t)?t.join(` + */const Z=M("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"},le={class:"sec"},te={class:"row"},ne={class:"f"},se={class:"f"},oe={class:"f"},ie={class:"row"},ae={class:"f"},ue={class:"f"},de={class:"f"},re={class:"sec"},me={class:"row"},ve={class:"f"},be={class:"f"},fe={class:"f"},ge={key:0,class:"sec"},ye={key:1,class:"sec locked"},ke={class:"sec"},pe={class:"row"},ce={class:"f"},xe={class:"f"},Ve={key:0,class:"muted"},we={class:"row actions"},Ue=["disabled"],$e={__name:"PersonaForm",props:{persona:{type:Object,required:!0}},emits:["save","cancel"],setup(x,{emit:_}){const p=x,V=_,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=y(""),g=y(""),b=y(""),c=y(!1);function w(t){return Array.isArray(t)?t.join(` `):String(t||"")}function U(t){return t.split(` -`).map(e=>e.trim()).filter(Boolean)}O(()=>p.persona,t=>{Object.assign(n,{name:(t==null?void 0:t.name)||"",difficulty:(t==null?void 0:t.difficulty)??1,tier:(t==null?void 0:t.tier)||"B",profession:(t==null?void 0:t.profession)||"",age_group:(t==null?void 0:t.age_group)||"",location:(t==null?void 0:t.location)||"",income:(t==null?void 0:t.income)||"",budget:(t==null?void 0:t.budget)||"",lifestyle:(t==null?void 0:t.lifestyle)||"",background:(t==null?void 0:t.background)||"",personality:(t==null?void 0:t.personality)||"",communication_style:(t==null?void 0:t.communication_style)||"",goal:(t==null?void 0:t.goal)||"",decision_timeline:(t==null?void 0:t.decision_timeline)||"",opener:(t==null?void 0:t.opener)||"",channel:(t==null?void 0:t.channel)||"line",initiation_mode:(t==null?void 0:t.initiation_mode)||"customer",special:(t==null?void 0:t.special)||""}),v.value=w(t==null?void 0:t.pains),g.value=w(t==null?void 0:t.objections),b.value=w(t==null?void 0:t.negotiation_levers)},{immediate:!0});function N(){V("save",{...n,pains:U(v.value),objections:U(g.value),negotiation_levers:U(b.value)})}return(t,e)=>(d(),m("div",h,[l("h3",null,a(x.persona.name||"New persona"),1),l("p",ee,a(x.persona.profession)+" · "+a(x.persona.personality),1),l("div",le,[e[30]||(e[30]=l("h4",null,"👤 ข้อมูลพื้นฐาน",-1)),l("div",te,[l("div",ne,[e[23]||(e[23]=l("label",null,"ชื่อ",-1)),o(l("input",{"onUpdate:modelValue":e[0]||(e[0]=s=>n.name=s)},null,512),[[i,n.name]])]),l("div",se,[e[24]||(e[24]=l("label",null,"ระดับความยาก (1-5)",-1)),o(l("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}]])]),l("div",oe,[e[26]||(e[26]=l("label",null,"ระดับลูกค้า",-1)),o(l("select",{"onUpdate:modelValue":e[2]||(e[2]=s=>n.tier=s)},[...e[25]||(e[25]=[l("option",{value:"A"},"A — พร้อมตัดสินใจซื้อ",-1),l("option",{value:"B"},"B — ยังไม่แน่ใจ",-1),l("option",{value:"C"},"C — ไม่สนใจแต่มีปัญหา",-1)])],512),[[L,n.tier]])])]),l("div",ie,[l("div",ae,[e[27]||(e[27]=l("label",null,"อาชีพ",-1)),o(l("input",{"onUpdate:modelValue":e[3]||(e[3]=s=>n.profession=s)},null,512),[[i,n.profession]])]),l("div",ue,[e[28]||(e[28]=l("label",null,"ช่วงอายุ",-1)),o(l("input",{"onUpdate:modelValue":e[4]||(e[4]=s=>n.age_group=s)},null,512),[[i,n.age_group]])]),l("div",de,[e[29]||(e[29]=l("label",null,"พื้นที่",-1)),o(l("input",{"onUpdate:modelValue":e[5]||(e[5]=s=>n.location=s)},null,512),[[i,n.location]])])])]),l("div",re,[e[34]||(e[34]=l("h4",null,"💼 ข้อมูลลูกค้า",-1)),l("div",me,[l("div",ve,[e[31]||(e[31]=l("label",null,"รายได้/สถานะการเงิน",-1)),o(l("input",{"onUpdate:modelValue":e[6]||(e[6]=s=>n.income=s)},null,512),[[i,n.income]])]),l("div",be,[e[32]||(e[32]=l("label",null,"งบประมาณ",-1)),o(l("input",{"onUpdate:modelValue":e[7]||(e[7]=s=>n.budget=s)},null,512),[[i,n.budget]])]),l("div",fe,[e[33]||(e[33]=l("label",null,"ไลฟ์สไตล์",-1)),o(l("input",{"onUpdate:modelValue":e[8]||(e[8]=s=>n.lifestyle=s)},null,512),[[i,n.lifestyle]])])]),e[35]||(e[35]=l("label",null,"ภูมิหลัง",-1)),o(l("textarea",{"onUpdate:modelValue":e[9]||(e[9]=s=>n.background=s)},null,512),[[i,n.background]]),e[36]||(e[36]=l("label",null,"บุคลิก / วิธีพูดคุย",-1)),o(l("textarea",{"onUpdate:modelValue":e[10]||(e[10]=s=>n.personality=s)},null,512),[[i,n.personality]]),e[37]||(e[37]=l("label",null,"สไตล์การสื่อสาร",-1)),o(l("textarea",{"onUpdate:modelValue":e[11]||(e[11]=s=>n.communication_style=s)},null,512),[[i,n.communication_style]])]),k(_).isSuperAdmin?(d(),m("div",ge,[e[38]||(e[38]=l("h4",null,"🎯 การขาย",-1)),e[39]||(e[39]=l("label",null,"เป้าหมาย",-1)),o(l("textarea",{"onUpdate:modelValue":e[12]||(e[12]=s=>n.goal=s)},null,512),[[i,n.goal]]),e[40]||(e[40]=l("label",null,"กรอบเวลาในการตัดสินใจ",-1)),o(l("input",{"onUpdate:modelValue":e[13]||(e[13]=s=>n.decision_timeline=s)},null,512),[[i,n.decision_timeline]]),e[41]||(e[41]=l("label",null,"Pain (ปัญหา) แบบ 1 ต่อบรรทัด",-1)),o(l("textarea",{"onUpdate:modelValue":e[14]||(e[14]=s=>v.value=s),placeholder:`เช่น ต้นทุนสูงเกินไป -ระบบล้าสมัย`},null,512),[[i,v.value]]),e[42]||(e[42]=l("label",null,"ข้อโต้แย้ง (Objections) 1 ต่อบรรทัด",-1)),o(l("textarea",{"onUpdate:modelValue":e[15]||(e[15]=s=>g.value=s)},null,512),[[i,g.value]]),e[43]||(e[43]=l("label",null,"สิ่งที่ใช้ต่อรอง (เช่น ส่วนลด, ฟรีติดตั้ง)",-1)),o(l("textarea",{"onUpdate:modelValue":e[16]||(e[16]=s=>b.value=s)},null,512),[[i,b.value]]),e[44]||(e[44]=l("label",null,"ช่องทางสำหรับลูกค้าเปิดบทสนทนา (opener)",-1)),o(l("textarea",{"onUpdate:modelValue":e[17]||(e[17]=s=>n.opener=s)},null,512),[[i,n.opener]])])):(d(),m("div",ye,[e[45]||(e[45]=l("h4",null,"🔒 ส่วนสูตรการขาย",-1)),e[46]||(e[46]=l("p",{class:"muted"},"ปิดการแก้ไข — เฉพาะผู้ดูแลระดับสูงเท่านั้นที่ดู/แก้ได้ (เพื่อรักษาความได้เปรียบ)",-1)),e[47]||(e[47]=l("label",null,"เป้าหมาย",-1)),o(l("textarea",{"onUpdate:modelValue":e[18]||(e[18]=s=>n.goal=s)},null,512),[[i,n.goal]]),e[48]||(e[48]=l("label",null,"กรอบเวลาในการตัดสินใจ",-1)),o(l("input",{"onUpdate:modelValue":e[19]||(e[19]=s=>n.decision_timeline=s)},null,512),[[i,n.decision_timeline]])])),l("div",ke,[e[53]||(e[53]=l("h4",null,"📡 ช่องทาง & โหมด",-1)),l("div",pe,[l("div",ce,[e[50]||(e[50]=l("label",null,"ช่องทาง",-1)),o(l("select",{"onUpdate:modelValue":e[20]||(e[20]=s=>n.channel=s)},[...e[49]||(e[49]=[l("option",{value:"line"},"LINE",-1),l("option",{value:"facebook"},"Facebook",-1)])],512),[[L,n.channel]])]),l("div",xe,[e[52]||(e[52]=l("label",null,"ใครเริ่มคุยก่อน",-1)),o(l("select",{"onUpdate:modelValue":e[21]||(e[21]=s=>n.initiation_mode=s)},[...e[51]||(e[51]=[l("option",{value:"customer"},"ลูกค้าทักก่อน",-1),l("option",{value:"seller"},"เราต้องเริ่มขาย (เชิงรุก)",-1)])],512),[[L,n.initiation_mode]])])]),k(_).isSuperAdmin&&n.special?(d(),m("label",Ve,"พิเศษ: "+a(n.special),1)):B("",!0)]),l("div",we,[l("button",{class:"primary",onClick:N,disabled:c.value},a(c.value?"กำลังบันทึก…":"💾 บันทึกบุคคลต้นแบบ"),9,Ue),l("button",{onClick:e[22]||(e[22]=s=>t.$emit("cancel"))},"ปิด")])]))}},Ce=G($e,[["__scopeId","data-v-cef61bd8"]]),Be={class:"row",style:{"align-items":"center","margin-bottom":"8px"}},Se={style:{margin:"0"}},Ae={class:"row",style:{"margin-left":"auto",gap:"10px"}},ze=["disabled"],Ne={key:0,class:"spinner"},je={key:0,class:"muted"},Le={key:1,class:"error",style:{margin:"12px 0"}},Pe={key:2,class:"card empty-state"},Ee={class:"grid"},Te={class:"row",style:{"justify-content":"space-between","align-items":"center"}},_e={class:"diff"},Fe={class:"muted"},Ge={class:"muted"},Ie=["onClick"],Me={class:"modal"},Oe={__name:"GroupEdit",setup(x){const p=Q().params.gid,V=y(null),n=y([]),v=y(!1),g=y(""),b=y(null);async function c(){const u=await z.getGroup(p);V.value=u.group,n.value=(await z.listPersonas(p)).personas}function w(u){return n.value.filter(r=>r.tier===u)}function U(u){return A.t(u==="A"?"tierA":u==="B"?"tierB":"tierC")}function N(u){return{A:"tier-a",B:"tier-b",C:"tier-c"}[u]||""}async function t(){v.value=!0,g.value="";try{const u=await z.analyzeGroup(p,{append:!0});n.value=u.personas,await c()}catch(u){g.value=u.message}finally{v.value=!1}}function e(u){b.value=JSON.parse(JSON.stringify(u))}async function s(u){v.value=!0;try{await z.updatePersona(p,b.value.id,u),b.value=null,await c()}catch(r){g.value=r.message}finally{v.value=!1}}return q(c),(u,r)=>{const I=W("router-link");return d(),m("div",null,[S(I,{to:"/training",class:"btn-back"},{default:D(()=>[C("← "+a(k(A).t("training")),1)]),_:1}),l("div",Be,[l("h2",Se,[S(k(X),{size:22,"stroke-width":1.8,style:{"vertical-align":"-4px"}}),C(" "+a(k(A).t("managePersonas")),1)]),l("div",Ae,[l("button",{onClick:t,disabled:v.value,class:"primary"},[v.value?(d(),m("span",Ne)):(d(),H(k(Y),{key:1,size:18,"stroke-width":1.8})),C(" "+a(v.value?"กำลังสร้าง…":"สร้างบุคคลต้นแบบเพิ่มเติม"),1)],8,ze)])]),V.value?(d(),m("p",je,[r[2]||(r[2]=C("สินค้า: ",-1)),l("strong",null,a(V.value.title),1)])):B("",!0),g.value?(d(),m("div",Le,a(g.value),1)):B("",!0),r[4]||(r[4]=R('
วิธีใช้หน้านี้
  1. ระบบสร้างบุคคลต้นแบบ (ลูกค้าจำลอง) ให้อัตโนมัติแล้ว หลังกดสร้าง
  2. แต่ละการ์ดคือลูกค้าจำลอง 1 คน — กด ✏️ แก้ไข เพื่อปรับรายละเอียดให้ตรงใจ
  3. อยากได้ลูกค้าจำลองเพิ่ม กด สร้างบุคคลต้นแบบเพิ่มเติม
  4. เมื่อพร้อมให้ผู้ใช้ฝึก กด เปิดใช้งาน (ยังทำในขั้นถัดไป)
',1)),n.value.length===0&&!v.value?(d(),m("div",Pe,[...r[3]||(r[3]=[l("strong",null,"ยังไม่มีบุคคลต้นแบบ",-1),l("span",null,'กด "สร้างบุคคลต้นแบบเพิ่มเติม" เพื่อให้ระบบสร้างลูกค้าจำลองให้',-1)])])):B("",!0),(d(),m(P,null,E(["A","B","C"],$=>l("div",{key:$,style:{"margin-bottom":"20px"}},[l("h4",null,a(U($)),1),l("div",Ee,[(d(!0),m(P,null,E(w($),f=>(d(),m("div",{key:f.id,class:"card pcard"},[l("div",Te,[l("strong",null,a(f.name),1),l("span",{class:F(["badge",N(f.tier)])},a(f.tier),3)]),l("div",_e,[(d(),m(P,null,E(5,j=>l("span",{key:j,class:F(["star",{on:j<=(f.difficulty||1)}])},"★",2)),64))]),l("div",Fe,a(f.profession)+" · "+a(f.age_group)+" · "+a(f.location),1),l("div",Ge,a(f.channel)+" · "+a(f.initiation_mode==="seller"?"เริ่มขายเอง":"ลูกค้าทักก่อน"),1),l("button",{class:"edit-btn",onClick:j=>e(f)},[S(k(Z),{size:15,"stroke-width":1.8}),C(" "+a(k(A).t("edit")),1)],8,Ie)]))),128))])])),64)),b.value?(d(),m("div",{key:3,class:"modal-backdrop",onClick:r[1]||(r[1]=K($=>b.value=null,["self"]))},[l("div",Me,[S(Ce,{persona:b.value,onSave:s,onCancel:r[0]||(r[0]=$=>b.value=null)},null,8,["persona"])])])):B("",!0)])}}},He=G(Oe,[["__scopeId","data-v-5517bbc4"]]);export{He as default}; +`).map(e=>e.trim()).filter(Boolean)}O(()=>p.persona,t=>{Object.assign(n,{name:(t==null?void 0:t.name)||"",difficulty:(t==null?void 0:t.difficulty)??1,tier:(t==null?void 0:t.tier)||"B",profession:(t==null?void 0:t.profession)||"",age_group:(t==null?void 0:t.age_group)||"",location:(t==null?void 0:t.location)||"",income:(t==null?void 0:t.income)||"",budget:(t==null?void 0:t.budget)||"",lifestyle:(t==null?void 0:t.lifestyle)||"",background:(t==null?void 0:t.background)||"",personality:(t==null?void 0:t.personality)||"",communication_style:(t==null?void 0:t.communication_style)||"",goal:(t==null?void 0:t.goal)||"",decision_timeline:(t==null?void 0:t.decision_timeline)||"",opener:(t==null?void 0:t.opener)||"",channel:(t==null?void 0:t.channel)||"line",initiation_mode:(t==null?void 0:t.initiation_mode)||"customer",special:(t==null?void 0:t.special)||""}),v.value=w(t==null?void 0:t.pains),g.value=w(t==null?void 0:t.objections),b.value=w(t==null?void 0:t.negotiation_levers)},{immediate:!0});function N(){V("save",{...n,pains:U(v.value),objections:U(g.value),negotiation_levers:U(b.value)})}return(t,e)=>(d(),m("div",h,[l("h3",null,a(x.persona.name||"New persona"),1),l("p",ee,a(x.persona.profession)+" · "+a(x.persona.personality),1),l("div",le,[e[30]||(e[30]=l("h4",null,"👤 ข้อมูลพื้นฐาน",-1)),l("div",te,[l("div",ne,[e[23]||(e[23]=l("label",null,"ชื่อ",-1)),o(l("input",{"onUpdate:modelValue":e[0]||(e[0]=s=>n.name=s)},null,512),[[i,n.name]])]),l("div",se,[e[24]||(e[24]=l("label",null,"ระดับความยาก (1-5)",-1)),o(l("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}]])]),l("div",oe,[e[26]||(e[26]=l("label",null,"ระดับลูกค้า",-1)),o(l("select",{"onUpdate:modelValue":e[2]||(e[2]=s=>n.tier=s)},[...e[25]||(e[25]=[l("option",{value:"A"},"A — พร้อมตัดสินใจซื้อ",-1),l("option",{value:"B"},"B — ยังไม่แน่ใจ",-1),l("option",{value:"C"},"C — ไม่สนใจแต่มีปัญหา",-1)])],512),[[L,n.tier]])])]),l("div",ie,[l("div",ae,[e[27]||(e[27]=l("label",null,"อาชีพ",-1)),o(l("input",{"onUpdate:modelValue":e[3]||(e[3]=s=>n.profession=s)},null,512),[[i,n.profession]])]),l("div",ue,[e[28]||(e[28]=l("label",null,"ช่วงอายุ",-1)),o(l("input",{"onUpdate:modelValue":e[4]||(e[4]=s=>n.age_group=s)},null,512),[[i,n.age_group]])]),l("div",de,[e[29]||(e[29]=l("label",null,"พื้นที่",-1)),o(l("input",{"onUpdate:modelValue":e[5]||(e[5]=s=>n.location=s)},null,512),[[i,n.location]])])])]),l("div",re,[e[34]||(e[34]=l("h4",null,"💼 ข้อมูลลูกค้า",-1)),l("div",me,[l("div",ve,[e[31]||(e[31]=l("label",null,"รายได้/สถานะการเงิน",-1)),o(l("input",{"onUpdate:modelValue":e[6]||(e[6]=s=>n.income=s)},null,512),[[i,n.income]])]),l("div",be,[e[32]||(e[32]=l("label",null,"งบประมาณ",-1)),o(l("input",{"onUpdate:modelValue":e[7]||(e[7]=s=>n.budget=s)},null,512),[[i,n.budget]])]),l("div",fe,[e[33]||(e[33]=l("label",null,"ไลฟ์สไตล์",-1)),o(l("input",{"onUpdate:modelValue":e[8]||(e[8]=s=>n.lifestyle=s)},null,512),[[i,n.lifestyle]])])]),e[35]||(e[35]=l("label",null,"ภูมิหลัง",-1)),o(l("textarea",{"onUpdate:modelValue":e[9]||(e[9]=s=>n.background=s)},null,512),[[i,n.background]]),e[36]||(e[36]=l("label",null,"บุคลิก / วิธีพูดคุย",-1)),o(l("textarea",{"onUpdate:modelValue":e[10]||(e[10]=s=>n.personality=s)},null,512),[[i,n.personality]]),e[37]||(e[37]=l("label",null,"สไตล์การสื่อสาร",-1)),o(l("textarea",{"onUpdate:modelValue":e[11]||(e[11]=s=>n.communication_style=s)},null,512),[[i,n.communication_style]])]),k(E).isSuperAdmin?(d(),m("div",ge,[e[38]||(e[38]=l("h4",null,"🎯 การขาย",-1)),e[39]||(e[39]=l("label",null,"เป้าหมาย",-1)),o(l("textarea",{"onUpdate:modelValue":e[12]||(e[12]=s=>n.goal=s)},null,512),[[i,n.goal]]),e[40]||(e[40]=l("label",null,"กรอบเวลาในการตัดสินใจ",-1)),o(l("input",{"onUpdate:modelValue":e[13]||(e[13]=s=>n.decision_timeline=s)},null,512),[[i,n.decision_timeline]]),e[41]||(e[41]=l("label",null,"Pain (ปัญหา) แบบ 1 ต่อบรรทัด",-1)),o(l("textarea",{"onUpdate:modelValue":e[14]||(e[14]=s=>v.value=s),placeholder:`เช่น ต้นทุนสูงเกินไป +ระบบล้าสมัย`},null,512),[[i,v.value]]),e[42]||(e[42]=l("label",null,"ข้อโต้แย้ง (Objections) 1 ต่อบรรทัด",-1)),o(l("textarea",{"onUpdate:modelValue":e[15]||(e[15]=s=>g.value=s)},null,512),[[i,g.value]]),e[43]||(e[43]=l("label",null,"สิ่งที่ใช้ต่อรอง (เช่น ส่วนลด, ฟรีติดตั้ง)",-1)),o(l("textarea",{"onUpdate:modelValue":e[16]||(e[16]=s=>b.value=s)},null,512),[[i,b.value]]),e[44]||(e[44]=l("label",null,"ช่องทางสำหรับลูกค้าเปิดบทสนทนา (opener)",-1)),o(l("textarea",{"onUpdate:modelValue":e[17]||(e[17]=s=>n.opener=s)},null,512),[[i,n.opener]])])):(d(),m("div",ye,[e[45]||(e[45]=l("h4",null,"🔒 ส่วนสูตรการขาย",-1)),e[46]||(e[46]=l("p",{class:"muted"},"ปิดการแก้ไข — เฉพาะผู้ดูแลระดับสูงเท่านั้นที่ดู/แก้ได้ (เพื่อรักษาความได้เปรียบ)",-1)),e[47]||(e[47]=l("label",null,"เป้าหมาย",-1)),o(l("textarea",{"onUpdate:modelValue":e[18]||(e[18]=s=>n.goal=s)},null,512),[[i,n.goal]]),e[48]||(e[48]=l("label",null,"กรอบเวลาในการตัดสินใจ",-1)),o(l("input",{"onUpdate:modelValue":e[19]||(e[19]=s=>n.decision_timeline=s)},null,512),[[i,n.decision_timeline]])])),l("div",ke,[e[53]||(e[53]=l("h4",null,"📡 ช่องทาง & โหมด",-1)),l("div",pe,[l("div",ce,[e[50]||(e[50]=l("label",null,"ช่องทาง",-1)),o(l("select",{"onUpdate:modelValue":e[20]||(e[20]=s=>n.channel=s)},[...e[49]||(e[49]=[l("option",{value:"line"},"LINE",-1),l("option",{value:"facebook"},"Facebook",-1)])],512),[[L,n.channel]])]),l("div",xe,[e[52]||(e[52]=l("label",null,"ใครเริ่มคุยก่อน",-1)),o(l("select",{"onUpdate:modelValue":e[21]||(e[21]=s=>n.initiation_mode=s)},[...e[51]||(e[51]=[l("option",{value:"customer"},"ลูกค้าทักก่อน",-1),l("option",{value:"seller"},"เราต้องเริ่มขาย (เชิงรุก)",-1)])],512),[[L,n.initiation_mode]])])]),k(E).isSuperAdmin&&n.special?(d(),m("label",Ve,"พิเศษ: "+a(n.special),1)):B("",!0)]),l("div",we,[l("button",{class:"primary",onClick:N,disabled:c.value},a(c.value?"กำลังบันทึก…":"💾 บันทึกบุคคลต้นแบบ"),9,Ue),l("button",{onClick:e[22]||(e[22]=s=>t.$emit("cancel"))},"ปิด")])]))}},Ce=G($e,[["__scopeId","data-v-cef61bd8"]]),Be={class:"row",style:{"align-items":"center","margin-bottom":"8px"}},Se={style:{margin:"0"}},Ae={class:"row",style:{"margin-left":"auto",gap:"10px"}},ze=["disabled"],Ne={key:0,class:"spinner"},je={key:0,class:"muted"},Le={key:1,class:"error",style:{margin:"12px 0"}},Pe={key:2,class:"card empty-state"},Te={class:"grid"},_e={class:"row",style:{"justify-content":"space-between","align-items":"center"}},Ee={class:"diff"},Fe={class:"muted"},Ge={class:"muted"},Ie=["onClick"],Me={class:"modal"},Oe={__name:"GroupEdit",setup(x){const p=Q().params.gid,V=y(null),n=y([]),v=y(!1),g=y(""),b=y(null);async function c(){const u=await z.getGroup(p);V.value=u.group,n.value=(await z.listPersonas(p)).personas}function w(u){return n.value.filter(r=>r.tier===u)}function U(u){return A.t(u==="A"?"tierA":u==="B"?"tierB":"tierC")}function N(u){return{A:"tier-a",B:"tier-b",C:"tier-c"}[u]||""}async function t(){v.value=!0,g.value="";try{const u=await z.analyzeGroup(p,{append:!0});n.value=u.personas,await c()}catch(u){g.value=u.message}finally{v.value=!1}}function e(u){b.value=JSON.parse(JSON.stringify(u))}async function s(u){v.value=!0;try{await z.updatePersona(p,b.value.id,u),b.value=null,await c()}catch(r){g.value=r.message}finally{v.value=!1}}return J(c),(u,r)=>{const I=W("router-link");return d(),m("div",null,[S(I,{to:"/training",class:"btn-back"},{default:q(()=>[C("← "+a(k(A).t("training")),1)]),_:1}),l("div",Be,[l("h2",Se,[S(k(X),{size:22,"stroke-width":1.8,style:{"vertical-align":"-4px"}}),C(" "+a(k(A).t("managePersonas")),1)]),l("div",Ae,[l("button",{onClick:t,disabled:v.value,class:"primary"},[v.value?(d(),m("span",Ne)):(d(),H(k(Y),{key:1,size:18,"stroke-width":1.8})),C(" "+a(v.value?"กำลังสร้าง…":"สร้างบุคคลต้นแบบเพิ่มเติม"),1)],8,ze)])]),V.value?(d(),m("p",je,[r[2]||(r[2]=C("สินค้า: ",-1)),l("strong",null,a(V.value.title),1)])):B("",!0),g.value?(d(),m("div",Le,a(g.value),1)):B("",!0),r[4]||(r[4]=R('
วิธีใช้หน้านี้
  1. ระบบสร้างบุคคลต้นแบบ (ลูกค้าจำลอง) ให้อัตโนมัติแล้ว หลังกดสร้าง
  2. แต่ละการ์ดคือลูกค้าจำลอง 1 คน — กด ✏️ แก้ไข เพื่อปรับรายละเอียดให้ตรงใจ
  3. อยากได้ลูกค้าจำลองเพิ่ม กด สร้างบุคคลต้นแบบเพิ่มเติม
  4. เมื่อพร้อมให้ผู้ใช้ฝึก กด เปิดใช้งาน (ยังทำในขั้นถัดไป)
',1)),n.value.length===0&&!v.value?(d(),m("div",Pe,[...r[3]||(r[3]=[l("strong",null,"ยังไม่มีบุคคลต้นแบบ",-1),l("span",null,'กด "สร้างบุคคลต้นแบบเพิ่มเติม" เพื่อให้ระบบสร้างลูกค้าจำลองให้',-1)])])):B("",!0),(d(),m(P,null,T(["A","B","C"],$=>l("div",{key:$,style:{"margin-bottom":"20px"}},[l("h4",null,a(U($)),1),l("div",Te,[(d(!0),m(P,null,T(w($),f=>(d(),m("div",{key:f.id,class:"card pcard"},[l("div",_e,[l("strong",null,a(f.name),1),l("span",{class:F(["badge",N(f.tier)])},a(f.tier),3)]),l("div",Ee,[(d(),m(P,null,T(5,j=>l("span",{key:j,class:F(["star",{on:j<=(f.difficulty||1)}])},"★",2)),64))]),l("div",Fe,a(f.profession)+" · "+a(f.age_group)+" · "+a(f.location),1),l("div",Ge,a(f.channel)+" · "+a(f.initiation_mode==="seller"?"เริ่มขายเอง":"ลูกค้าทักก่อน"),1),l("button",{class:"edit-btn",onClick:j=>e(f)},[S(k(Z),{size:15,"stroke-width":1.8}),C(" "+a(k(A).t("edit")),1)],8,Ie)]))),128))])])),64)),b.value?(d(),m("div",{key:3,class:"modal-backdrop",onClick:r[1]||(r[1]=K($=>b.value=null,["self"]))},[l("div",Me,[S(Ce,{persona:b.value,onSave:s,onCancel:r[0]||(r[0]=$=>b.value=null)},null,8,["persona"])])])):B("",!0)])}}},He=G(Oe,[["__scopeId","data-v-5517bbc4"]]);export{He as default}; diff --git a/frontend/dist/assets/Guide-B9uxl_Jn.js b/frontend/dist/assets/Guide-D116suSn.js similarity index 94% rename from frontend/dist/assets/Guide-B9uxl_Jn.js rename to frontend/dist/assets/Guide-D116suSn.js index 4cda4a7..9c35699 100644 --- a/frontend/dist/assets/Guide-B9uxl_Jn.js +++ b/frontend/dist/assets/Guide-D116suSn.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,E as u,o as d}from"./index-DNAVmyB5.js";import{B as g}from"./book-open-o2ZLc9m6.js";import{L as p}from"./layout-dashboard-dl9BywAR.js";import{T as m}from"./target-BYJn0LyF.js";import{S as y}from"./sparkles-0MI5vUsR.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-CvsGOA4u.js";import{B as g}from"./book-open-BPDJkLfy.js";import{L as p}from"./layout-dashboard-k0A07o6y.js";import{T as m}from"./target-BHq78myU.js";import{S as y}from"./sparkles-BfSk-aam.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-O3bvqwVm.js b/frontend/dist/assets/Login-BxCw0ggt.js similarity index 98% rename from frontend/dist/assets/Login-O3bvqwVm.js rename to frontend/dist/assets/Login-BxCw0ggt.js index 9be7322..de915d2 100644 --- a/frontend/dist/assets/Login-O3bvqwVm.js +++ b/frontend/dist/assets/Login-BxCw0ggt.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-DNAVmyB5.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-CvsGOA4u.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-Daz4fzbD.js b/frontend/dist/assets/MyBoard-Bw1xIyZv.js similarity index 94% rename from frontend/dist/assets/MyBoard-Daz4fzbD.js rename to frontend/dist/assets/MyBoard-Bw1xIyZv.js index 94f64f1..ffbb1f5 100644 --- a/frontend/dist/assets/MyBoard-Daz4fzbD.js +++ b/frontend/dist/assets/MyBoard-Bw1xIyZv.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-DNAVmyB5.js";import{L as V}from"./layout-dashboard-dl9BywAR.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-CvsGOA4u.js";import{L as V}from"./layout-dashboard-k0A07o6y.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-B92S9P5I.js b/frontend/dist/assets/Personas-OK6zEMMZ.js similarity index 95% rename from frontend/dist/assets/Personas-B92S9P5I.js rename to frontend/dist/assets/Personas-OK6zEMMZ.js index 1ee1940..31a05d1 100644 --- a/frontend/dist/assets/Personas-B92S9P5I.js +++ b/frontend/dist/assets/Personas-OK6zEMMZ.js @@ -1 +1 @@ -import{_ as T,p as $,a as r,l as d,q as m,b as t,t as s,u as e,i as n,h as f,m as _,g as w,F as y,x as p,k as L,y as N,z as V,r as b,o as a,S as I,C as h,f as A}from"./index-DNAVmyB5.js";import{T as S}from"./target-BYJn0LyF.js";import{A as F}from"./arrow-left-6ACDPyW9.js";const j={class:"row",style:{"align-items":"center"}},q={style:{margin:"0"}},D={class:"muted",style:{"margin-left":"auto"}},E={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,k=b([]),C=b(!0);async function B(){try{k.value=(await N.listPersonas(c)).personas}finally{C.value=!1}}function z(l){return k.value.filter(i=>i.tier===l)}function P(l){return n.t(l==="A"?"tierA":l==="B"?"tierB":"tierC")}function v(l){return l==="won"?n.t("won"):l==="lost"?n.t("lost"):n.t("notTried")}return $(B),(l,i)=>{const u=V("router-link");return a(),r("div",null,[d(u,{to:"/training",class:"btn-back"},{default:m(()=>[d(e(F),{size:16,"stroke-width":2}),_(" "+s(e(n).t("training")),1)]),_:1}),t("div",j,[t("h2",q,s(e(n).t("personas")),1),t("span",D,s(e(n).t("selectPersona")),1)]),e(f).isAdmin?w("",!0):(a(),r("div",E,[t("strong",null,[d(e(S),{size:17,"stroke-width":1.8,style:{"vertical-align":"-3px"}}),i[0]||(i[0]=_(" วิธีฝึก",-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,"กดปุ่มสำหรับฝึกแชทกับลูกค้าคนนั้น (ฝึกได้คนละครั้งเท่านั้น)")],-1))])),e(f).isAdmin?(a(),r("div",M,[d(u,{to:`/admin/groups/${e(c)}/edit`},{default:m(()=>[t("button",R,[d(e(I),{size:18,"stroke-width":1.8}),_(" "+s(e(n).t("managePersonas")),1)])]),_:1},8,["to"]),i[2]||(i[2]=t("span",{class:"muted"},"Admin: จัดการรายละเอียดบุคคลต้นแบบได้ที่นี่",-1))])):w("",!0),(a(),r(y,null,p(["A","B","C"],g=>t("div",{key:g,style:{margin:"20px 0"}},[t("h4",null,s(P(g)),1),t("div",G,[(a(!0),r(y,null,p(z(g),o=>(a(),r("div",{key:o.id,class:"card pcard lift"},[t("div",H,[t("strong",null,s(o.name),1),t("span",{class:h(["badge",o.my_outcome])},s(v(o.my_outcome)),3)]),t("div",J,[(a(),r(y,null,p(5,x=>t("span",{key:x,class:h(["star",{on:x<=(o.difficulty||1)}])},"★",2)),64)),t("span",K,s(e(n).t("difficulty"))+" "+s(o.difficulty||1)+"/5",1)]),t("div",O,[_(s(o.profession)+" · "+s(o.age_group)+" · "+s(o.location),1),i[3]||(i[3]=t("br",null,null,-1)),t("span",{class:h(["badge",o.channel])},s(o.channel),3),t("span",Q," · "+s(o.initiation_mode==="seller"?e(n).t("sellerInitiated"):e(n).t("customerInitiated")),1)]),t("div",U,s(o.product_context),1),e(f).isAdmin?(a(),A(u,{key:0,to:`/groups/${e(c)}/chat/${o.id}`,style:{"margin-top":"auto"}},{default:m(()=>[t("button",W,s(e(n).t("chat")),1)]),_:1},8,["to"])):(a(),r(y,{key:1},[o.my_outcome==="not_tried"?(a(),A(u,{key:0,to:`/groups/${e(c)}/chat/${o.id}`,style:{"margin-top":"auto"}},{default:m(()=>[t("button",X,s(e(n).t("chat")),1)]),_:1},8,["to"])):(a(),r("div",Y,"✓ "+s(e(n).t("trained"))+" ("+s(v(o.my_outcome))+")",1))],64))]))),128))])])),64))])}}},at=T(Z,[["__scopeId","data-v-46d50c99"]]);export{at as default}; +import{_ as T,p as $,a as r,l as d,q as m,b as t,t as s,u as e,i as n,h as f,m as _,g as w,F as y,x as p,k as L,y as N,z as V,r as b,o as a,S as I,C as h,f as A}from"./index-CvsGOA4u.js";import{T as S}from"./target-BHq78myU.js";import{A as F}from"./arrow-left-DF5NIPgR.js";const j={class:"row",style:{"align-items":"center"}},q={style:{margin:"0"}},D={class:"muted",style:{"margin-left":"auto"}},E={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,k=b([]),C=b(!0);async function B(){try{k.value=(await N.listPersonas(c)).personas}finally{C.value=!1}}function z(l){return k.value.filter(i=>i.tier===l)}function P(l){return n.t(l==="A"?"tierA":l==="B"?"tierB":"tierC")}function v(l){return l==="won"?n.t("won"):l==="lost"?n.t("lost"):n.t("notTried")}return $(B),(l,i)=>{const u=V("router-link");return a(),r("div",null,[d(u,{to:"/training",class:"btn-back"},{default:m(()=>[d(e(F),{size:16,"stroke-width":2}),_(" "+s(e(n).t("training")),1)]),_:1}),t("div",j,[t("h2",q,s(e(n).t("personas")),1),t("span",D,s(e(n).t("selectPersona")),1)]),e(f).isAdmin?w("",!0):(a(),r("div",E,[t("strong",null,[d(e(S),{size:17,"stroke-width":1.8,style:{"vertical-align":"-3px"}}),i[0]||(i[0]=_(" วิธีฝึก",-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,"กดปุ่มสำหรับฝึกแชทกับลูกค้าคนนั้น (ฝึกได้คนละครั้งเท่านั้น)")],-1))])),e(f).isAdmin?(a(),r("div",M,[d(u,{to:`/admin/groups/${e(c)}/edit`},{default:m(()=>[t("button",R,[d(e(I),{size:18,"stroke-width":1.8}),_(" "+s(e(n).t("managePersonas")),1)])]),_:1},8,["to"]),i[2]||(i[2]=t("span",{class:"muted"},"Admin: จัดการรายละเอียดบุคคลต้นแบบได้ที่นี่",-1))])):w("",!0),(a(),r(y,null,p(["A","B","C"],g=>t("div",{key:g,style:{margin:"20px 0"}},[t("h4",null,s(P(g)),1),t("div",G,[(a(!0),r(y,null,p(z(g),o=>(a(),r("div",{key:o.id,class:"card pcard lift"},[t("div",H,[t("strong",null,s(o.name),1),t("span",{class:h(["badge",o.my_outcome])},s(v(o.my_outcome)),3)]),t("div",J,[(a(),r(y,null,p(5,x=>t("span",{key:x,class:h(["star",{on:x<=(o.difficulty||1)}])},"★",2)),64)),t("span",K,s(e(n).t("difficulty"))+" "+s(o.difficulty||1)+"/5",1)]),t("div",O,[_(s(o.profession)+" · "+s(o.age_group)+" · "+s(o.location),1),i[3]||(i[3]=t("br",null,null,-1)),t("span",{class:h(["badge",o.channel])},s(o.channel),3),t("span",Q," · "+s(o.initiation_mode==="seller"?e(n).t("sellerInitiated"):e(n).t("customerInitiated")),1)]),t("div",U,s(o.product_context),1),e(f).isAdmin?(a(),A(u,{key:0,to:`/groups/${e(c)}/chat/${o.id}`,style:{"margin-top":"auto"}},{default:m(()=>[t("button",W,s(e(n).t("chat")),1)]),_:1},8,["to"])):(a(),r(y,{key:1},[o.my_outcome==="not_tried"?(a(),A(u,{key:0,to:`/groups/${e(c)}/chat/${o.id}`,style:{"margin-top":"auto"}},{default:m(()=>[t("button",X,s(e(n).t("chat")),1)]),_:1},8,["to"])):(a(),r("div",Y,"✓ "+s(e(n).t("trained"))+" ("+s(v(o.my_outcome))+")",1))],64))]))),128))])])),64))])}}},at=T(Z,[["__scopeId","data-v-46d50c99"]]);export{at as default}; diff --git a/frontend/dist/assets/Settings-C4qI85q5.js b/frontend/dist/assets/Settings-C96o5eMo.js similarity index 98% rename from frontend/dist/assets/Settings-C4qI85q5.js rename to frontend/dist/assets/Settings-C96o5eMo.js index 2a5f9b7..0b6e949 100644 --- a/frontend/dist/assets/Settings-C4qI85q5.js +++ b/frontend/dist/assets/Settings-C96o5eMo.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-DNAVmyB5.js";import{L as D}from"./lock-fipmYN8_.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-CvsGOA4u.js";import{L as D}from"./lock-Lq5iEyDR.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-H6LANAbr.js b/frontend/dist/assets/Setup-C4CPiNJG.js similarity index 94% rename from frontend/dist/assets/Setup-H6LANAbr.js rename to frontend/dist/assets/Setup-C4CPiNJG.js index aba0ca1..fc6244f 100644 --- a/frontend/dist/assets/Setup-H6LANAbr.js +++ b/frontend/dist/assets/Setup-C4CPiNJG.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-DNAVmyB5.js";import{L as C}from"./lock-fipmYN8_.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-CvsGOA4u.js";import{L as C}from"./lock-Lq5iEyDR.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-BYBkfggr.css b/frontend/dist/assets/Training-BYBkfggr.css deleted file mode 100644 index 6ed3850..0000000 --- a/frontend/dist/assets/Training-BYBkfggr.css +++ /dev/null @@ -1 +0,0 @@ -.grid[data-v-e39df241]{display:grid;grid-template-columns:repeat(auto-fill,minmax(260px,1fr));gap:16px}.train-card[data-v-e39df241]{display:flex;flex-direction:column;height:100%}.badge.ready[data-v-e39df241]{background:#dcfce7;color:#166534}.badge.draft[data-v-e39df241]{background:#fef9c3;color:#854d0e}button.soft[data-v-e39df241]{background:#f1f3f9;border-color:transparent}.badge.facebook[data-v-e39df241]{background:#e0f2fe;color:#0369a1}.badge.line[data-v-e39df241]{background:#dcfce7;color:#15803d} diff --git a/frontend/dist/assets/Training-Ctr9xSAG.js b/frontend/dist/assets/Training-Ctr9xSAG.js new file mode 100644 index 0000000..f7132dc --- /dev/null +++ b/frontend/dist/assets/Training-Ctr9xSAG.js @@ -0,0 +1,6 @@ +import{c as b,_ as C,p as A,y as f,h as l,a as d,b as e,l as c,u as t,m,t as o,i,q as _,f as z,g as h,F as M,x as T,r as v,z as B,o as r,C as k,D as N}from"./index-CvsGOA4u.js";import{T as P}from"./target-BHq78myU.js";import{B as V}from"./book-open-BPDJkLfy.js";import{P as $}from"./plus-BSRzoT5b.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 j=b("trash-2",[["path",{d:"M10 11v6",key:"nco0om"}],["path",{d:"M14 11v6",key:"outv1u"}],["path",{d:"M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6",key:"miytrc"}],["path",{d:"M3 6h18",key:"d0wm0j"}],["path",{d:"M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2",key:"e791ji"}]]),G={class:"row",style:{"align-items":"center","margin-bottom":"16px"}},L={style:{margin:"0"}},D={class:"row",style:{"margin-left":"auto",gap:"10px"}},F={class:"soft"},I={class:"primary"},S={class:"muted"},q={key:0,class:"card",style:{"min-height":"80px"}},E={key:1,class:"card empty-state"},H={key:0},O={key:1},J={class:"grid"},K={class:"row",style:{"justify-content":"space-between"}},Q={class:"row",style:{gap:"6px","align-items":"center"}},R=["onClick"],U={class:"muted",style:{margin:"6px 0 12px"}},W={class:"row",style:{gap:"8px"}},X={class:"muted"},Y={class:"primary",style:{width:"100%","margin-top":"12px"}},Z={__name:"Training",setup(tt){const u=v([]),y=v(!0);function g(s){return s.sales_kit&&s.sales_kit.productName||s.input&&s.input.product||""}function w(s){return s.personas||s.persona_count||0}async function x(s){if(confirm(`ลบกลุ่ม "${s.title}" และบุคคลต้นแบบทั้งหมด? (ติดลบถาวร)`))try{await f.deleteGroup(s.id),u.value=u.value.filter(n=>n.id!==s.id)}catch(n){alert(n.message)}}return A(async()=>{try{const s=(await f.listGroups()).groups||[];u.value=l.isAdmin?s:s.filter(n=>n.status==="ready")}finally{y.value=!1}}),(s,n)=>{const p=B("router-link");return r(),d("div",null,[e("div",G,[e("h2",L,[c(t(P),{size:22,"stroke-width":1.8,style:{"vertical-align":"-4px"}}),m(" "+o(t(i).t("training")),1)]),e("div",D,[c(p,{to:"/guide"},{default:_(()=>[e("button",F,[c(t(V),{size:18,"stroke-width":1.8}),n[0]||(n[0]=m(" คู่มือ",-1))])]),_:1}),t(l).isAdmin?(r(),z(p,{key:0,to:"/admin/new-group"},{default:_(()=>[e("button",I,[c(t($),{size:18,"stroke-width":2}),m(" "+o(t(i).t("addProduct")),1)])]),_:1})):h("",!0)])]),e("p",S,o(t(i).t("trainingSubtitle")),1),y.value?(r(),d("div",q,[...n[1]||(n[1]=[e("div",{class:"skeleton",style:{height:"50px"}},null,-1)])])):u.value.length===0?(r(),d("div",E,[e("strong",null,o(t(i).t("noTraining")),1),t(l).isAdmin?(r(),d("span",H,'Click "'+o(t(i).t("addProduct"))+'" to create a persona group.',1)):(r(),d("span",O,"Ask an admin to create a persona group first."))])):h("",!0),e("div",J,[(r(!0),d(M,null,T(u.value,a=>(r(),d("div",{key:a.id,class:"card lift train-card"},[e("div",K,[c(p,{to:t(l).isAdmin?`/admin/groups/${a.id}/edit`:`/groups/${a.id}/personas`,style:{"text-decoration":"none",color:"inherit","min-width":"0"}},{default:_(()=>[e("strong",null,o(a.title),1)]),_:2},1032,["to"]),e("div",Q,[e("span",{class:k(["badge",a.status==="ready"?"ready":"draft"])},o(a.status),3),t(l).isAdmin?(r(),d("button",{key:0,class:"del-btn",title:"ลบกลุ่มนี้ (ติดลบถาวร)",onClick:N(et=>x(a),["prevent","stop"])},[c(t(j),{size:16,"stroke-width":1.8})],8,R)):h("",!0)])]),c(p,{to:t(l).isAdmin?`/admin/groups/${a.id}/edit`:`/groups/${a.id}/personas`,style:{"text-decoration":"none",color:"inherit"}},{default:_(()=>[e("div",U,o(g(a)),1),e("div",W,[e("span",{class:k(["badge",a.channel||"line"])},o(a.channel||"line"),3),e("span",X,o(w(a))+" "+o(t(i).t("personas").toLowerCase()),1)]),e("button",Y,o(t(l).isAdmin?a.status==="ready"?t(i).t("managePersonas"):t(i).t("analyze"):t(i).t("selectPersona")),1)]),_:2},1032,["to"])]))),128))])])}}},it=C(Z,[["__scopeId","data-v-b1b30fec"]]);export{it as default}; diff --git a/frontend/dist/assets/Training-DTTvOhJR.js b/frontend/dist/assets/Training-DTTvOhJR.js deleted file mode 100644 index d7b91f9..0000000 --- a/frontend/dist/assets/Training-DTTvOhJR.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as w,p as b,y as C,h as d,a as l,b as t,l as c,u as s,m as _,t as e,i as n,q as m,f as h,g as f,F as z,x as A,r as g,z as B,o as i,C as k}from"./index-DNAVmyB5.js";import{T as N}from"./target-BYJn0LyF.js";import{B as P}from"./book-open-o2ZLc9m6.js";import{P as T}from"./plus-BGdYQyzB.js";const V={class:"row",style:{"align-items":"center","margin-bottom":"16px"}},F={style:{margin:"0"}},L={class:"row",style:{"margin-left":"auto",gap:"10px"}},S={class:"soft"},$={class:"primary"},j={class:"muted"},q={key:0,class:"card",style:{"min-height":"80px"}},D={key:1,class:"card empty-state"},E={key:0},G={key:1},I={class:"grid"},M={class:"card lift train-card"},O={class:"row",style:{"justify-content":"space-between"}},H={class:"muted",style:{margin:"6px 0 12px"}},J={class:"row",style:{gap:"8px"}},K={class:"muted"},Q={class:"primary",style:{width:"100%","margin-top":"12px"}},R={__name:"Training",setup(U){const u=g([]),y=g(!0);function v(a){return a.sales_kit&&a.sales_kit.productName||a.input&&a.input.product||""}function x(a){return a.personas||a.persona_count||0}return b(async()=>{try{const a=(await C.listGroups()).groups||[];u.value=d.isAdmin?a:a.filter(r=>r.status==="ready")}finally{y.value=!1}}),(a,r)=>{const p=B("router-link");return i(),l("div",null,[t("div",V,[t("h2",F,[c(s(N),{size:22,"stroke-width":1.8,style:{"vertical-align":"-4px"}}),_(" "+e(s(n).t("training")),1)]),t("div",L,[c(p,{to:"/guide"},{default:m(()=>[t("button",S,[c(s(P),{size:18,"stroke-width":1.8}),r[0]||(r[0]=_(" คู่มือ",-1))])]),_:1}),s(d).isAdmin?(i(),h(p,{key:0,to:"/admin/new-group"},{default:m(()=>[t("button",$,[c(s(T),{size:18,"stroke-width":2}),_(" "+e(s(n).t("addProduct")),1)])]),_:1})):f("",!0)])]),t("p",j,e(s(n).t("trainingSubtitle")),1),y.value?(i(),l("div",q,[...r[1]||(r[1]=[t("div",{class:"skeleton",style:{height:"50px"}},null,-1)])])):u.value.length===0?(i(),l("div",D,[t("strong",null,e(s(n).t("noTraining")),1),s(d).isAdmin?(i(),l("span",E,'Click "'+e(s(n).t("addProduct"))+'" to create a persona group.',1)):(i(),l("span",G,"Ask an admin to create a persona group first."))])):f("",!0),t("div",I,[(i(!0),l(z,null,A(u.value,o=>(i(),h(p,{key:o.id,to:s(d).isAdmin?`/admin/groups/${o.id}/edit`:`/groups/${o.id}/personas`,style:{"text-decoration":"none"}},{default:m(()=>[t("div",M,[t("div",O,[t("strong",null,e(o.title),1),t("span",{class:k(["badge",o.status==="ready"?"ready":"draft"])},e(o.status),3)]),t("div",H,e(v(o)),1),t("div",J,[t("span",{class:k(["badge",o.channel||"line"])},e(o.channel||"line"),3),t("span",K,e(x(o))+" "+e(s(n).t("personas").toLowerCase()),1)]),t("button",Q,e(s(d).isAdmin?o.status==="ready"?s(n).t("managePersonas"):s(n).t("analyze"):s(n).t("selectPersona")),1)])]),_:2},1032,["to"]))),128))])])}}},tt=w(R,[["__scopeId","data-v-e39df241"]]);export{tt as default}; diff --git a/frontend/dist/assets/Training-onRzuNB5.css b/frontend/dist/assets/Training-onRzuNB5.css new file mode 100644 index 0000000..5c5c3e5 --- /dev/null +++ b/frontend/dist/assets/Training-onRzuNB5.css @@ -0,0 +1 @@ +.grid[data-v-b1b30fec]{display:grid;grid-template-columns:repeat(auto-fill,minmax(260px,1fr));gap:16px}.train-card[data-v-b1b30fec]{display:flex;flex-direction:column;height:100%}.badge.ready[data-v-b1b30fec]{background:#dcfce7;color:#166534}.badge.draft[data-v-b1b30fec]{background:#fef9c3;color:#854d0e}button.soft[data-v-b1b30fec]{background:#f1f3f9;border-color:transparent}.badge.facebook[data-v-b1b30fec]{background:#e0f2fe;color:#0369a1}.badge.line[data-v-b1b30fec]{background:#dcfce7;color:#15803d}.del-btn[data-v-b1b30fec]{background:transparent;border:1px solid var(--border);border-radius:8px;min-height:32px;width:32px;padding:0;display:inline-flex;align-items:center;justify-content:center;color:var(--muted);cursor:pointer}.del-btn[data-v-b1b30fec]:hover{background:#fee2e2;border-color:#fecaca;color:var(--red)} diff --git a/frontend/dist/assets/arrow-left-6ACDPyW9.js b/frontend/dist/assets/arrow-left-DF5NIPgR.js similarity index 86% rename from frontend/dist/assets/arrow-left-6ACDPyW9.js rename to frontend/dist/assets/arrow-left-DF5NIPgR.js index 7383561..d30787d 100644 --- a/frontend/dist/assets/arrow-left-6ACDPyW9.js +++ b/frontend/dist/assets/arrow-left-DF5NIPgR.js @@ -1,4 +1,4 @@ -import{c as e}from"./index-DNAVmyB5.js";/** +import{c as e}from"./index-CvsGOA4u.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-o2ZLc9m6.js b/frontend/dist/assets/book-open-BPDJkLfy.js similarity index 90% rename from frontend/dist/assets/book-open-o2ZLc9m6.js rename to frontend/dist/assets/book-open-BPDJkLfy.js index c37aa40..7b435ba 100644 --- a/frontend/dist/assets/book-open-o2ZLc9m6.js +++ b/frontend/dist/assets/book-open-BPDJkLfy.js @@ -1,4 +1,4 @@ -import{c as a}from"./index-DNAVmyB5.js";/** +import{c as a}from"./index-CvsGOA4u.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-DNAVmyB5.js b/frontend/dist/assets/index-CvsGOA4u.js similarity index 86% rename from frontend/dist/assets/index-DNAVmyB5.js rename to frontend/dist/assets/index-CvsGOA4u.js index 1b94010..b68563b 100644 --- a/frontend/dist/assets/index-DNAVmyB5.js +++ b/frontend/dist/assets/index-CvsGOA4u.js @@ -1,4 +1,4 @@ -const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/Login-O3bvqwVm.js","assets/Login-j4sHK_z1.css","assets/Setup-H6LANAbr.js","assets/lock-fipmYN8_.js","assets/Setup-BHyRmSn1.css","assets/Analytics-C8V4bNnH.js","assets/users-DG0hBplh.js","assets/plus-BGdYQyzB.js","assets/Analytics-CQgwlaXy.css","assets/MyBoard-Daz4fzbD.js","assets/layout-dashboard-dl9BywAR.js","assets/MyBoard-B7ypQ1JT.css","assets/Training-DTTvOhJR.js","assets/target-BYJn0LyF.js","assets/book-open-o2ZLc9m6.js","assets/Training-BYBkfggr.css","assets/Personas-B92S9P5I.js","assets/arrow-left-6ACDPyW9.js","assets/Personas-CskAUbZv.css","assets/Chat-5M8zA86W.js","assets/Chat-Dhsij-Zk.css","assets/Settings-C4qI85q5.js","assets/Settings-BCN2EZQ5.css","assets/Guide-B9uxl_Jn.js","assets/sparkles-0MI5vUsR.js","assets/GroupBuilder-0QeIeG8Q.js","assets/GroupBuilder-7-9ZskJo.css","assets/GroupEdit-yRcPSwSE.js","assets/GroupEdit-BdbUBMcX.css","assets/AdminUsers-B98Kq2kn.js","assets/AdminUsers-B0sl0uHS.css"])))=>i.map(i=>d[i]); +const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/Login-BxCw0ggt.js","assets/Login-j4sHK_z1.css","assets/Setup-C4CPiNJG.js","assets/lock-Lq5iEyDR.js","assets/Setup-BHyRmSn1.css","assets/Analytics-C9ppufAi.js","assets/users-Cs9RtsVY.js","assets/plus-BSRzoT5b.js","assets/Analytics-CQgwlaXy.css","assets/MyBoard-Bw1xIyZv.js","assets/layout-dashboard-k0A07o6y.js","assets/MyBoard-B7ypQ1JT.css","assets/Training-Ctr9xSAG.js","assets/target-BHq78myU.js","assets/book-open-BPDJkLfy.js","assets/Training-onRzuNB5.css","assets/Personas-OK6zEMMZ.js","assets/arrow-left-DF5NIPgR.js","assets/Personas-CskAUbZv.css","assets/Chat-pM87eouK.js","assets/Chat-Dhsij-Zk.css","assets/Settings-C96o5eMo.js","assets/Settings-BCN2EZQ5.css","assets/Guide-D116suSn.js","assets/sparkles-BfSk-aam.js","assets/GroupBuilder-DP_16_kW.js","assets/GroupBuilder-7-9ZskJo.css","assets/GroupEdit-46rsA4b7.js","assets/GroupEdit-BdbUBMcX.css","assets/AdminUsers-BZpNytKz.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-O3bvqwVm. * * 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}`),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-O3bvqwVm.js"),__vite__mapDeps([0,1])),meta:{public:!0}},{path:"/setup",component:()=>Se(()=>import("./Setup-H6LANAbr.js"),__vite__mapDeps([2,3,4]))},{path:"/",component:()=>Se(()=>import("./Analytics-C8V4bNnH.js"),__vite__mapDeps([5,6,7,8])),meta:{admin:!0}},{path:"/my/board",component:()=>Se(()=>import("./MyBoard-Daz4fzbD.js"),__vite__mapDeps([9,10,11]))},{path:"/training",component:()=>Se(()=>import("./Training-DTTvOhJR.js"),__vite__mapDeps([12,13,14,7,15]))},{path:"/groups/:gid/personas",component:()=>Se(()=>import("./Personas-B92S9P5I.js"),__vite__mapDeps([16,13,17,18]))},{path:"/groups/:gid/chat/:pid",component:()=>Se(()=>import("./Chat-5M8zA86W.js"),__vite__mapDeps([19,13,17,20]))},{path:"/settings",component:()=>Se(()=>import("./Settings-C4qI85q5.js"),__vite__mapDeps([21,3,22]))},{path:"/guide",component:()=>Se(()=>import("./Guide-B9uxl_Jn.js"),__vite__mapDeps([23,14,10,13,24]))},{path:"/admin/new-group",component:()=>Se(()=>import("./GroupBuilder-0QeIeG8Q.js"),__vite__mapDeps([25,17,26])),meta:{admin:!0}},{path:"/admin/groups/:gid/edit",component:()=>Se(()=>import("./GroupEdit-yRcPSwSE.js"),__vite__mapDeps([27,6,24,28])),meta:{admin:!0}},{path:"/admin/users",component:()=>Se(()=>import("./AdminUsers-B98Kq2kn.js"),__vite__mapDeps([29,6,30])),meta:{admin:!0}},{path:"/admin/analytics",component:()=>Se(()=>import("./Analytics-C8V4bNnH.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,Zs as D,Vu as E,tt as F,Xc as G,Mn as H,Yt as I,Uu 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-BxCw0ggt.js"),__vite__mapDeps([0,1])),meta:{public:!0}},{path:"/setup",component:()=>Se(()=>import("./Setup-C4CPiNJG.js"),__vite__mapDeps([2,3,4]))},{path:"/",component:()=>Se(()=>import("./Analytics-C9ppufAi.js"),__vite__mapDeps([5,6,7,8])),meta:{admin:!0}},{path:"/my/board",component:()=>Se(()=>import("./MyBoard-Bw1xIyZv.js"),__vite__mapDeps([9,10,11]))},{path:"/training",component:()=>Se(()=>import("./Training-Ctr9xSAG.js"),__vite__mapDeps([12,13,14,7,15]))},{path:"/groups/:gid/personas",component:()=>Se(()=>import("./Personas-OK6zEMMZ.js"),__vite__mapDeps([16,13,17,18]))},{path:"/groups/:gid/chat/:pid",component:()=>Se(()=>import("./Chat-pM87eouK.js"),__vite__mapDeps([19,13,17,20]))},{path:"/settings",component:()=>Se(()=>import("./Settings-C96o5eMo.js"),__vite__mapDeps([21,3,22]))},{path:"/guide",component:()=>Se(()=>import("./Guide-D116suSn.js"),__vite__mapDeps([23,14,10,13,24]))},{path:"/admin/new-group",component:()=>Se(()=>import("./GroupBuilder-DP_16_kW.js"),__vite__mapDeps([25,17,26])),meta:{admin:!0}},{path:"/admin/groups/:gid/edit",component:()=>Se(()=>import("./GroupEdit-46rsA4b7.js"),__vite__mapDeps([27,6,24,28])),meta:{admin:!0}},{path:"/admin/users",component:()=>Se(()=>import("./AdminUsers-BZpNytKz.js"),__vite__mapDeps([29,6,30])),meta:{admin:!0}},{path:"/admin/analytics",component:()=>Se(()=>import("./Analytics-C9ppufAi.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-dl9BywAR.js b/frontend/dist/assets/layout-dashboard-k0A07o6y.js similarity index 91% rename from frontend/dist/assets/layout-dashboard-dl9BywAR.js rename to frontend/dist/assets/layout-dashboard-k0A07o6y.js index 2f1f9db..a120f75 100644 --- a/frontend/dist/assets/layout-dashboard-dl9BywAR.js +++ b/frontend/dist/assets/layout-dashboard-k0A07o6y.js @@ -1,4 +1,4 @@ -import{c as t}from"./index-DNAVmyB5.js";/** +import{c as t}from"./index-CvsGOA4u.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-fipmYN8_.js b/frontend/dist/assets/lock-Lq5iEyDR.js similarity index 88% rename from frontend/dist/assets/lock-fipmYN8_.js rename to frontend/dist/assets/lock-Lq5iEyDR.js index 9a9833e..b16362a 100644 --- a/frontend/dist/assets/lock-fipmYN8_.js +++ b/frontend/dist/assets/lock-Lq5iEyDR.js @@ -1,4 +1,4 @@ -import{c as e}from"./index-DNAVmyB5.js";/** +import{c as e}from"./index-CvsGOA4u.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-BGdYQyzB.js b/frontend/dist/assets/plus-BSRzoT5b.js similarity index 86% rename from frontend/dist/assets/plus-BGdYQyzB.js rename to frontend/dist/assets/plus-BSRzoT5b.js index 12e360c..b606f26 100644 --- a/frontend/dist/assets/plus-BGdYQyzB.js +++ b/frontend/dist/assets/plus-BSRzoT5b.js @@ -1,4 +1,4 @@ -import{c as e}from"./index-DNAVmyB5.js";/** +import{c as e}from"./index-CvsGOA4u.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-0MI5vUsR.js b/frontend/dist/assets/sparkles-BfSk-aam.js similarity index 93% rename from frontend/dist/assets/sparkles-0MI5vUsR.js rename to frontend/dist/assets/sparkles-BfSk-aam.js index 0e0e88b..dd9bd02 100644 --- a/frontend/dist/assets/sparkles-0MI5vUsR.js +++ b/frontend/dist/assets/sparkles-BfSk-aam.js @@ -1,4 +1,4 @@ -import{c as a}from"./index-DNAVmyB5.js";/** +import{c as a}from"./index-CvsGOA4u.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-BYJn0LyF.js b/frontend/dist/assets/target-BHq78myU.js similarity index 90% rename from frontend/dist/assets/target-BYJn0LyF.js rename to frontend/dist/assets/target-BHq78myU.js index fe44ddc..00560d6 100644 --- a/frontend/dist/assets/target-BYJn0LyF.js +++ b/frontend/dist/assets/target-BHq78myU.js @@ -1,4 +1,4 @@ -import{c}from"./index-DNAVmyB5.js";/** +import{c}from"./index-CvsGOA4u.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-DG0hBplh.js b/frontend/dist/assets/users-Cs9RtsVY.js similarity index 90% rename from frontend/dist/assets/users-DG0hBplh.js rename to frontend/dist/assets/users-Cs9RtsVY.js index fbd898b..1dd7bef 100644 --- a/frontend/dist/assets/users-DG0hBplh.js +++ b/frontend/dist/assets/users-Cs9RtsVY.js @@ -1,4 +1,4 @@ -import{c as e}from"./index-DNAVmyB5.js";/** +import{c as e}from"./index-CvsGOA4u.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 d97c7b3..72db002 100644 --- a/frontend/dist/index.html +++ b/frontend/dist/index.html @@ -4,7 +4,7 @@ Sales Trainer - + diff --git a/frontend/src/api/index.js b/frontend/src/api/index.js index 98e70b8..10aaac8 100644 --- a/frontend/src/api/index.js +++ b/frontend/src/api/index.js @@ -43,6 +43,7 @@ export const api = { createGroup: (formData) => request('POST', '/api/groups', formData, true), listGroups: () => request('GET', '/api/groups'), getGroup: (id) => request('GET', `/api/groups/${id}`), + deleteGroup: (id) => request('DELETE', `/api/groups/${id}`), analyzeGroup: (id, opts = {}) => request('POST', `/api/groups/${id}/analyze${opts.append ? '?append=true' : ''}`), listPersonas: (gid) => request('GET', `/api/groups/${gid}/personas`), getPersona: (gid, pid) => request('GET', `/api/groups/${gid}/personas/${pid}`), diff --git a/frontend/src/views/Training.vue b/frontend/src/views/Training.vue index 1ea4dc2..660a3ca 100644 --- a/frontend/src/views/Training.vue +++ b/frontend/src/views/Training.vue @@ -20,17 +20,30 @@ Ask an admin to create a persona group first.
- -
-
+
+
+ {{ g.title }} + +
{{ g.status }} +
+
+
{{ productName(g) }}
{{ g.channel || 'line' }} @@ -39,15 +52,15 @@ -
-
+ +