1import{r as x,u as fe,a as Q,j as e,L as Kt}from"./vendor-react-CBLV7qUt.js";import{p as De,l as Wt,a as st,u as At,bd as Ht,a8 as _,B as P,D as oe,Y as Gt,P as Yt,g as de,h as ce,i as me,J as ue,be as Qe,X as Jt,a6 as Ft,I as ae,b6 as Ie,C as It,d as Zt,F as $t,q as K,k as W,L,S as Xt,x as kt,f as Ye,ag as es,ah as ts,ai as ss,aj as z,E as as,ad as ns,bf as ie,m as rs,j as Je,U as is}from"./index-DUM73hpv.js";import{a as Ze,e as Ke,s as We,C as ls}from"./calendar-CBsxNXcG.js";import{A as je,a as be,b as ye,c as ve,d as ke,e as we,f as Ne,g as Ce}from"./alert-dialog-DnU35eLj.js";import{T as os,a as ds,b as Bt,c as Fe,d as cs,e as G}from"./table-CHyys0r6.js";import{S as Le,a as Ee,b as Oe,c as Re,d as R}from"./select-Dg2CCArX.js";import{B as Xe}from"./badge-D-RE7SRG.js";import{C as $e}from"./checkbox-DVpAPdwv.js";import{S as Lt}from"./scroll-area-CFqd4tIn.js";import{P as Et,a as Ot,b as Rt}from"./popover-Bpqzft3v.js";import{C as ms,a as us,b as hs,c as xs,d as gs,e as ps,f as wt}from"./command-BaJLtlbZ.js";import{T as Nt}from"./textarea-B-EFuClr.js";import{S as fs,W as js}from"./session-recording-C3fNn6B5.js";import{M as bs}from"./monitor-CBf6tLRU.js";import{C as ys}from"./calendar-days-BmutNlFE.js";import{a as et}from"./addWeeks-CaB8md2I.js";import{T as tt}from"./trash-2-B8sFk0am.js";import{R as vs}from"./repeat-DVsIf8_Z.js";import{C as ks}from"./circle-alert-CQ51hjyG.js";import{U as ws}
1from"./user-x-D-rlcfIb.js";import{W as Ns}from"./wifi-DaNG-k5I.js";import{s as Cs}from"./differenceInCalendarDays-D14lvNyU.js";import{f as E,s as He}from"./format-xXJg02vb.js";import{i as Se}from"./isSameDay-B8DpiG0_.js";import{s as Ct}from"./subDays-CllbyEm2.js";import{a as Be}from"./addDays-CNEgSBaJ.js";import"./chevron-left-BodA76JM.js";import"./index-BdQq_4o_.js";import"./chevron-up-CseyHy4T.js";import"./skeleton-Co5St8lD.js";import"./clock-l7oy0Jx1.js";import"./play-BuQlzxaK.js";import"./download-DI1klBAE.js";/** 2 * @license lucide-react v0.453.0 - ISC 3 * 4 * This source code is licensed under the ISC license. 5 * See the LICENSE file in the root directory of this source tree. 6 */const Ss=De("ArrowDown",[["path",{d:"M12 5v14",key:"s699le"}],["path",{d:"m19 12-7 7-7-7",key:"1idqje"}]]);/** 7 * @license lucide-react v0.453.0 - ISC 8 * 9 * This source code is licensed under the ISC license. 10 * See the LICENSE file in the root directory of this source tree. 11 */const Ds=De("ArrowUpDown",[["path",{d:"m21 16-4 4-4-4",key:"f6ql7i"}],["path",{d:"M17 20V4",key:"1ejh1v"}],["path",{d:"m3 8 4-4 4 4",key:"11wl7u"}],["path",{d:"M7 4v16",key:"1glfcx"}]]);/** 12 * @license lucide-react v0.453.0 - ISC 13 * 14 * This source code is licensed under the ISC license. 15 * See the LICENSE file in the root directory of this source tree. 16 */const Ts=De("ArrowUp",[["path",{d:"m5 12 7-7 7 7",key:"hav0vg"}],["path",{d:"M12 19V5",key:"x0mq9r"}]]);/** 17 * @license lucide-react v0.453.0 - ISC 18 * 19 * This source code is licensed under the ISC license. 20 * See the LICENSE file in the root directory of this source tree. 21 */const Ge=De("Disc",[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["circle",{cx:"12",cy:"12",r:"2",key:"1c9p78"}]]);/** 22 * @license lucide-react v0.453.0 - ISC 23 * 24 * This source code is licensed under the ISC license. 25 * See the LICENSE file in the root directory of this source tree. 26 */const Ms=De("Ellipsis",[["circle",{cx:"12",cy:"12",r:"1",key:"41hilf"}],["circle",{cx:"19",cy:"12",r:"1",key:"1wjl8i"}],["circle",{cx:"5",cy:"12",r:"1",key:"1pcz8c"}]]);function St(t,n){return Ze(t,-1)}function Dt(t,n){return et(t,-1)}const Ps=["cancelled","rescheduled","confirmed_complete","overdue"];function Ut(t){return typeof t=="string"&&Ps.includes(t)}const As=30,Fs=10;function Is(t,n){const r=new Date(t).getTime(),o=r+n*60*1e3;return{opens:r-As*60*1e3,closes:o+Fs*60*1e3}}function $s(t,n,r){const{closes:o}=Is(t,n),y=r.getTime()-o;return y>0?y/(60*60*1e3):0}function Bs(t){return t.absentStatus==="pending"||t.absentStatus==="confirmed"?"absent":String(t.status??"unknown")}function Ls(t){const n=new Map;for(const r of t){const o=Bs(r);n.set(o,(n.get(o)||0)+1)}return n}function Es(t){const n=[t.teacherJoinTime,t.studentJoinTime].filter(r=>!!r).map(r=>$s(t.startDatetime,t.durationMinutes,new Date(r))).filter(r=>r>0);return n.length===0?null:{hoursPast:Math.min(...n)}}const C=(t,n=!0)=>{var r;return t.classNickname||t.username||(n?t.email:`User ${((r=t.id)==null?void 0:r.slice(0,8))||"Unknown"}`)},Tt=["Sun","Mon","Tue","Wed","Thu","Fri","Sat"];function Mt({items:t,value:n,onChange:r,placeholder:o,searchPlaceholder:y,showEmails:b=!1,testId:m}){const[f,a]=x.useState(!1),h=t.find(k=>k.id===n);return e.jsxs(Et,{open:f,onOpenChange:a,children:[e.jsx(Ot,{asChild:!0,children:e.jsxs(P,{variant:"outline",role:"combobox","aria-expanded":f,className:"w-full md:w-auto md:min-w-[160px] justify-between font-normal h-9 text-sm","data-testid":m,children:[e.jsx("span",{className:"truncate",children:h?C(h,b):o}),e.jsx(ms,{className:"ml-2 h-4 w-4 shrink-0 opacity-50"})]})}),e.jsx(Rt,{className:"min-w-[220px] w-[--radix-popover-trigger-width] p-0",align:"start",children:e.jsxs(us,{children:[e.jsx(hs,{placeholder:y}),e.jsxs(xs,{children:[e.jsx(gs,{children:"No results found."}),e.jsxs(ps,{children:[e.jsxs(wt,{onSelect:()=>{r(""),a(!1)},children:[e.jsx(kt,{className:_("mr-2 h-4 w-4",n?"opacity-0":"opacity-100")}),o]}),t.map(k=>e.jsxs(wt,{value:C(k,b),onSelect:()=>{r(k.id),a(!1)},children:[e.jsx(kt,{className:_("mr-2 h-4 w-4",n===k.id?"opacity-100":"opacity-0")}),C(k,b)]},k.id))]})]})]})})]})}function Sa(){const{t}=Wt(),{toast:n}=st(),{user:r}
26=At(),o=(r==null?void 0:r.isSuperAdmin)===!0,[y,b]=x.useState(!1),m=(()=>{if(typeof window>"u")return{teacher:"",student:""};const s=new URLSearchParams(window.location.search);return{teacher:s.get("teacherId")??"",student:s.get("studentId")??""}})(),[f,a]=x.useState(m.teacher),[h,k]=x.useState(m.student),[d,w]=x.useState(""),A=!!(m.teacher||m.student),[l,T]=x.useState(A?void 0:{from:new Date,to:new Date}),[v,I]=x.useState(A),[U,M]=x.useState(!1),[he,i]=x.useState(new Date),[g,D]=x.useState("date"),[N,X]=x.useState("asc"),q=s=>{g===s?X(c=>c==="asc"?"desc":"asc"):(D(s),X("asc"))},{data:ee}=fe({queryKey:["/api/users"],enabled:!0}),{data:Ue}=fe({queryKey:["/api/teachers"],enabled:!0}),Y=l!=null&&l.from?Cs(l.from).toISOString():"",ne=l!=null&&l.from?Ht(l.to||l.from).toISOString():"",{data:F,isLoading:H,error:xe}=fe({queryKey:["/api/admin/sessions",f,h,Y,ne,v],queryFn:async()=>{const s=new URLSearchParams;f&&s.set("teacherId",f),h&&s.set("studentId",h),Y&&s.set("startDate",Y),ne&&s.set("endDate",ne),v&&s.set("limit","5000");const c=await fetch(`/api/admin/sessions?${s}`);if(!c.ok){const u=await c.json().catch(()=>({}));throw new Error(u.error||`Failed to fetch sessions: ${c.status} ${c.statusText}`)}return c.json()},enabled:!!Y||v}),Te=Q({mutationFn:async s=>W("POST","/api/admin/sessions/book",s),onSuccess:async()=>{n({title:t("Success"),description:"Session booked successfully"}),b(!1),await K.refetchQueries({queryKey:["/api/admin/sessions"]})},onError:s=>{n({title:t("Error"),description:s.message||"Failed to book session",variant:"destructive"})}}),re=Q({mutationFn:async s=>W("POST","/api/admin/sessions/bulk-book",s),onSuccess:async s=>{const c=s.booked||0,u=s.skipped||0;n({title:t("Success"),description:`Booked ${c} sessions${u>0?`, ${u} skipped due to conflicts`:""}`}),b(!1),await K.refetchQueries({queryKey:["/api/admin/sessions"]})},onError:s=>{n({title:t("Error"),description:s.message||"Failed to book sessions",variant:"destructive"})}}),ge=Q({mutationFn:async s=>W("DELETE",`/api/admin/sessions/${s}`),onSuccess:async()=>{n({title:t("Success"),description:"Session deleted successfully"}),await K.refetchQueries({queryKey:["/api/admin/sessions"]})},onError:s=>{n({title:t("Error"),description:s.message||"Failed to delete session",variant:"destructive"})}}),Me=s=>{ge.mutate(s)},p=s=>{Ae.mutate([s])},V=(ee==null?void 0:ee.users)||[],J=V.filter(s=>!s.isTeacher),pe=Ue||[],qe=(F==null?void 0:F.sessions)||[],$=x.useMemo(()=>{let s=[...qe];if(f&&(s=s.filter(u=>u.teacherId===f)),h&&(s=s.filter(u=>u.studentId===h)),d){const u=d.toLowerCase();s=s.filter(j=>{var ht,xt,gt,pt,ft,jt,bt,yt,vt;const te=[(ht=j.teacher)==null?void 0:ht.classNickname,(xt=j.teacher)==null?void 0:xt.displayName,(gt=j.teacher)==null?void 0:gt.username,...o?[(pt=j.teacher)==null?void 0:pt.email]:[]].filter(Boolean).join(" ").toLowerCase(),se=[(ft=j.student)==null?void 0:ft.classNickname,(jt=j.student)==null?void 0:jt.displayName,(bt=j.student)==null?void 0:bt.username,...o?[(yt=j.student)==null?void 0:yt.email]:[]].filter(Boolean).join(" ").toLowerCase(),Qt=((vt=j.id)==null?void 0:vt.toLowerCase())||"";return te.includes(u)||se.includes(u)||Qt.includes(u)})}const c=N==="asc"?1:-1;return s.sort((u,j)=>{switch(g){case"student":{const te=C(u.student||{},!1).toLowerCase(),se=C(j.student||{},!1).toLowerCase();return c*te.localeCompare(se)}case"teacher":{const te=C(u.teacher||{},!1).toLowerCase(),se=C(j.teacher||{},!1).toLowerCase();return c*te.localeCompare(se)}case"duration":return c*((u.durationMinutes||0)-(j.durationMinutes||0));case"cost":return c*((u.gemCost||0)-(j.gemCost||0));case"status":return c*(u.status||"").localeCompare(j.status||"");case"date":default:{const te=new Date(u.startDatetime).getTime(),se=new Date(j.startDatetime).getTime();return c*(te-se)}}}),s},[qe,f,h,d,l,o,g,N]),[S,Z]=x.useState(new Set),[Pe,qt]=x.useState("livekit"),[at,nt]=x.useState(!1),[rt,it]=x.useState(!1),[lt,ot]=x.useState(!1),ze=$,dt=x.useMemo(()=>Ls($),[$]),ct=F==null?void 0:F.totalMatching,zt=!!(F!=null&&F.truncated)&&!d&&!f&&!h,mt=v?"All time":l!=null&&l.from?`${E(l.from,"MMM d")} â ${E(l.to||l.from,"MMM d, yyyy")}`:"",O=x.useMemo(()=>$.filter(s=>S.has(s.id)&&["scheduled","on_track"].includes(s.status)&&!s.absentStatus),[$,S]),B=x.useMemo(()=>$.filter(s=>S.has(s.id)&&Ut(s.status)),[$,S]),ut=ze.length>0&&ze.every(s=>S.has(s.id)),_t=s=>{Z(c=>{const u=new Set(c);return u.has(s)?u.delete(s):u.add(s),u})},Vt=()=>{Z(ut?new Set:new Set(ze.map(s=>s.id)))};x.useEffect(()=>{Z(new Set)},[f,h,d,Y,ne]);const _e=Q({mutationFn:async({sessionIds:s,provider:c})=>(await W("POST","/api/admin/sessions/bulk-provider",{sessionIds:s,videoProvider:c})).json(),onSuccess:async s=>{const c=s.results.filter(j=>j.success).length,u=s.results.filter(j=>!j.success);u.length>0?n({title:"Bulk Update Complete",description:`${c} updated, ${u.length} failed: ${u.map(j=>j.error).join("; ")}`,variant:u.length===s.results.length?"destructive":"default"}):n({title:"Bulk Update Complete",description:`${c} session${c!==1?"s":""} updated`}),Z(new Set),await K.refetchQueries({queryKey:["/api/admin/sessions"]})},onError:s=>{n({title:"Error",description:s.message||"Bulk update failed",variant:"destructive"})}}),Ae=Q({mutationFn:async s=>(await W("POST","/api/admin/sessions/bulk-cancel",{sessionIds:s})).json(),onSuccess:async s=>{const c=s.results.filter(j=>!j.success),u=s.totalRefundedGems>0?`, ${s.totalRefundedGems} gems refunded`:"";c.length>0?n({title:"Bulk Cancel Complete",description:`${s.cancelled} cancelled${u}. ${c.length} failed: ${c.map(j=>j.error).join("; ")}`,variant:c.length===s.results.length?"destructive":"default"}):n({title:"Bulk Cancel Complete",description:`${s.cancelled} booking${s.cancelled!==1?"s":""} cancelled${u}`}),Z(new Set),await K.refetchQueries({queryKey:["/api/admin/sessions"]})},onError:s=>{n({title:"Error",description:s.message||"Bulk cancel failed",variant:"destructive"})}}),Ve=Q({mutationFn:async s=>(await W("POST","/api/admin/sessions/bulk-delete",{sessionIds:s})).json(),onSuccess:async s=>{const c=s.results.filter(j=>j.success).length,u=s.results.filter(j=>!j.success);u.length>0?n({title:"Bulk Delete Complete",description:`${c} deleted, ${u.length} failed: ${u.map(j=>j.error).join("; ")}`,variant:u.length===s.results.length?"destructive":"default"}):n({title:"Bulk Delete Complete",description:`${c} booking${c!==1?"s":""} deleted`}),Z(new Set),await K.refetchQueries({queryKey:["/api/admin/sessions"]})},onError:s=>{n({title:"Error",description:s.message||"Bulk delete failed",variant:"destructive"})}});
26return e.jsxs("div",{className:_("container mx-auto pt-8 px-4",S.size>0?"pb-24":"pb-4"),children:[e.jsxs("div",{className:"flex justify-between items-center mb-6",children:[e.jsxs("div",{children:[e.jsx("h1",{className:"text-3xl font-bold","data-testid":"text-page-title",children:"Admin: Session Bookings"}),e.jsx("p",{className:"text-muted-foreground",children:"Book sessions on behalf of students and manage all bookings"}),!H&&!xe&&e.jsxs("div",{className:"mt-2 flex flex-wrap items-center gap-x-3 gap-y-1","data-testid":"text-booking-count",children:[e.jsxs("span",{className:"text-sm font-medium",children:[e.jsx("span",{translate:"no",children:$.length.toLocaleString()})," ",$.length===1?"booking":"bookings",mt&&e.jsxs("span",{className:"text-muted-foreground font-normal",children:[" · ",mt]})]}),_s.filter(({key:s})=>dt.get(s)).map(({key:s,label:c,className:u})=>e.jsxs("span",{className:_("text-xs px-2 py-0.5 rounded-full border",u),"data-testid":`count-${s}`,children:[e.jsx("span",{translate:"no",children:dt.get(s)})," ",c]},s)),zt&&ct!==void 0&&e.jsxs("span",{className:"text-xs text-amber-600 dark:text-amber-500","data-testid":"text-count-truncated",children:["showing the most recent ",$.length.toLocaleString()," of ",ct.toLocaleString()," â narrow the dates to see the rest"]})]})]}),e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx(Kt,{href:"/admin/bookings/monitor",children:e.jsxs(P,{variant:"outline","data-testid":"button-session-monitor",children:[e.jsx(bs,{className:"w-4 h-4 mr-2"}),"Live Monitor"]})}),e.jsxs(oe,{open:y,onOpenChange:b,children:[e.jsx(Gt,{asChild:!0,children:e.jsxs(P,{"data-testid":"button-new-booking",children:[e.jsx(Yt,{className:"w-4 h-4 mr-2"}),"New Booking"]})}),e.jsxs(de,{className:"max-w-2xl max-h-[85vh] overflow-y-auto",children:[e.jsxs(ce,{children:[e.jsx(me,{children:"Book Session for Student"}),e.jsx(ue,{children:"Create a session booking on behalf of a student. Gems will be deducted from the student's wallet."})]}),e.jsx(Os,{students:J,teachers:pe,onSubmit:s=>Te.mutate(s),onBulkSubmit:s=>re.mutate(s),isSubmitting:Te.isPending||re.isPending,isSuperAdmin:o})]})]})]})]}),e.jsxs("div",{className:"flex items-center gap-2 mb-4 flex-wrap border-b pb-4",children:[e.jsxs(Et,{open:U,onOpenChange:M,children:[e.jsx(Ot,{asChild:!0,children:e.jsxs(P,{variant:"outline",size:"sm",className:_("justify-start text-left font-normal h-9",!(l!=null&&l.from)&&!v&&"text-muted-foreground"),"data-testid":"button-date-picker",children:[e.jsx(ys,{className:"mr-2 h-4 w-4"}),v?"All Time":l!=null&&l.from?l.to&&!Se(l.from,l.to)?`${E(l.from,"MMM d")} â ${E(l.to,"MMM d, yyyy")}`:E(l.from,"MMM d, yyyy"):"Pick a date"]})}),e.jsx(Rt,{className:"w-auto p-0",align:"start",sideOffset:8,children:e.jsxs("div",{className:"flex",children:[e.jsxs("div",{className:"border-r p-3 space-y-1 min-w-[140px]",children:[e.jsx("p",{className:"text-xs font-medium text-muted-foreground mb-2 px-2",children:"Quick Select"}),[{label:"Yesterday",range:()=>({from:Ct(new Date,1),to:Ct(new Date,1)})},{label:"Today",range:()=>({from:new Date,to:new Date})},{label:"Tomorrow",range:()=>({from:Be(new Date,1),to:Be(new Date,1)})},{label:"divider1"},{label:"Last Week",range:()=>({from:He(Dt(new Date),{weekStartsOn:1}),to:Ke(Dt(new Date),{weekStartsOn:1})})},{label:"This Week",range:()=>({from:He(new Date,{weekStartsOn:1}),to:Ke(new Date,{weekStartsOn:1})})},{label:"Next Week",range:()=>({from:He(et(new Date,1),{weekStartsOn:1}),to:Ke(et(new Date,1),{weekStartsOn:1})})},{label:"divider2"},{label:"Last Month",range:()=>({from:We(St(new Date)),to:Qe(St(new Date))})},{label:"This Month",range:()=>({from:We(new Date),to:Qe(new Date)})},{label:"Next Month",range:()=>({from:We(Ze(new Date,1)),to:Qe(Ze(new Date,1))})},{label:"divider3"},{label:"All Time",allTime:!0}].map(s=>s.label.startsWith("divider")?e.jsx("div",{className:"border-t my-1"},s.label):e.jsx(P,{variant:"ghost",size:"sm",className:"w-full justify-start text-sm h-8 px-2",onClick:()=>{if(s.allTime)T(void 0),I(!0),M(!1);else{const c=s.range();T(c),I(!1),i(c.from),M(!1)}},children:s.label},s.label))]}),e.jsx(ls,{mode:"range",selected:l,onSelect:s=>{T(s),I(!1)},numberOfMonths:2,month:he,onMonthChange:i})]})})]}),(v||(l==null?void 0:l.from)&&!(Se(l.from,new Date)&&(!l.to||Se(l.to,new Date))))&&e.jsx(P,{variant:"ghost",size:"sm",onClick:()=>{T({from:new Date,to:new Date}),I(!1),i(new Date)},className:"h-8 px-1 text-muted-foreground hover:text-foreground",title:"Reset to today",children:e.jsx(Jt,{className:"h-4 w-4"})}),e.jsx(Mt,{items:pe,value:f,onChange:a,placeholder:"All Teachers",searchPlaceholder:"Search teachers...",showEmails:o,testId:"select-teacher-filter"}),e.jsx(Mt,{items:V,value:h,onChange:k,placeholder:"All Students",searchPlaceholder:"Search students...",showEmails:o,testId:"select-student-filter"}),e.jsxs("div",{className:"relative",children:[e.jsx(Ft,{className:"absolute left-3 top-1/2 transform -translate-y-1/2 w-4 h-4 text-muted-foreground"}),e.jsx(ae,{placeholder:o?"Search name, email, ID...":"Search name, ID...",value:d,onChange:s=>w(s.target.value),className:"pl-9 h-9 w-[200px] text-sm","data-testid":"input-search"})]}),e.jsx("button",{className:"text-sm text-muted-foreground hover:text-foreground transition-colors ml-auto",onClick:()=>{a(""),k(""),w(""),T({from:new Date,to:new Date}),I(!1),i(new Date)},children:"Clear filters"})]}),xe?e.jsxs("div",{className:"bg-destructive/10 p-4 rounded-md text-destructive flex items-center gap-2",children:[e.jsx(Ie,{className:"h-5 w-5"}),e.jsxs("div",{children:[e.jsx("p",{className:"font-semibold",children:"Error loading sessions"}),e.jsx("p",{className:"text-sm",children:xe.message})]})]}):H?e.jsx("div",{className:"text-center py-12 text-muted-foreground",children:"Loading sessions..."}):$.length===0?e.jsx(It,{children:e.jsx(Zt,{className:"py-12 text-center text-muted-foreground",children:"No sessions foun
26d. Create your first admin booking above."})}):e.jsxs("div",{className:"border rounded-lg overflow-hidden",children:[S.size>0&&e.jsxs("div",{className:"flex items-center gap-3 bg-primary/10 border-b border-primary/20 px-4 py-2.5",children:[e.jsxs("span",{className:"text-sm font-semibold text-primary",children:[S.size," selected"]}),e.jsx("div",{className:"h-4 w-px bg-border"}),e.jsx("span",{className:"text-sm text-muted-foreground",children:"Change provider:"}),e.jsxs(Le,{value:Pe,onValueChange:qt,children:[e.jsx(Ee,{className:"w-56 h-8 text-sm",children:e.jsx(Oe,{})}),e.jsxs(Re,{children:[e.jsx(R,{value:"livekit",children:"Standard System (LiveKit)"}),e.jsx(R,{value:"tencent_trtc",children:"Global China Bridge (Tencent)"}),e.jsx(R,{value:"agora",children:"Universal System (Agora)"}),e.jsx(R,{value:"classin",children:"ClassIn (VIP China)"})]})]}),e.jsx(P,{size:"sm",onClick:()=>nt(!0),disabled:_e.isPending,children:_e.isPending?"Applying...":"Apply to All"}),e.jsx("div",{className:"h-4 w-px bg-border"}),e.jsxs(P,{variant:"outline",size:"sm",onClick:()=>ot(!0),disabled:Ae.isPending||O.length===0,title:O.length===0?"None of the selected bookings can be cancelled":void 0,"data-testid":"button-bulk-cancel",children:[e.jsx($t,{className:"h-4 w-4 mr-1"}),Ae.isPending?"Cancelling...":`Cancel Selected${O.length!==S.size?` (${O.length})`:""}`]}),e.jsxs(P,{variant:"destructive",size:"sm",onClick:()=>it(!0),disabled:Ve.isPending||B.length===0,title:B.length===0?"Only cancelled or finished bookings can be deleted":void 0,"data-testid":"button-bulk-delete",children:[e.jsx(tt,{className:"h-4 w-4 mr-1"}),Ve.isPending?"Deleting...":`Delete Selected${B.length!==S.size?` (${B.length})`:""}`]}),e.jsx(P,{variant:"ghost",size:"sm",onClick:()=>Z(new Set),children:"Cancel"})]}),e.jsxs(os,{children:[e.jsx(ds,{children:e.jsxs(Bt,{className:"bg-muted/50",children:[e.jsx(Fe,{className:"w-[40px]",children:e.jsx($e,{checked:ut,onCheckedChange:Vt,"aria-label":"Select all"})}),e.jsx(le,{field:"student",label:"Student",currentField:g,direction:N,onSort:q}),e.jsx(le,{field:"teacher",label:"Teacher",currentField:g,direction:N,onSort:q}),e.jsx(le,{field:"date",label:"Date & Time",currentField:g,direction:N,onSort:q}),e.jsx(le,{field:"duration",label:"Duration",currentField:g,direction:N,onSort:q,className:"hidden md:table-cell"}),e.jsx(le,{field:"cost",label:"Cost",currentField:g,direction:N,onSort:q,className:"hidden md:table-cell"}),e.jsx(le,{field:"status",label:"Status",currentField:g,direction:N,onSort:q}),e.jsx(Fe,{className:"font-heading text-[11px] uppercase tracking-wider",children:"Provider"}),e.jsx(Fe,{className:"w-[40px]"})]})}),e.jsx(cs,{children:$.map(s=>e.jsx(Qs,{session:s,showEmails:o,onDelete:Me,onCancel:p,isSelected:S.has(s.id),onToggleSelect:_t},s.id))})]})]}),at&&e.jsx(je,{open:at,onOpenChange:nt,children:e.jsxs(be,{children:[e.jsxs(ye,{children:[e.jsx(ve,{children:"Change Video Provider"}),e.jsx(ke,{asChild:!0,children:e.jsxs("div",{className:"space-y-3",children:[e.jsxs("p",{children:["Are you sure you want to change the video provider to"," ",e.jsx("span",{className:"font-semibold text-foreground",children:{livekit:"Standard System (LiveKit)",tencent_trtc:"Global China Bridge (Tencent)",agora:"Universal System (Agora)",classin:"ClassIn (VIP China)"}[Pe]||Pe})," ","for ",S.size," session",S.size!==1?"s":"","?"]}),e.jsx("div",{className:"max-h-40 overflow-y-auto rounded-md border p-2 space-y-1",children:$==null?void 0:$.filter(s=>S.has(s.id)).map(s=>{const c=s.startTime?E(new Date(s.startTime),"MMM d, HH:mm"):"No date";return e.jsxs("div",{className:"flex items-center justify-between text-sm py-1 px-1",children:[e.jsxs("span",{className:"font-medium",children:[C(s.student||{},!1)," Ã ",C(s.teacher||{},!1)]}),e.jsx("span",{className:"text-muted-foreground text-xs",children:c})]},s.id)})})]})})]}),e.jsxs(we,{children:[e.jsx(Ne,{children:"Cancel"}),e.jsx(Ce,{onClick:()=>{_e.mutate({sessionIds:Array.from(S),provider:Pe})},children:"Confirm"})]})]})}),lt&&e.jsx(je,{open:lt,onOpenChange:ot,children:e.jsxs(be,{children:[e.jsxs(ye,{children:[e.jsxs(ve,{children:["Cancel ",O.length," booking",O.length!==1?"s":"","?"]}),e.jsxs(ke,{children:["Each student gets a full refund of the gems they paid, with no cancellation penalty and no effect on anyone's hearts or reliability. Students and teachers are notified by email and in the app.",O.length!==S.size&&e.jsxs(e.Fragment,{children:[" ","The other ",S.size-O.length," selected booking",S.size-O.length!==1?"s are":" is"," already cancelled, finished, or under absent review, so ",S.size-O.length!==1?"they":"it"," will be skipped."]})]})]}),e.jsxs(we,{children:[e.jsx(Ne,{children:"Go back"}),e.jsxs(Ce,{onClick:()=>Ae.mutate(O.map(s=>s.id)),"data-testid":"button-confirm-bulk-cancel",children:["Cancel ",O.length," booking",O.length!==1?"s":""]})]})]})}),rt&&e.jsx(je,{open:rt,onOpenChange:it,children:e.jsxs(be,{children:[e.jsxs(ye,{children:[e.jsxs(ve,{children:["Delete ",B.length," booking",B.length!==1?"s":"","?"]}),e.jsxs(ke,{children:["This removes the booking",B.length!==1?"s":""," from the schedule and from the daily report. It is a soft delete (reversible by a developer), but students and teachers will no longer see ",B.length!==1?"them":"it",". This does not refund gems.",B.length!==S.size&&e.jsxs(e.Fragment,{children:[" ","The other ",S.size-B.length," selected booking",S.size-B.length!==1?"s are":" is"," still live and will be skipped â cancel ",S.size-B.length!==1?"them":"it"," first so the student gets a refund."]})]})]}),e.jsxs(we,{children:[e.jsx(Ne,{children:"Cancel"}),e.jsxs(Ce,{className:"bg-destructive text-destructive-foreground hover:bg-destructive/90",onClick:()=>Ve.mutate(B.map(s=>s.id)),children:["Delete ",B.length," booking",B.length!==1?"s":""]})]})]})})]})}function Pt({users:t,value:n,onChange:r,placeholder:o,testId:y,showCredits:b=!1,showEmails:m=!0}){const[f,a]=x.useState(""),h=x.useMemo(()=>
26{if(!f)return t;const d=f.toLowerCase();return t.filter(w=>{const A=C(w,m).toLowerCase(),l=(w.username||"").toLowerCase(),T=(w.classNickname||"").toLowerCase();if(m){const v=(w.email||"").toLowerCase();return A.includes(d)||v.includes(d)||l.includes(d)||T.includes(d)}return A.includes(d)||l.includes(d)||T.includes(d)})},[t,f,m]),k=t.find(d=>d.id===n);return e.jsxs("div",{className:"space-y-2",children:[e.jsxs("div",{className:"relative",children:[e.jsx(Ft,{className:"absolute left-2.5 top-2.5 h-4 w-4 text-muted-foreground"}),e.jsx(ae,{placeholder:`Search ${o}...`,value:f,onChange:d=>a(d.target.value),className:"pl-8","data-testid":`${y}-search`})]}),k&&e.jsxs("div",{className:"flex items-center gap-2 p-2 bg-muted rounded-md",children:[e.jsx("span",{className:"text-sm font-medium",children:"Selected:"}),e.jsx("span",{className:"text-sm",children:C(k,m)}),k.classNickname&&k.username&&e.jsxs("span",{className:"text-xs text-muted-foreground",children:["(@",k.username,")"]}),b&&e.jsxs(Xe,{variant:"secondary",className:"ml-auto",children:[e.jsx(Ye,{className:"h-3 w-3 mr-1"}),k.credits||0]})]}),e.jsx(Lt,{className:"h-40 border rounded-md",children:e.jsx("div",{className:"p-1",children:h.length===0?e.jsx("div",{className:"p-4 text-center text-sm text-muted-foreground",children:"No users found"}):h.map(d=>e.jsxs("div",{className:`flex items-center justify-between p-2 rounded-md cursor-pointer hover-elevate ${n===d.id?"bg-primary/10":""}`,onClick:()=>r(d.id),"data-testid":`${y}-option-${d.id}`,children:[e.jsxs("div",{className:"flex flex-col",children:[e.jsx("span",{className:"text-sm font-medium",children:C(d,m)}),d.classNickname&&d.username&&e.jsxs("span",{className:"text-xs text-muted-foreground",children:["@",d.username]}),m&&!d.classNickname&&d.email&&d.username&&e.jsx("span",{className:"text-xs text-muted-foreground",children:d.email})]}),b&&e.jsxs(Xe,{variant:"outline",className:"shrink-0",children:[e.jsx(Ye,{className:"h-3 w-3 mr-1"}),d.credits||0]})]},d.id))})})]})}function Os({students:t,teachers:n,onSubmit:r,onBulkSubmit:o,isSubmitting:y,isSuperAdmin:b=!1}){const[m,f]=x.useState(!1),[a,h]=x.useState({teacherId:"",studentId:"",startDatetime:"",startTime:"09:00",durationMinutes:25,customPrice:"",isFreeClass:!1,selectedDays:[],sessionCount:4}),k=i=>{h(g=>({...g,selectedDays:g.selectedDays.includes(i)?g.selectedDays.filter(D=>D!==i):[...g.selectedDays,i].sort()}))},w=m?(()=>{if(a.selectedDays.length===0||!a.startDatetime)return[];const i=[];let g=new Date(a.startDatetime),D=0;for(;D<a.sessionCount&&D<50;){const N=g.getDay();a.selectedDays.includes(N)&&(i.push({date:new Date(g),dayName:Tt[N]}),D++),g=Be(g,1)}return i})():[],A=n.find(i=>String(i.id)===String(a.teacherId)),l=(()=>{const i=A==null?void 0:A.pricing;if(!i)return null;const D={10:i.slot10min,25:i.slot25min,50:i.slot50min}[a.durationMinutes];return typeof D=="number"?D:null})(),T=a.customPrice.trim()===""?null:Number(a.customPrice),v=T!==null&&Number.isFinite(T)&&T>=0,I=a.isFreeClass?0:v?T:l,U=m?w.length:1,M=I===null?null:I*U,he=i=>{if(i.preventDefault(),m){if(w.length===0)return;const g=w.map(N=>{const[X,q]=a.startTime.split(":").map(Number),ee=new Date(N.date);return ee.setHours(X,q,0,0),ee.toISOString()}),D={teacherId:a.teacherId,studentId:a.studentId,sessions:g,durationMinutes:a.durationMinutes};a.isFreeClass?(D.isFreeClass=!0,D.customPrice=0):a.customPrice&&(D.customPrice=parseInt(a.customPrice,10)),o(D)}else{const D=new Date(a.startDatetime).toISOString(),N={teacherId:a.teacherId,studentId:a.studentId,startDatetime:D,durationMinutes:a.durationMinutes};a.isFreeClass?(N.isFreeClass=!0,N.customPrice=0):a.customPrice&&(N.customPrice=parseInt(a.customPrice,10)),r(N)}};return e.jsxs("form",{onSubmit:he,className:"space-y-4",children:[e.jsxs("div",{className:"flex items-center justify-between pb-2 border-b",children:[e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx(vs,{className:"h-4 w-4 text-muted-foreground"}),e.jsx(L,{htmlFor:"bulk-mode",children:"Bulk Booking Mode"})]}),e.jsx(Xt,{id:"bulk-mode",checked:m,onCheckedChange:f,"data-testid":"switch-bulk-mode"})]}),e.jsxs("div",{children:[e.jsx(L,{children:"Student"}),e.jsx(Pt,{users:t,value:a.studentId,onChange:i=>h({...a,studentId:i}),placeholder:"students",testId:"select-student",showCredits:!0,showEmails:b})]}),e.jsxs("div",{children:[e.jsx(L,{children:"Teacher"}),e.jsx(Pt,{users:n,value:a.teacherId,onChange:i=>h({...a,teacherId:i}),placeholder:"teachers",testId:"select-teacher",showEmails:b})]}),m?e.jsxs(e.Fragment,{children:[e.jsxs("div",{children:[e.jsx(L,{children:"Start Date"}),e.jsx(ae,{type:"date",value:a.startDatetime,onChange:i=>h({...a,startDatetime:i.target.value}),required:!0,"data-testid":"input-start-date"})]}),e.jsxs("div",{children:[e.jsx(L,{children:"Session Time"}),e.jsx(ae,{type:"time",value:a.startTime,onChange:i=>h({...a,startTime:i.target.value}),required:!0,"data-testid":"input-start-time"})]}),e.jsxs("div",{children:[e.jsx(L,{className:"mb-2 block",children:"Days of Week"}),e.jsx("div",{className:"flex flex-wrap gap-2",children:Tt.map((i,g)=>e.jsx("div",{className:`flex items-center gap-1.5 px-3 py-1.5 rounded-md border cursor-pointer transition-colors ${a.selectedDays.includes(g)?"bg-primary text-primary-foreground border-primary":"hover-elevate"}`,onClick:()=>k(g),"data-testid":`checkbox-day-${g}`,children:e.jsx("span",{className:"text-sm font-medium",children:i})},i))})]}),e.jsxs("div",{children:[e.jsx(L,{htmlFor:"session-count",children:"Number of Sessions"}),e.jsx(ae,{id:"session-count",type:"number",min:"1",max:"50",value:a.sessionCount,onChange:i=>h({...a,sessionCount:parseInt(i.target.value)||1}),"data-testid":"input-session-count"})]}),w.length>0&&e.jsxs("div",{className:"p-3 bg-muted rounded-md",children:[e.jsxs("p",{className:"text-sm font-medium mb-2",children:["Preview (",w.length," sessions):"]}),e.jsx(Lt,{className:"h-32",children:e.jsx("div",{className:"space-y-1",children:w.map((i,g)=>e.jsxs("div",{className:"text-sm flex items-center gap-2",children:[e.jsx(Xe,{variant:"outline",className:"shrink-0",children:i.dayName}),e.jsxs("span",{children:[E(i.date,"MMM d, yyyy")," at ",a.startTime]})]},g))})})]})]}):e.jsxs("div",{children:[e.jsx(L,{htmlFor:"datetime",children:"Start Date & Time"}),e.jsx(ae,{id:"datetime",type:"datetime-local",value:a.startDatetime,onChange:i=>h({...a,startDatetime:i.target.value}),required:!0,"data-testid":"input-datetime"}),e.jsxs("p",{className:"text-sm text-muted-foreground mt-1",children:["Your browser timezone: ",e.jsx("span",{className:"font-medium",children:Intl.DateTimeFormat().resolvedOptions().timeZone})," (UTC",new Date().getTimezoneOffset()>0?"-":"+",Math.abs(new Date().getTimezoneOffset()/60),")"]})]}),e.jsxs("div",{children:[e.jsx(L,{htmlFor:"duration",children:"Duration"}),e.jsxs(Le,{value:a.durationMinutes.toString(),onValueChange:i=>h({...a,durationMinutes:parseInt(i,10)}),children:[e.jsx(Ee,{"data-testid":"select-duration",children:e.jsx(Oe,{})}),e.jsxs(Re,{children:[e.jsx(R,{value:"10",children:"10 minutes"}),e.jsx(R,{value:"25",children:"25 minutes"}),e.jsx(R,{value:"50",children:"50 minutes"})]})]})]}),e.jsxs("div",{className:"space-y-3",children:[e.jsxs("div",{className:"flex items-center space-x-3 p-3 rounded-md bg-green-50 dark:bg-green-950/30 border border-green-200 dark:border-green-800",children:[e.jsx($e,{id:"isFreeClass",checked:a.isFreeClass,onCheckedChange:i=>h({...a,isFreeClass:i,customPrice:i?"":a.customPrice}),"data-testid":"checkbox-free-class"}),e.jsxs("div",{className:"flex-1",children:[e.jsx(L,{htmlFor:"isFreeClass",className:"cursor-pointer font-medium",children:"Free Class (Compensation)"}),e.jsx("p",{className:"text-xs text-muted-foreground",children:"No gems will be charged - use for makeup classes or compensation"})]})]}),!a.isFreeClass&&e.jsxs("div",{children:[e.jsx(L,{htmlFor:"customPrice",children:"Custom Price (gems) â per class"}),e.jsx(ae,{id:"customPrice",type:"number",placeholder:l!==null?`Standard: ${l} gems per ${a.durationMinutes} min class`:"Leave empty for standard pricing",value:a.customPrice,onChange:i=>h({...a,customPrice:i.target.value}),min:"0","data-testid":"input-custom-price"}),e.jsxs("p",{className:"text-sm text-muted-foreground mt-1",children:["This is the price for ",e.jsx("strong",{children:"one class"}),", not the total.",l!==null?` Leave empty to use the teacher's standard ${l} gems per ${a.durationMinutes} min class.`:" Leave empty to use the teacher's standard pricing."]})]}),e.jsx("div",{className:"rounded-md border bg-muted/40 px-3 py-2 text-sm","data-testid":"text-booking-total",children:a.isFreeClass?e.jsx("span",{className:"font-medium",children:"Free class â no gems will be charged."}):M===null?e.jsx("span",{className:"text-muted-foreground",children:A?"This teacher has no standard price for this duration â enter a custom price per class.":"Pick a teacher to see the total the student will be charged."}):e.jsxs(e.Fragment,{children:[e.jsxs("span",{className:"font-medium",children:[U," ",U===1?"class":"classes"," à ",I," gems ="," ",e.jsx("span",{translate:"no",children:M})," gems"]}),e.jsxs("span",{className:"text-muted-foreground",children:[" ","will be deducted from the student",v?" (custom price)":" (standard price)","."]})]})})]}),e.jsx("div",{className:"flex justify-end gap-2 pt-4",children:e.jsx(P,{type:"submit",disabled:y||m&&w.length===0,"data-testid":"button-submit-booking",children:y?"Booking...":m?`Book ${w.length} Sessions${M!==null?` · ${M} gems`:""}`:`Book Session${M!==null?` · ${M} gems`:""}`})})]})}function Rs({sessionId:t,isOpen:n,onClose:r}){const{data:o,isLoading:y,error:b}=fe({queryKey:["/api/sessions",t,"diagnostics"],queryFn:async()=>{const a=await fetch(`/api/sessions/${t}/diagnostics`,{credentials:"include"});if(!a.ok){const h=await a.json().catch(()=>({}));throw new Error(h.error||"Failed to fetch diagnostics")}return a.json()},enabled:n}),m=a=>{switch(a){case"Excellent":return"text-green-500";case"Good":return"text-yellow-500";case"Poor":return"text-orange-500";case"Lost":return"text-red-500";default:return"text-muted-foreground"}},f=a=>a==null?"N/A":a>1e3?`${(a/1e3).toFixed(1)} Mbps`:`${a} kbps`;return n?e.jsx(oe,{open:n,onOpenChange:r,children:e.jsxs(de,{className:"max-w-2xl max-h-[80vh] overflow-y-auto",onClick:a=>a.stopPropagation(),children:[e.jsxs(ce,{children:[e.jsxs(me,{className:"flex items-center gap-2",children:[e.jsx(Ie,{className:"h-5 w-5"}),"Call Quality Data"]}),e.jsx(ue,{children:"Connection diagnostics recorded during this session"})]}),y&&e.jsx("p",{className:"text-muted-foreground",children:"Loading diagnostics..."}),b&&e.jsx("div",{className:"text-center py-4 text-destructive",children:e.jsxs("p",{children:["Error loading diagnostics: ",b.message]})}),!y&&!b&&(!o||o.length===0)&&e.jsxs("div",{className:"text-center py-8 text-muted-foreground",children:[e.jsx(Ie,{className:"h-8 w-8 mx-auto mb-2 opacity-50"}),e.jsx("p",{children:"No diagnostics data recorded for this session"}),e.jsx("p",{className:"text-xs mt-1",children:"Diagnostics are logged automatically during sessions"})]}),!y&&!b&&o&&o.length>0&&e.jsxs("div",{className:"space-y-4",children:[e.jsxs("div",{className:"text-sm text-muted-foreground mb-4",children:[o.length," diagnostic records foun
26d"]}),e.jsx("div",{className:"space-y-3",children:o.map((a,h)=>e.jsxs(It,{className:"p-3",children:[e.jsxs("div",{className:"flex justify-between items-start mb-2",children:[e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx(is,{className:"h-4 w-4"}),e.jsx("span",{className:"font-medium text-sm",children:a.classNickname||a.username||a.email})]}),e.jsx("span",{className:"text-xs text-muted-foreground",children:a.recordedAt?E(new Date(a.recordedAt),"MMM d, h:mm:ss a"):"N/A"})]}),e.jsxs("div",{className:"grid grid-cols-2 md:grid-cols-4 gap-2 text-xs",children:[e.jsxs("div",{className:"p-2 bg-muted rounded",children:[e.jsx("p",{className:"text-muted-foreground",children:"Quality"}),e.jsxs("p",{className:`font-medium flex items-center gap-1 ${m(a.connectionQuality)}`,children:[a.connectionQuality==="Lost"?e.jsx(js,{className:"h-3 w-3"}):e.jsx(Ns,{className:"h-3 w-3"}),a.connectionQuality||"N/A"]})]}),e.jsxs("div",{className:"p-2 bg-muted rounded",children:[e.jsx("p",{className:"text-muted-foreground",children:"Latency"}),e.jsx("p",{className:"font-medium",children:a.latencyMs!==null?`${a.latencyMs} ms`:"N/A"})]}),e.jsxs("div",{className:"p-2 bg-muted rounded",children:[e.jsx("p",{className:"text-muted-foreground",children:"Packet Loss"}),e.jsx("p",{className:"font-medium",children:a.packetLossPercent!==null?`${a.packetLossPercent}%`:"N/A"})]}),e.jsxs("div",{className:"p-2 bg-muted rounded",children:[e.jsx("p",{className:"text-muted-foreground",children:"Jitter"}),e.jsx("p",{className:"font-medium",children:a.jitterMs!==null?`${a.jitterMs} ms`:"N/A"})]}),e.jsxs("div",{className:"p-2 bg-muted rounded",children:[e.jsx("p",{className:"text-muted-foreground",children:"Upload"}),e.jsx("p",{className:"font-medium",children:f(a.uploadBitrate)})]}),e.jsxs("div",{className:"p-2 bg-muted rounded",children:[e.jsx("p",{className:"text-muted-foreground",children:"Download"}),e.jsx("p",{className:"font-medium",children:f(a.downloadBitrate)})]}),e.jsxs("div",{className:"p-2 bg-muted rounded",children:[e.jsx("p",{className:"text-muted-foreground",children:"Server"}),e.jsx("p",{className:"font-medium text-xs truncate",children:a.serverRegion||"N/A"})]}),e.jsxs("div",{className:"p-2 bg-muted rounded",children:[e.jsx("p",{className:"text-muted-foreground",children:"Timezone"}),e.jsx("p",{className:"font-medium text-xs truncate",children:a.userTimezone||"N/A"})]})]})]},a.id||h))})]})]})}):null}function Us({sessionId:t,currentProvider:n,session:r,isOpen:o,onClose:y}){const{toast:b}=st(),[m,f]=x.useState(n);x.useEffect(()=>{o&&f(n)},[o,n]);const a=(r==null?void 0:r.studentTier)==="paid"||(r==null?void 0:r.studentTier)==="team",h=(r==null?void 0:r.teacherTier)==="paid"||(r==null?void 0:r.teacherTier)==="team",k=m==="classin"&&(!a||!h),d=Q({mutationFn:async w=>W("PATCH",`/api/admin/sessions/${t}`,{videoProvider:w}),onSuccess:async()=>{b({title:"Success",description:"Video provider updated"}),await K.refetchQueries({queryKey:["/api/admin/sessions"]}),y()},onError:w=>{b({title:"Error",description:w.message||"Failed to update provider",variant:"destructive"})}});return e.jsx(oe,{open:o,onOpenChange:y,children:e.jsxs(de,{children:[e.jsxs(ce,{children:[e.jsx(me,{children:"Session Settings"}),e.jsx(ue,{children:"Change the video provider for this session."})]}),e.jsxs("div",{className:"py-4 space-y-4",children:[e.jsxs("div",{children:[e.jsx(L,{children:"Video Provider"}),e.jsxs(Le,{value:m,onValueChange:f,children:[e.jsx(Ee,{children:e.jsx(Oe,{})}),e.jsxs(Re,{children:[e.jsx(R,{value:"livekit",children:"Standard System (LiveKit)"}),e.jsx(R,{value:"tencent_trtc",children:"Global China Bridge (Tencent)"}),e.jsx(R,{value:"agora",children:"Universal System (Agora)"}),e.jsx(R,{value:"classin",children:"ClassIn (VIP China)"})]})]})]}),k&&e.jsxs("div",{className:"p-3 bg-amber-50 dark:bg-amber-900/30 border border-amber-200 dark:border-amber-700 rounded-lg",children:[e.jsx("p",{className:"text-sm font-medium text-amber-800 dark:text-amber-200",children:"â ï¸ VIP Membership Notice"}),e.jsxs("p",{className:"text-xs text-amber-700 dark:text-amber-300 mt-1",children:[!a&&!h?"Both the student and teacher do not have VIP membership.":a?"The teacher does not have VIP membership.":"The student does not have VIP membership."," ","They will see an upgrade prompt when joining the ClassIn session."]})]}),e.jsxs("p",{className:"text-xs text-muted-foreground",children:[e.jsx("strong",{children:"Note:"})," Changing the provider will affect both the teacher and student immediately. If they are currently in the session, they will need to refresh the page."]})]}),e.jsxs(Je,{children:[e.jsx(P,{variant:"outline",onClick:y,children:"Cancel"}),e.jsx(P,{onClick:()=>
26d.mutate(m),disabled:d.isPending,children:d.isPending?"Updating...":"Save Changes"})]})]})})}function le({field:t,label:n,currentField:r,direction:o,onSort:y,className:b}){const m=r===t;return e.jsx(Fe,{className:_("font-heading text-[11px] uppercase tracking-wider cursor-pointer select-none hover:text-foreground",m&&"text-primary",b),onClick:()=>y(t),children:e.jsxs("span",{className:"inline-flex items-center gap-1",children:[n,m?o==="asc"?e.jsx(Ts,{className:"h-3 w-3"}):e.jsx(Ss,{className:"h-3 w-3"}):e.jsx(Ds,{className:"h-3 w-3 opacity-40"})]})})}function qs(t){switch(t){case"tencent_trtc":return"Tencent";case"agora":return"Agora";case"classin":return"ClassIn";default:return"LiveKit"}}const zs={scheduled:{label:"Scheduled",className:"bg-blue-50 text-blue-700 border-blue-200 dark:bg-blue-950 dark:text-blue-300 dark:border-blue-800",dotClass:"bg-blue-500"},on_track:{label:"In Progress",className:"bg-amber-50 text-amber-700 border-amber-200 dark:bg-amber-950 dark:text-amber-300 dark:border-amber-800",dotClass:"bg-amber-500 animate-pulse"},confirmed_complete:{label:"Completed",className:"bg-green-50 text-green-700 border-green-200 dark:bg-green-950 dark:text-green-300 dark:border-green-800",dotClass:"bg-green-500"},cancelled:{label:"Cancelled",className:"bg-red-50 text-red-700 border-red-200 dark:bg-red-950 dark:text-red-300 dark:border-red-800",dotClass:"bg-red-500"}},_s=[{key:"scheduled",label:"scheduled",className:"bg-blue-50 text-blue-700 border-blue-200 dark:bg-blue-950 dark:text-blue-300 dark:border-blue-800"},{key:"on_track",label:"in progress",className:"bg-amber-50 text-amber-700 border-amber-200 dark:bg-amber-950 dark:text-amber-300 dark:border-amber-800"},{key:"confirmed_complete",label:"completed",className:"bg-emerald-50 text-emerald-700 border-emerald-200 dark:bg-emerald-950 dark:text-emerald-300 dark:border-emerald-800"},{key:"cancelled",label:"cancelled",className:"bg-red-50 text-red-700 border-red-200 dark:bg-red-950 dark:text-red-300 dark:border-red-800"},{key:"absent",label:"absent",className:"bg-orange-50 text-orange-700 border-orange-200 dark:bg-orange-950 dar
26k:text-orange-300 dark:border-orange-800"},{key:"rescheduled",label:"rescheduled",className:"bg-gray-50 text-gray-700 border-gray-200 dark:bg-gray-800 dark:text-gray-300 dark:border-gray-700"},{key:"overdue",label:"overdue",className:"bg-gray-50 text-gray-700 border-gray-200 dark:bg-gray-800 dark:text-gray-300 dark:border-gray-700"},{key:"disputed",label:"disputed",className:"bg-gray-50 text-gray-700 border-gray-200 dark:bg-gray-800 dark:text-gray-300 dark:border-gray-700"}];function Vs(t,n){return n==="pending"||n==="confirmed"?{label:"Absent",className:"bg-orange-50 text-orange-700 border-orange-200 dark:bg-orange-950 dark:text-orange-300 dark:border-orange-800",dotClass:"bg-orange-500"}:zs[t]||{label:t,className:"bg-gray-50 text-gray-700 border-gray-200 dark:bg-gray-800 dark:text-gray-300 dark:border-gray-700",dotClass:"bg-gray-500"}}function Qs({session:t,showEmails:n=!0,onDelete:r,onCancel:o,isSelected:y,onToggleSelect:b}){var U;const m=new Date(t.startDatetime),f=new Date,a=m>f,h=Se(m,f),k=Se(m,Be(f,1)),d=["confirmed_complete","cancelled"].includes(t.status)||t.absentStatus==="pending"||t.absentStatus==="confirmed",w=((U=Intl.DateTimeFormat("en-US",{timeZoneName:"short"}).formatToParts(m).find(M=>M.type==="timeZoneName"))==null?void 0:U.value)||"",A=h?`Today, ${E(m,"HH:mm")}`:k?`Tomorrow, ${E(m,"HH:mm")}`:E(m,"MMM d, HH:mm"),l=`${E(m,"EEE MMM d")} ${w}`,T=Vs(t.status,t.absentStatus),v=Es(t),I=()=>{window.open(`/session/${t.id}`,"_blank")};return e.jsxs(Bt,{"data-state":y?"selected":void 0,className:_("cursor-pointer",d&&"opacity-70"),onClick:I,children:[e.jsx(G,{onClick:M=>M.stopPropagation(),children:b?e.jsx($e,{checked:!!y,onCheckedChange:()=>b(t.id),"aria-label":"Select session"}):e.jsx($e,{disabled:!0,className:"opacity-30"})}),e.jsxs(G,{children:[e.jsx("span",{className:"font-medium text-sm",children:C(t.student||{},n)}),(t.studentTier==="paid"||t.studentTier==="team")&&e.jsx("span",{className:"ml-1.5 inline-flex items-center px-1.5 py-0 rounded text-[9px] font-bold uppercase bg-amber-100 text-amber-700 dark:bg-amber-900/40 dark:text-amber-400 leading-4",children:"VIP"})]}),e.jsx(G,{children:e.jsx("span",{className:"font-medium text-sm",children:C(t.teacher||{},n)})}),e.jsxs(G,{children:[e.jsx("div",{className:_("text-sm",a?"font-semibold":"text-muted-foreground"),children:A}),e.jsx("div",{className:"text-[11px] text-muted-foreground",children:l}),v&&e.jsxs("span",{className:"mt-0.5 inline-flex items-center px-1.5 py-0 rounded text-[9px] font-bold uppercase bg-orange-100 text-orange-700 dark:bg-orange-900/40 dark:text-orange-400 leading-4",title:`This class was taught about ${v.hoursPast.toFixed(1)} hours after its booked time. Attendance was still credited.`,"data-testid":`badge-off-schedule-${t.id}`,children:["Taught ",v.hoursPast<1?"<1":v.hoursPast.toFixed(0),"h late"]})]}),e.jsxs(G,{className:"hidden md:table-cell text-muted-foreground text-sm",children:[t.durationMinutes," min"]}),e.jsx(G,{className:"hidden md:table-cell",children:e.jsxs("span",{className:"inline-flex items-center gap-1 text-sm text-muted-foreground",children:[e.jsx(Ye,{className:"h-3.5 w-3.5 text-purple-500"}),t.gemCost||0]})}),e.jsx(G,{children:e.jsxs("span",{className:_("inline-flex items-center gap-1.5 px-2 py-0.5 rounded-full text-[11px] font-semibold border",T.className),children:[e.jsx("span",{className:_("h-1.5 w-1.5 rounded-full shrink-0",T.dotClass)}),T.label]})}),e.jsx(G,{children:e.jsx("span",{className:"text-[11px] font-medium px-2 py-0.5 rounded border border-border bg-muted/50 text-muted-foreground",children:qs(t.videoProvider)})}),e.jsx(G,{onClick:M=>M.stopPropagation(),children:e.jsx(Ks,{session:t,showEmails:n,onDelete:r,onCancel:o})})]})}function Ks({session:t,showEmails:n=!0,onDelete:r,onCancel:o}){const[y,b]=x.useState(!1),[m,f]=x.useState(!1),[a,h]=x.useState(!1),[k,d]=x.useState(!1),[w,A]=x.useState(!1),[l,T]=x.useState(!1),[v,I]=x.useState(""),[U,M]=x.useState(""),[he,i]=x.useState(!1),[g,D]=x.useState(""),{toast:N}=st(),X=new Date(t.startDatetime),{user:q}=At(),Ue=(q==null?void 0:q.isSuperAdmin)===!0?30:7,Y=(Date.now()-X.getTime())/(1e3*60*60*24),ne=t.status!=="c
26ancelled"&&!t.absentStatus&&Y<=Ue&&Y>=0,{data:F}=fe({queryKey:["/api/cancellation-policies"],enabled:w,select:p=>p==null?void 0:p.find(V=>V.timeWindowKey==="no_show")}),H=(()=>{if(!v||!(t.gemCost>0))return null;const p=(F==null?void 0:F.studentCancelsTeacherCompensationPercent)??50,V=(F==null?void 0:F.teacherCancelsStudentRefundPercent)??100,J=v==="student",pe=J?p:V;return{gems:Math.round(t.gemCost*pe/100*100)/100,percent:pe,kind:J?"compensate":"refund",recipient:J?t.teacher:t.student}})(),xe=new Date(t.endDatetime||new Date(new Date(t.startDatetime).getTime()+(t.durationMinutes||0)*6e4)),Te=(t.status==="scheduled"||t.status==="on_track")&&xe<new Date,re=Q({mutationFn:async()=>(await W("POST",`/api/admin/sessions/${t.id}/mark-absent-retroactive`,{absentParty:v,reason:U.trim()})).json(),onSuccess:()=>{A(!1),I(""),M(""),N({title:"Marked Absent",description:`${v==="teacher"?"Teacher":"Student"} marked absent. Penalties applied.`}),K.invalidateQueries({queryKey:["/api/admin/sessions"]})},onError:p=>{N({variant:"destructive",title:"Failed",description:p.message||"Failed to mark absent"})}}),ge=Q({mutationFn:async()=>(await W("POST",`/api/admin/sessions/${t.id}/admin-resolve`,{reason:g.trim()})).json(),onSuccess:p=>{var V,J;i(!1),D(""),N({title:"Session Resolved",description:(V=p.payment)!=null&&V.success?"Session resolved and payment processed.":`Session resolved. ${((J=p.payment)==null?void 0:J.message)||""}`}),K.invalidateQueries({queryKey:["/api/admin/sessions"]})},onError:p=>{N({variant:"destructive",title:"Failed to Resolve",description:p.message||"Failed to resolve session"})}}),Me=Q({mutationFn:async p=>(await W("POST",`/api/admin/sessions/${t.id}/record-flag`,{record:p})).json(),onSuccess:p=>{N({title:"Recording Updated",description:(p==null?void 0:p.message)||""}),K.invalidateQueries({queryKey:["/api/admin/sessions"]})},onError:p=>{N({variant:"destructive",title:"Failed",description:p.message||"Failed to update recording flag"})}});return e.jsxs(e.Fragment,{children:[e.jsxs(es,{children:[e.jsx(ts,{asChild:!0,children:e.jsx(P,{variant:"ghost",size:"icon",className:"h-8 w-8",children:e.jsx(Ms,{className:"h-4 w-4"})})}),e.jsxs(ss,{align:"end",className:"w-[220px]",children:[e.jsxs(z,{onClick:()=>window.open(`/session/${t.id}`,"_blank"),children:[e.jsx(as,{className:"h-4 w-4"}),"View / Open Session"]}),e.jsxs(z,{onClick:()=>d(!0),children:[e.jsx(ns,{className:"h-4 w-4"}),"Change Video Provider"]}),e.jsxs(z,{onClick:()=>f(!0),children:[e.jsx(Ie,{className:"h-4 w-4"}),"View Call Quality"]}),e.jsxs(z,{onClick:()=>window.open(`/admin/sessions/${t.id}/post-mortem`,"_blank"),children:[e.jsx(ks,{className:"h-4 w-4"}),"Post-mortem"]}),ne&&e.jsxs(e.Fragment,{children:[e.jsx(ie,{}),e.jsxs(z,{onClick:()=>A(!0),children:[e.jsx(ws,{className:"h-4 w-4"}),"Mark Absent"]})]}),Te&&e.jsxs(e.Fragment,{children:[e.jsx(ie,{}),e.jsxs(z,{onClick:()=>i(!0),children:[e.jsx(rs,{className:"h-4 w-4"}),"Resolve Session"]})]}),t.status==="scheduled"&&e.jsxs(e.Fragment,{children:[e.jsx(ie,{}),t.videoProvider==="livekit"||t.recordRequested?e.jsxs(z,{onClick:()=>Me.mutate(!t.recordRequested),disabled:Me.isPending,"data-testid":"button-record-flag",children:[e.jsx(Ge,{className:_("h-4 w-4",t.recordRequested&&"text-red-500")}),t.recordRequested?"Recording: ON (click to cancel)":"Record this class"]}):e.jsxs(z,{disabled:!0,"data-testid":"button-record-flag-unsupported",children:[e.jsx(Ge,{className:"h-4 w-4"}),"Recording (LiveKit only for now)"]})]}),t.recordingStatus==="saved"&&e.jsxs(e.Fragment,{children:[e.jsx(ie,{}),e.jsxs(z,{onClick:()=>h(!0),"data-testid":"button-view-recording",children:[e.jsx(Ge,{className:"h-4 w-4 text-emerald-600"}),"View Recording"]})]}),o&&["scheduled","on_track"].includes(t.status)&&!t.absentStatus&&e.jsxs(e.Fragment,{children:[e.jsx(ie,{}),e.jsxs(z,{onClick:()=>b(!0),"data-testid":"button-cancel-session",children:[e.jsx($t,{className:"h-4 w-4"}),"Cancel Session"]})]}),r&&e.jsxs(e.Fragment,{children:[e.jsx(ie,{}),Ut(t.status)?e.jsxs(z,{className:"text-destructive focus:text-destructive",onClick:()=>T(!0),"data-testid":"button-delete-session",children:[e.jsx(tt,{className:"h-4 w-4"}),"Delete Session"]}):e.jsxs(z,{disabled:!0,"data-testid":"button-delete-session-blocked",children:[e.jsx(tt,{className:"h-4 w-4"}),"Delete (cancel this class first)"]})]})]})]}),e.jsx(oe,{open:a,onOpenChange:p=>{p||h(!1)},children:e.jsxs(de,{className:"max-w-3xl",children:[e.jsxs(ce,{children:[e.jsx(me,{children:"Session Recording"}),e.jsxs(ue,{children:[C(t.teacher,!1)," â ",C(t.student,!1)," · ",E(X,"PPp")]})]}),a&&e.jsx(fs,{sessionId:t.id,isAdmin:!0})]})}),e.jsx(Rs,{sessionId:t.id,isOpen:m,onClose:()=>f(!1)}),e.jsx(Us,{sessionId:t.id,currentProvider:t.videoProvider||"livekit",session:t,isOpen:k,onClose:()=>d(!1)}),e.jsx(oe,{open:w,onOpenChange:p=>{p||(A(!1),I(""),M(""))},children:e.jsxs(de,{children:[e.jsxs(ce,{children:[e.jsx(me,{children:"Retroactive Mark Absent"}),e.jsx(ue,{children:"Mark a party absent for this past session. This will immediately apply no-show penalties (heart deduction + gem settlement per policy) and cancel the session."})]}),e.jsxs("div",{className:"space-y-4 py-2",children:[e.jsxs("div",{className:"space-y-2",children:[e.jsx(L,{children:"Who w
26as absent?"}),e.jsxs(Le,{value:v,onValueChange:I,children:[e.jsx(Ee,{children:e.jsx(Oe,{placeholder:"Select absent party..."})}),e.jsxs(Re,{children:[e.jsxs(R,{value:"student",children:["Student: ",C(t.student,!1)]}),e.jsxs(R,{value:"teacher",children:["Teacher: ",C(t.teacher,!1)]})]})]})]}),e.jsxs("div",{className:"space-y-2",children:[e.jsx(L,{children:"Reason (min 10 characters)"}),e.jsx(Nt,{value:U,onChange:p=>M(p.target.value),placeholder:"e.g. Student did not attend the scheduled session...",rows:3})]}),v&&e.jsxs("div",{className:"rounded-md bg-red-50 dark:bg-red-900/20 p-3 text-sm text-red-800 dark:text-red-200",children:[e.jsx("strong",{children:"This will:"}),e.jsxs("ul",{className:"list-disc list-inside mt-1 space-y-0.5",children:[e.jsxs("li",{children:["Deduct hearts from ",C(v==="teacher"?t.teacher:t.student,!1)]}),t.gemCost>0&&H&&e.jsxs("li",{children:[H.kind==="compensate"?"Compensate":"Refund"," ",H.gems," gems to ",C(H.recipient,!1)," (",H.percent,"% no-show ",H.kind==="compensate"?"rate":"refund",")"]}),e.jsx("li",{children:"Cancel this session"})]})]})]}),e.jsxs(Je,{children:[e.jsx(P,{variant:"outline",onClick:()=>A(!1),children:"Cancel"}),e.jsx(P,{variant:"destructive",onClick:()=>re.mutate(),disabled:!v||U.trim().length<10||re.isPending,children:re.isPending?"Applying...":"Mark Absent & Apply Penalties"})]})]})}),e.jsx(oe,{open:he,onOpenChange:p=>{p||(i(!1),D(""))},children:e.jsxs(de,{children:[e.jsxs(ce,{children:[e.jsx(me,{children:"Admin Resolve Session"}),e.jsx(ue,{children:"Manually complete a session that took place on an external platform. This bypasses the normal two-party confirmation flow."})]}),e.jsxs("div",{className:"space-y-4 py-2",children:[e.jsxs("div",{className:"rounded-md bg-muted p-3 text-sm space-y-1",children:[e.jsxs("div",{children:[e.jsx("strong",{children:"Student:"})," ",C(t.student,!1)]}),e.jsxs("div",{children:[e.jsx("strong",{children:"Teacher:"})," ",C(t.teacher,!1)]}),e.jsxs("div",{children:[e.jsx("strong",{children:"Date:"})," ",E(new Date(t.startDatetime),"PPp")]}),e.jsxs("div",{children:[e.jsx("strong",{children:"Provider:"})," ",t.videoProvider||"Unknown"]}),(t.gemCost||0)>0&&e.jsxs("div",{children:[e.jsx("strong",{children:"Cost:"})," ",t.gemCost," gems"]})]}),e.jsxs("div",{className:"rounded-md bg-blue-50 dark:bg-blue-950/40 p-3 text-sm text-blue-800 dark:text-blue-200",children:[e.jsx("strong",{children:"When to use this:"}),e.jsxs("ul",{className:"list-disc list-inside mt-1 space-y-0.5",children:[e.jsx("li",{children:"Class happened on an external platform (ClassIn, Zoom, etc.)"}),e.jsx("li",{children:"Neither party confirmed through our platform"}),e.jsx("li",{children:"You have verified the class took place"})]})]}),e.jsxs("div",{className:"space-y-2",children:[e.jsx(L,{children:"Reason (min 20 characters)"}),e.jsx(Nt,{value:g,onChange:p=>D(p.target.value),placeholder:"e.g. Verified via ClassIn attendance log that both teacher and student attended the full 25-minute session on March 17...",rows:4}),e.jsxs("p",{className:"text-xs text-muted-foreground",children:[g.trim().length,"/20 characters minimum"]})]}),e.jsxs("div",{className:"rounded-md bg-amber-50 dark:bg-amber-950/40 p-3 text-sm text-amber-800 dark:text-amber-200",children:[e.jsx("strong",{children:"This will:"}),e.jsxs("ul",{className:"list-disc list-inside mt-1 space-y-0.5",children:[e.jsx("li",{children:"Mark the session as complete"}),e.jsx("li",{children:"Set both teacher and student as confirmed"}),(t.gemCost||0)>0&&e.jsxs("li",{children:["Release ",t.gemCost," gems to ",C(t.teacher,!1)]}),e.jsx("li",{children:"Create a permanent audit trail entry"})]})]})]}),e.jsxs(Je,{children:[e.jsx(P,{variant:"outline",onClick:()=>i(!1),children:"Cancel"}),e.jsx(P,{onClick:()=>ge.mutate(),disabled:g.trim().length<20||ge.isPending,children:ge.isPending?"Resolving...":"Resolve & Release Payment"})]})]})}),e.jsx(je,{open:y,onOpenChange:b,children:e.jsxs(be,{children:[e.jsxs(ye,{children:[e.jsx(ve,{children:"Cancel this session?"}),e.jsxs(ke,{children:[C(t.student,!1)," gets a full refund of ",t.gemCost||0," gems, with no cancellation penalty and no effect on hearts or reliability. Both the student and teacher are notified by email and in the app."]})]}),e.jsxs(we,{children:[e.jsx(Ne,{children:"Go back"}),e.jsx(Ce,{onClick:()=>o==null?void 0:o(t.id),"data-testid":"button-confirm-cancel-session",children:"Cancel session"})]})]})}),e.jsx(je,{open:l,onOpenChange:T,children:e.jsxs(be,{children:[e.jsxs(ye,{children:[e.jsx(ve,{children:"Delete Session"}),e.jsx(ke,{children:"Are you sure you want to delete this session? This will remove it from all views but keep the data for auditing purposes."})]}),e.jsxs(we,{children:[e.jsx(Ne,{children:"Cancel"}),e.jsx(Ce,{onClick:()=>r==null?void 0:r(t.id),className:"bg-destructive text-destructive-foreground hover:bg-destructive/90",children:"Delete"})]})]})})]})}export{Sa as default};
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.