feat(training): delete group button (admin only) on Training page
- Backend DELETE /api/groups/<gid> (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.
This commit is contained in:
@@ -324,6 +324,28 @@ def update_persona(gid: str, pid: str):
|
|||||||
return jsonify({"persona": strip_secret_fields(full)})
|
return jsonify({"persona": strip_secret_fields(full)})
|
||||||
|
|
||||||
|
|
||||||
|
@groups_bp.delete("/<gid>")
|
||||||
|
@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("/<gid>/reanalyze")
|
@groups_bp.post("/<gid>/reanalyze")
|
||||||
@require_auth
|
@require_auth
|
||||||
@require_roles("admin")
|
@require_roles("admin")
|
||||||
|
|||||||
@@ -95,3 +95,7 @@ class GroupStore:
|
|||||||
if not found:
|
if not found:
|
||||||
raise ValueError("persona not found")
|
raise ValueError("persona not found")
|
||||||
return self.groups.replace(gid, group)
|
return self.groups.replace(gid, group)
|
||||||
|
|
||||||
|
def delete(self, gid: str) -> None:
|
||||||
|
"""Hard-delete a group (personas/report included)."""
|
||||||
|
self.groups.delete(gid)
|
||||||
|
|||||||
@@ -93,4 +93,14 @@ after = len(r.get_json()["personas"])
|
|||||||
assert after > before, f"append should add personas (before={before}, after={after})"
|
assert after > before, f"append should add personas (before={before}, after={after})"
|
||||||
print(f"[ok] append adds personas ({before} -> {after}); existing kept")
|
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")
|
print("ALL USER-JOURNEY FLOW TESTS PASSED")
|
||||||
|
|||||||
@@ -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
|
* @license lucide-vue-next v1.0.0 - ISC
|
||||||
*
|
*
|
||||||
* This source code is licensed under the ISC license.
|
* 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.
|
* This source code is licensed under the ISC license.
|
||||||
* See the LICENSE file in the root directory of this source tree.
|
* 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};
|
||||||
@@ -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
|
* @license lucide-vue-next v1.0.0 - ISC
|
||||||
*
|
*
|
||||||
* This source code is licensed under the ISC license.
|
* This source code is licensed under the ISC license.
|
||||||
File diff suppressed because one or more lines are too long
@@ -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
|
* @license lucide-vue-next v1.0.0 - ISC
|
||||||
*
|
*
|
||||||
* This source code is licensed under the ISC license.
|
* This source code is licensed under the ISC license.
|
||||||
* See the LICENSE file in the root directory of this source tree.
|
* 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
|
* @license lucide-vue-next v1.0.0 - ISC
|
||||||
*
|
*
|
||||||
* This source code is licensed under the ISC license.
|
* This source code is licensed under the ISC license.
|
||||||
* See the LICENSE file in the root directory of this source tree.
|
* 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
|
* @license lucide-vue-next v1.0.0 - ISC
|
||||||
*
|
*
|
||||||
* This source code is licensed under the ISC license.
|
* 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.
|
* This source code is licensed under the ISC license.
|
||||||
* See the LICENSE file in the root directory of this source tree.
|
* 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};
|
||||||
File diff suppressed because one or more lines are too long
@@ -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
|
* @license lucide-vue-next v1.0.0 - ISC
|
||||||
*
|
*
|
||||||
* This source code is licensed under the ISC license.
|
* This source code is licensed under the ISC license.
|
||||||
@@ -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
|
* @license lucide-vue-next v1.0.0 - ISC
|
||||||
*
|
*
|
||||||
* This source code is licensed under the ISC license.
|
* This source code is licensed under the ISC license.
|
||||||
@@ -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};
|
||||||
@@ -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};
|
||||||
@@ -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
|
* @license lucide-vue-next v1.0.0 - ISC
|
||||||
*
|
*
|
||||||
* This source code is licensed under the ISC license.
|
* This source code is licensed under the ISC license.
|
||||||
@@ -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};
|
||||||
1
frontend/dist/assets/Training-BYBkfggr.css
vendored
1
frontend/dist/assets/Training-BYBkfggr.css
vendored
@@ -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}
|
|
||||||
6
frontend/dist/assets/Training-Ctr9xSAG.js
vendored
Normal file
6
frontend/dist/assets/Training-Ctr9xSAG.js
vendored
Normal file
@@ -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};
|
||||||
1
frontend/dist/assets/Training-DTTvOhJR.js
vendored
1
frontend/dist/assets/Training-DTTvOhJR.js
vendored
@@ -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};
|
|
||||||
1
frontend/dist/assets/Training-onRzuNB5.css
vendored
Normal file
1
frontend/dist/assets/Training-onRzuNB5.css
vendored
Normal file
@@ -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)}
|
||||||
@@ -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
|
* @license lucide-vue-next v1.0.0 - ISC
|
||||||
*
|
*
|
||||||
* This source code is licensed under the ISC license.
|
* This source code is licensed under the ISC license.
|
||||||
@@ -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
|
* @license lucide-vue-next v1.0.0 - ISC
|
||||||
*
|
*
|
||||||
* This source code is licensed under the ISC license.
|
* This source code is licensed under the ISC license.
|
||||||
File diff suppressed because one or more lines are too long
@@ -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
|
* @license lucide-vue-next v1.0.0 - ISC
|
||||||
*
|
*
|
||||||
* This source code is licensed under the ISC license.
|
* This source code is licensed under the ISC license.
|
||||||
@@ -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
|
* @license lucide-vue-next v1.0.0 - ISC
|
||||||
*
|
*
|
||||||
* This source code is licensed under the ISC license.
|
* This source code is licensed under the ISC license.
|
||||||
@@ -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
|
* @license lucide-vue-next v1.0.0 - ISC
|
||||||
*
|
*
|
||||||
* This source code is licensed under the ISC license.
|
* This source code is licensed under the ISC license.
|
||||||
@@ -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
|
* @license lucide-vue-next v1.0.0 - ISC
|
||||||
*
|
*
|
||||||
* This source code is licensed under the ISC license.
|
* This source code is licensed under the ISC license.
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import{c}from"./index-DNAVmyB5.js";/**
|
import{c}from"./index-CvsGOA4u.js";/**
|
||||||
* @license lucide-vue-next v1.0.0 - ISC
|
* @license lucide-vue-next v1.0.0 - ISC
|
||||||
*
|
*
|
||||||
* This source code is licensed under the ISC license.
|
* This source code is licensed under the ISC license.
|
||||||
@@ -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
|
* @license lucide-vue-next v1.0.0 - ISC
|
||||||
*
|
*
|
||||||
* This source code is licensed under the ISC license.
|
* This source code is licensed under the ISC license.
|
||||||
2
frontend/dist/index.html
vendored
2
frontend/dist/index.html
vendored
@@ -4,7 +4,7 @@
|
|||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>Sales Trainer</title>
|
<title>Sales Trainer</title>
|
||||||
<script type="module" crossorigin src="/assets/index-DNAVmyB5.js"></script>
|
<script type="module" crossorigin src="/assets/index-CvsGOA4u.js"></script>
|
||||||
<link rel="stylesheet" crossorigin href="/assets/index-B9WjPRES.css">
|
<link rel="stylesheet" crossorigin href="/assets/index-B9WjPRES.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ export const api = {
|
|||||||
createGroup: (formData) => request('POST', '/api/groups', formData, true),
|
createGroup: (formData) => request('POST', '/api/groups', formData, true),
|
||||||
listGroups: () => request('GET', '/api/groups'),
|
listGroups: () => request('GET', '/api/groups'),
|
||||||
getGroup: (id) => request('GET', `/api/groups/${id}`),
|
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' : ''}`),
|
analyzeGroup: (id, opts = {}) => request('POST', `/api/groups/${id}/analyze${opts.append ? '?append=true' : ''}`),
|
||||||
listPersonas: (gid) => request('GET', `/api/groups/${gid}/personas`),
|
listPersonas: (gid) => request('GET', `/api/groups/${gid}/personas`),
|
||||||
getPersona: (gid, pid) => request('GET', `/api/groups/${gid}/personas/${pid}`),
|
getPersona: (gid, pid) => request('GET', `/api/groups/${gid}/personas/${pid}`),
|
||||||
|
|||||||
@@ -20,17 +20,30 @@
|
|||||||
<span v-else>Ask an admin to create a persona group first.</span>
|
<span v-else>Ask an admin to create a persona group first.</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="grid">
|
<div class="grid">
|
||||||
<router-link
|
<div v-for="g in groups" :key="g.id" class="card lift train-card">
|
||||||
v-for="g in groups"
|
|
||||||
:key="g.id"
|
|
||||||
:to="auth.isAdmin ? `/admin/groups/${g.id}/edit` : `/groups/${g.id}/personas`"
|
|
||||||
style="text-decoration:none"
|
|
||||||
>
|
|
||||||
<div class="card lift train-card">
|
|
||||||
<div class="row" style="justify-content:space-between">
|
<div class="row" style="justify-content:space-between">
|
||||||
|
<router-link
|
||||||
|
:to="auth.isAdmin ? `/admin/groups/${g.id}/edit` : `/groups/${g.id}/personas`"
|
||||||
|
style="text-decoration:none;color:inherit;min-width:0"
|
||||||
|
>
|
||||||
<strong>{{ g.title }}</strong>
|
<strong>{{ g.title }}</strong>
|
||||||
|
</router-link>
|
||||||
|
<div class="row" style="gap:6px;align-items:center">
|
||||||
<span class="badge" :class="g.status === 'ready' ? 'ready' : 'draft'">{{ g.status }}</span>
|
<span class="badge" :class="g.status === 'ready' ? 'ready' : 'draft'">{{ g.status }}</span>
|
||||||
|
<button
|
||||||
|
v-if="auth.isAdmin"
|
||||||
|
class="del-btn"
|
||||||
|
:title="'ลบกลุ่มนี้ (ติดลบถาวร)'"
|
||||||
|
@click.prevent.stop="removeGroup(g)"
|
||||||
|
>
|
||||||
|
<Trash2 :size="16" :stroke-width="1.8" />
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
<router-link
|
||||||
|
:to="auth.isAdmin ? `/admin/groups/${g.id}/edit` : `/groups/${g.id}/personas`"
|
||||||
|
style="text-decoration:none;color:inherit"
|
||||||
|
>
|
||||||
<div class="muted" style="margin:6px 0 12px">{{ productName(g) }}</div>
|
<div class="muted" style="margin:6px 0 12px">{{ productName(g) }}</div>
|
||||||
<div class="row" style="gap:8px">
|
<div class="row" style="gap:8px">
|
||||||
<span class="badge" :class="g.channel || 'line'">{{ g.channel || 'line' }}</span>
|
<span class="badge" :class="g.channel || 'line'">{{ g.channel || 'line' }}</span>
|
||||||
@@ -39,15 +52,15 @@
|
|||||||
<button class="primary" style="width:100%;margin-top:12px">
|
<button class="primary" style="width:100%;margin-top:12px">
|
||||||
{{ auth.isAdmin ? (g.status === 'ready' ? i18n.t('managePersonas') : i18n.t('analyze')) : i18n.t('selectPersona') }}
|
{{ auth.isAdmin ? (g.status === 'ready' ? i18n.t('managePersonas') : i18n.t('analyze')) : i18n.t('selectPersona') }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
|
||||||
</router-link>
|
</router-link>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { onMounted, ref } from 'vue'
|
import { onMounted, ref } from 'vue'
|
||||||
import { Target, Plus, BookOpen } from 'lucide-vue-next'
|
import { Target, Plus, BookOpen, Trash2 } from 'lucide-vue-next'
|
||||||
import { api } from '../api'
|
import { api } from '../api'
|
||||||
import { auth } from '../store/auth'
|
import { auth } from '../store/auth'
|
||||||
import { i18n } from '../i18n'
|
import { i18n } from '../i18n'
|
||||||
@@ -62,6 +75,16 @@ function personaCount(g) {
|
|||||||
return (g.personas || g.persona_count || 0)
|
return (g.personas || g.persona_count || 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function removeGroup(g) {
|
||||||
|
if (!confirm(`ลบกลุ่ม "${g.title}" และบุคคลต้นแบบทั้งหมด? (ติดลบถาวร)`)) return
|
||||||
|
try {
|
||||||
|
await api.deleteGroup(g.id)
|
||||||
|
groups.value = groups.value.filter((x) => x.id !== g.id)
|
||||||
|
} catch (e) {
|
||||||
|
alert(e.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
try {
|
try {
|
||||||
const all = (await api.listGroups()).groups || []
|
const all = (await api.listGroups()).groups || []
|
||||||
@@ -81,4 +104,10 @@ onMounted(async () => {
|
|||||||
button.soft { background: #f1f3f9; border-color: transparent; }
|
button.soft { background: #f1f3f9; border-color: transparent; }
|
||||||
.badge.facebook { background: #e0f2fe; color: #0369a1; }
|
.badge.facebook { background: #e0f2fe; color: #0369a1; }
|
||||||
.badge.line { background: #dcfce7; color: #15803d; }
|
.badge.line { background: #dcfce7; color: #15803d; }
|
||||||
|
.del-btn {
|
||||||
|
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:hover { background: #fee2e2; border-color: #fecaca; color: var(--red); }
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
Reference in New Issue
Block a user