1import{h as Ee,n as Ze,Q as et,r as n,u as he,j as e,a5 as ot,ag as ct,B as j,I as g,x as ee,y as te,z as se,D as ae,E as p,O as Ie,l as tt,ah as re,a as qe,b as $e,ao as De,L as Be,F as dt}from"./index-D2rUcUpe.js";import{u as mt}from"./useMutation-N7h9ZAvw.js";import{A as ut,C as ht}from"./AdminLayout-kkTddtqD.js";import{C as ue,c as Se}from"./card-Bto263fv.js";import{L as u}from"./label-FpK1FRAL.js";import{C as ze}from"./checkbox-BmaTvz5L.js";import{L as xt}from"./LeadSourceSelect-DKYTSQ9c.js";import{D as pt,a as gt,d as ft,b as jt,c as yt}from"./dialog-6d7Y_iu1.js";import{T as bt,a as vt,b as Re,c as Ye}from"./tabs-bmQNBKsk.js";import{C as Nt}from"./CustomerPicker-B2OUsR0E.js";import{U as wt}from"./user-plus-DMuvwpEs.js";import{L as Ve}from"./loader-circle-BLb9ftDd.js";import{T as Ct}from"./triangle-alert-CRmme1oj.js";import{C as St}from"./calendar-days-BLrdobId.js";import{C as kt}from"./clipboard-check-BvFIUhXn.js";import{L as Dt}from"./list-T6zTH3Tv.js";import{C as Ft}from"./chevron-left-CPCO6D92.js";import{C as At}from"./copy-ClT1kzOb.js";import"./Helmet-D7MYuSoa.js";import"./log-out-Bsz1vMOl.js";import"./clipboard-list-COd5Au4H.js";import"./tag-s72l0Uly.js";import"./building-2-DpPm7j8z.js";import"./star-Dj3WDnZ7.js";import"./upload-B90ZdJYW.js";import"./share-2-BMS9JZH2.js";import"./wrench-CCizyO6G.js";import"./index-CirNZHYS.js";import"./user-DWnRSMVP.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 Tt=Ee("CalendarRange",[["rect",{width:"18",height:"18",x:"3",y:"4",rx:"2",key:"1hopcy"}],["path",{d:"M16 2v4",key:"4m81vk"}],["path",{d:"M3 10h18",key:"8toen8"}],["path",{d:"M8 2v4",key:"1cmpym"}],["path",{d:"M17 14h-6",key:"bkmgh3"}],["path",{d:"M13 18H7",key:"bb0bb7"}],["path",{d:"M7 14h.01",key:"1qa3f1"}],["path",{d:"M17 18h.01",key:"1bdyru"}]]);/** 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 Lt=Ee("UserX",[["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:"17",x2:"22",y1:"8",y2:"13",key:"3nzzx3"}],["line",{x1:"22",x2:"17",y1:"8",y2:"13",key:"1swrse"}]]);/** 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 Mt=Ee("Users",[["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"}],["path",{d:"M22 21v-2a4 4 0 0 0-3-3.87",key:"kshegd"}],["path",{d:"M16 3.13a4 4 0 0 1 0 7.75",key:"1da9ce"}]]),ke="__manual";function It({open:r,onOpenChange:i,fitters:l,existingJobQuoteIds:M,initialCustomer:x}){const[,ne]=Ze(),{toast:I}=et(),[ge,C]=n.useState(""),[fe,y]=n.useState(""),[je,v]=n.useState(""),[ie,N]=n.useState(""),[ye,S]=n.useState(""),[Fe,F]=n.useState(!1),[B,R]=n.useState(""),[le,A]=n.useState(""),[oe,T]=n.useState(""),[ce,w]=n.useState(""),[Y,be]=n.useState(""),[P,ve]=n.useState(""),[$,V]=n.useState(""),[G,E]=n.useState(""),[K,Ne]=n.useState(ke),[k,f]=n.useState(""),[J,de]=n.useState(""),[O,_]=n.useState(!1),Ae=(Number(P)||0)*(Number($)||0)+(Number(G)||0),Te=a=>a.toLocaleString("en-GB",{style:"currency",currency:"GBP"}),[H,me]=n.useState(""),Q=he({queryKey:["/api/admin/quotes"],enabled:r});n.useEffect(()=>{r&&x&&(C(x.customerName||""),y(x.phone||""),v(x.email||""),N(x.address||""),S(x.postcode||""))},[r,x]);const we=a=>{C(a.customerName||""),y(a.phone||""),v(a.email||""),N(a.address||""),S(a.postcode||"")},X=()=>{C(""),y(""),v(""),N(""),S(""),F(!1),R(""),A(""),T(""),w(""),be(""),ve(""),V(""),E(""),Ne(ke),f(""),de(""),me("")},W=()=>{i(!1),setTimeout(X,200)},Le=async()=>{const a=ge.trim();if(!a){I({title:"Add a customer name",description:"A job needs a customer name to get started.",variant:"destructive"});return}if(k&&J&&J<k){I({title:"Check the dates",description:"The end date canât be before the start date.",variant:"destructive"});return}const D=K!==ke?l.find(h=>String(h.id)===K):void 0;_(!0);try{const h={customerName:a,phone:fe.trim()||void 0,email:je.trim()||void 0,address:ie.trim()||void 0,postcode:ye.trim()||void 0,generalArea:B.trim()||null,workSummary:le.trim()||null,leadSource:oe||null,workDescription:ce.trim()||null,collectionList:Y.trim()||null,payRate:P===""?null:Number(P),payArea:$===""?null:Number($),payFixed:G===""?null:Number(G),scheduledDate:k||null,scheduledEndDate:J||null};D&&(h.fitterId=D.id,h.fitterName=D.name,h.fitterPhone=D.phone??null,h.fitterEmail=D.email??null);const b=await $e("/api/admin/job-sheets",{method:"POST",data:h}),U=b==null?void 0:b.quoteId;await De.invalidateQueries({predicate:z=>typeof z.queryKey[0]=="string"&&z.queryKey[0].startsWith("/api/admin/job-sheets")}),await De.invalidateQueries({queryKey:["/api/admin/quotes"]}),I({title:"Job created",description:"Finish off the details on the job sheet."}),W(),U&&ne(`/admin/quotes/${U}/job-sheet`)}catch(h){I({title:"Could not create the job",description:(h==null?void 0:h.message)||"Please try again.",variant:"destructive"})}finally{_(!1)}},L=a=>{W(),ne(`/admin/quotes/${a}/job-sheet`)},Z=n.useMemo(()=>{const a=H.trim().toLowerCase(),h=[...Q.data??[]].sort((b,U)=>U.id-b.id);return a?h.filter(b=>[b.customerName,b.postcode,b.address,`#${b.id}
16`].filter(Boolean).join(" ").toLowerCase().includes(a)).slice(0,50):h.slice(0,50)},[Q.data,H]);return e.jsxs(pt,{open:r,onOpenChange:a=>a?i(!0):W(),children:[e.jsxs(gt,{className:"max-w-lg max-h-[90vh] overflow-y-auto",children:[e.jsxs(ft,{children:[e.jsx(jt,{children:"Add a job sheet"}),e.jsx(yt,{children:"Start a brand-new job, or build a job sheet from an existing customer quote."})]}),e.jsxs(bt,{defaultValue:"quick",className:"mt-2",children:[e.jsxs(vt,{className:"grid w-full grid-cols-2",children:[e.jsxs(Re,{value:"quick",className:"flex items-center gap-1.5",children:[e.jsx(ot,{className:"h-4 w-4"})," Quick new job"]}),e.jsxs(Re,{value:"quote",className:"flex items-center gap-1.5",children:[e.jsx(ct,{className:"h-4 w-4"})," From a quote"]})]}),e.jsxs(Ye,{value:"quick",className:"space-y-3 pt-3",children:[e.jsx("div",{className:"flex justify-end",children:e.jsxs(j,{type:"button",variant:"outline",size:"sm",onClick:()=>F(!0),children:[e.jsx(wt,{className:"h-4 w-4 mr-1.5"})," Load existing customer"]})}),e.jsxs("div",{children:[e.jsxs(u,{htmlFor:"njs-customer",children:["Customer name ",e.jsx("span",{className:"text-red-500",children:"*"})]}),e.jsx(g,{id:"njs-customer",value:ge,onChange:a=>C(a.target.value),placeholder:"e.g. Jane Smith",autoFocus:!0})]}),e.jsxs("div",{className:"grid grid-cols-2 gap-3",children:[e.jsxs("div",{children:[e.jsx(u,{htmlFor:"njs-phone",children:"Phone"}),e.jsx(g,{id:"njs-phone",value:fe,onChange:a=>y(a.target.value),placeholder:"07â¦"})]}),e.jsxs("div",{children:[e.jsx(u,{htmlFor:"njs-postcode",children:"Postcode"}),e.jsx(g,{id:"njs-postcode",value:ye,onChange:a=>S(a.target.value),placeholder:"WA7â¦"})]})]}),e.jsxs("div",{children:[e.jsx(u,{htmlFor:"njs-email",children:"Email"}),e.jsx(g,{id:"njs-email",value:je,onChange:a=>v(a.target.value),placeholder:"[email protected]"})]}),e.jsxs("div",{children:[e.jsx(u,{htmlFor:"njs-address",children:"Address"}),e.jsx(g,{id:"njs-address",value:ie,onChange:a=>N(a.target.value),placeholder:"House & street"})]}),e.jsxs("div",{children:[e.jsx(u,{htmlFor:"njs-area",children:"Area"}),e.jsx(g,{id:"njs-area",value:B,onChange:a=>R(a.target.value),placeholder:"e.g. Runcorn, WA7"})]}),e.jsxs("div",{children:[e.jsx(u,{htmlFor:"njs-fitter",children:"Fitter"}),e.jsxs(ee,{value:K,onValueChange:Ne,children:[e.jsx(te,{id:"njs-fitter",children:e.jsx(se,{placeholder:"No fitter yet"})}),e.jsxs(ae,{children:[e.jsx(p,{value:ke,children:"No fitter yet"}),l.map(a=>e.jsx(p,{value:String(a.id),children:a.name},a.id))]})]})]}),e.jsxs("div",{className:"grid grid-cols-2 gap-3",children:[e.jsxs("div",{children:[e.jsx(u,{htmlFor:"njs-from",children:"Start date"}),e.jsx(g,{id:"njs-from",type:"date",value:k,max:J||void 0,onChange:a=>f(a.target.value)})]}),e.jsxs("div",{children:[e.jsx(u,{htmlFor:"njs-to",children:"End date"}),e.jsx(g,{id:"njs-to",type:"date",value:J,min:k||void 0,onChange:a=>de(a.target.value)})]})]}),e.jsxs("div",{children:[e.jsx(u,{children:"Lead source"}),e.jsx(xt,{testId:"select-new-job-lead-source",value:oe,onChange:a=>T(a??"")})]}),e.jsxs("div",{children:[e.jsx(u,{htmlFor:"njs-summary",children:"Floor / work summary"}),e.jsx(Ie,{id:"njs-summary",value:le,onChange:a=>A(a.target.value),placeholder:"e.g. 40m² LVT glue-down herringbone",rows:2})]}),e.jsxs("div",{children:[e.jsxs(u,{htmlFor:"njs-detail",children:["Work to do & prep ",e.jsx("span",{className:"text-gray-400 font-normal",children:"(full detail)"})]}),e.jsx(Ie,{id:"njs-detail",value:ce,onChange:a=>w(a.target.value),placeholder:"e.g. Lift existing carpet, level subfloor, apply DPM, fit LVT herringbone in lounge + hall.",rows:3})]}),e.jsxs("div",{children:[e.jsx(u,{htmlFor:"njs-collect",children:"Collect / materials list"}),e.jsx(Ie,{id:"njs-collect",value:Y,onChange:a=>be(a.target.value),placeholder:`One item per line, e.g. 17Adhesive Ã4 18Self-level compound Ã3`,rows:2})]}),e.jsxs("div",{className:"rounded-lg border border-gray-200 p-3 space-y-3",children:[e.jsx("div",{className:"text-sm font-medium",children:"Fitter pay"}),e.jsxs("div",{className:"grid grid-cols-3 gap-2",children:[e.jsxs("div",{children:[e.jsx(u,{htmlFor:"njs-rate",className:"text-xs",children:"Per m² (£)"}),e.jsx(g,{id:"njs-rate",type:"number",step:"0.01",min:"0",value:P,onChange:a=>ve(a.target.value),placeholder:"0.00"})]}),e.jsxs("div",{children:[e.jsx(u,{htmlFor:"njs-pay-area",className:"text-xs",children:"Area (m²)"}),e.jsx(g,{id:"njs-pay-area",type:"number",step:"0.01",min:"0",value:$,onChange:a=>V(a.target.value),placeholder:"0"})]}),e.jsxs("div",{children:[e.jsx(u,{htmlFor:"njs-fixed",className:"text-xs",children:"Fixed (£)"}),e.jsx(g,{id:"njs-fixed",type:"number",step:"0.01",min:"0",value:G,onChange:a=>E(a.target.value),placeholder:"0.00"})]})]}),e.jsxs("div",{className:"flex justify-between text-sm font-semibold border-t pt-2",children:[e.jsx("span",{children:"Total fitter pay"}),e.jsx("span",{children:Te(Ae)})]})]}),e.jsxs("div",{className:"flex justify-end gap-2 pt-1",children:[e.jsx(j,{variant:"outline",onClick:W,disabled:O,children:"Cancel"}),e.jsxs(j,{onClick:Le,disabled:O,children:[O&&e.jsx(Ve,{className:"h-4 w-4 mr-1.5 animate-spin"}),"Create job"]})]})]}),e.jsxs(Ye,{value:"quote",className:"space-y-3 pt-3",children:[e.jsxs("div",{className:"relative",children:[e.jsx(tt,{className:"h-4 w-4 absolute left-2.5 top-1/2 -translate-y-1/2 text-gray-400"}),e.jsx(g,{className:"pl-8",placeholder:"Search customer, postcode, #idâ¦",value:H,onChange:a=>me(a.target.value)})]}),e.jsx("div",{className:"max-h-[320px] overflow-y-auto rounded-lg border border-gray-200 divide-y",children:Q.isLoading?e.jsxs("div",{className:"py-8 text-center text-sm text-gray-500",children:[e.jsx(Ve,{className:"h-4 w-4 mx-auto mb-1.5 animate-spin"})," Loading quotesâ¦"]}):Z.length===0?e.jsx("div",{className:"py-8 text-center text-sm text-gray-500",children:"No quotes match your search."}):Z.map(a=>{const D=M.has(a.id);return e.jsxs("button",{type:"button",onClick:()=>L(a.id),className:"w-full flex items-center gap-2 px-3 py-2.5 text-left hover:bg-primary/5",children:[e.jsxs("div",{className:"min-w-0 flex-1",children:[e.jsx("div",{className:"font-medium text-gray-900 truncate",children:a.customerName||`Quote #${a.id}`}),e.jsxs("div",{className:"text-xs text-gray-500 truncate",children:["#",a.id,a.postcode?` · ${a.postcode}`:""]})]}),D&&e.jsx(re,{variant:"secondary",className:"bg-[#BCCDB9] text-stone-900 shrink-0",children:"Has job sheet"}),e.jsx(qe,{className:"h-4 w-4 text-gray-400 shrink-0"})]},a.id)})}),e.jsx("p",{className:"text-xs text-gray-500",children:"Choosing a quote opens its job sheet â weâll create one if it doesnât exist yet."})]})]})]}),e.jsx(Nt,{open:Fe,onOpenChange:F,onSelect:we})]})}function Bt(r){return new Intl.NumberFormat("en-GB",{style:"currency",currency:"GBP"}).format(r)}function Ge(r){return r.balancePaymentStatus==="unpaid"&&(r.balanceAmount??0)>0}function q(r){return r.completedAt||r.status==="done"?"completed":r.acceptedAt||r.status==="accepted"?"booked":"provisional"}const Pt={provisional:"bg-amber-100 text-amber-900",booked:"bg-[#BCCDB9] text-stone-900",completed:"bg-stone-100 text-stone-600"},qt={provisional:"bg-amber-100 text-amber-900 border border-amber-300",booked:"bg-[#BCCDB9] text-stone-900 border border-[#9bb097]",completed:"bg-stone-100 text-stone-500 border border-stone-300 line-through"},Ke={provisional:"Awaiting fitter",booked:"Booked in",completed:"Completed"};function Pe(r){return new Date(r.getFullYear(),r.getMonth(),1)}function pe(r,i){const l=new Date(r);return l.setDate(l.getDate()+i),l}function st(r,i){return r.getFullYear()===i.getFullYear()&&r.getMonth()===i.getMonth()&&r.getDate()===i.getDate()}function _e(r){const i=l=>String(l).padStart(2,"0");return`${r.getFullYear()}-${i(r.getMonth()+1)}-${i(r.getDate())}`}function He(r){const i=new Date(r);return new Date(i.getUTCFullYear(),i.getUTCMonth(),i.getUTCDate())}function $t(){const r=new Date;return new Date(r.getFullYear(),r.getMonth(),r.getDate())}function Et(r){const i=(r.getDay()+6)%7,l=pe(new Date(r.getFullYear(),r.getMonth(),r.getDate()),-i),M=pe(l,6);return{start:l,end:M}}function xe(r){if(!r.scheduledDate)return null;const i=He(r.scheduledDate),l=r.scheduledEndDate?He(r.scheduledEndDate):i;return{start:i,end:l.getTime()>=i.getTime()?l:i}}function Xe(r){if(!r.scheduledDate)return"No date yet";const i=M=>M.toLocaleDateString("en-GB",{day:"numeric",month:"short",year:"numeric"}),l=xe(r);return st(l.start,l.end)?i(l.start):`${i(l.start)} â ${i(l.end)}`}function Jt(){var Qe,We;
18const r=he({queryKey:["/api/admin/lead-sources"]}),[,i]=Ze(),[l,M]=n.useState("list"),[x,ne]=n.useState(null),[I,ge]=n.useState(""),[C,fe]=n.useState(""),[y,je]=n.useState("all"),[v,ie]=n.useState("all"),[N,ye]=n.useState("all"),[S,Fe]=n.useState("all"),[F,B]=n.useState(new Set),[R,le]=n.useState(""),[A,oe]=n.useState(""),[T,ce]=n.useState(""),[w,Y]=n.useState(()=>Pe(new Date)),[be,P]=n.useState(!1),[ve,$]=n.useState(null),{toast:V}=et(),G=async t=>{try{const s=await $e(`/api/admin/quotes/${t.quoteId}`,{method:"GET"});$({customerName:(s==null?void 0:s.customerName)||t.customerName||"",email:(s==null?void 0:s.email)??t.customerEmail??null,phone:(s==null?void 0:s.phone)??null,address:(s==null?void 0:s.address)??null,postcode:(s==null?void 0:s.postcode)??null}),P(!0)}catch(s){V({title:"Could not duplicate",description:(s==null?void 0:s.message)||"Please try again.",variant:"destructive"})}};n.useEffect(()=>{const t=setTimeout(()=>fe(I.trim()),300);return()=>clearTimeout(t)},[I]);const E=n.useMemo(()=>{const t=new URLSearchParams;y!=="all"&&t.set("fitterId",y),v!=="all"&&t.set("status",v),A&&t.set("from",A),T&&t.set("to",T),C&&t.set("q",C);const s=t.toString();return`/api/admin/job-sheets${s?`?${s}`:""}`},[y,v,A,T,C]),K=he({queryKey:[E],refetchOnMount:"always",refetchInterval:E==="/api/admin/job-sheets"?6e4:!1,refetchIntervalInBackground:!1}),Ne=he({queryKey:["/api/admin/job-sheets"],enabled:E!=="/api/admin/job-sheets",refetchOnMount:"always",refetchInterval:6e4,refetchIntervalInBackground:!1}),k=K.data??[],f=E==="/api/admin/job-sheets"?k:Ne.data??[],J=K.isLoading,{data:de=[]}=he({queryKey:["/api/admin/fitters"]}),O=mt({mutationFn:()=>$e("/api/admin/job-sheets/lead-source",{method:"PATCH",data:{quoteIds:Array.from(F),leadSource:R}}),onSuccess:async t=>{await De.invalidateQueries({predicate:s=>String(s.queryKey[0]).startsWith("/api/admin/job-sheets")}),await De.invalidateQueries({predicate:s=>String(s.queryKey[0]).startsWith("/api/admin/reports/management")}),B(new Set),le(""),V({title:"Lead sources updated",description:`${t.updated} job${t.updated===1?"":"s"} updated.`})},onError:t=>{V({title:"Could not update lead sources",description:(t==null?void 0:t.message)||"Please try again.",variant:"destructive"})}}),_=$t(),{start:Ae,end:Te}=Et(_),H=t=>{const s=xe(t);return s?s.start.getTime()<=Te.getTime()&&s.end.getTime()>=Ae.getTime():!1},me=t=>q(t)!=="completed"&&!t.fitterId&&!(t.fitterName&&t.fitterName.trim()),Q=t=>{if(q(t)!=="booked")return!1;const s=xe(t);return s?s.end.getTime()<=_.getTime():!1},we=t=>{if(q(t)==="completed")return!1;const s=xe(t);return s?s.start.getTime()>_.getTime():!1},X=n.useMemo(()=>({thisWeek:f.filter(H).length,noFitter:f.filter(me).length,awaitingSignoff:f.filter(Q).length,upcoming:f.filter(we).length}),[f]),W=t=>t==="thisWeek"?H:t==="noFitter"?me:t==="awaitingSignoff"?Q:we,Le=t=>N==="all"?!0:N==="outstanding"?Ge(t):N==="paid"?t.balancePaymentStatus==="paid":!0,L=n.useMemo(()=>{let t=x?k.filter(W(x)):k;return N!=="all"&&(t=t.filter(Le)),S==="unknown"?t=t.filter(s=>q(s)==="completed"&&!s.leadSource):S!=="all"&&(t=t.filter(s=>s.leadSource===S)),t},[k,x,N,S]),Z=L.filter(t=>q(t)==="completed"&&!t.leadSource),a=Z.length>0&&Z.every(t=>F.has(t.quoteId)),D=(t,s)=>{B(o=>{const c=new Set(o);return s?c.add(t):c.delete(t),c})},h=n.useMemo(()=>{const t=C.toLowerCase();return f.filter(s=>{if(x&&!W(x)(s))return!1;if(y==="unassigned"){if(s.fitterId||s.fitterName&&s.fitterName.trim())return!1}else if(y!=="all"&&String(s.fitterId??"")!==y)return!1;return!(v!=="all"&&q(s)!==v||t&&![s.customerName,s.jobNumber,s.fitterName,s.generalArea,s.workSummary].filter(Boolean).join(" ").toLowerCase().includes(t))})},[f,C,y,v,x]),b=t=>i(`/admin/quotes/${t.quoteId}/job-sheet`),U=n.useMemo(()=>new Set(f.map(t=>t.quoteId)),[f]),z=n.useMemo(()=>f.filter(t=>{var s;return(((s=t.meetYourFitterWarnings)==null?void 0:s.length)??0)>0}).sort((t,s)=>new Date(t.scheduledDate).getTime()-new Date(s.scheduledDate).getTime()),[f]),at=t=>t.completedAt||t.status==="done"?{label:"Signed off",cls:"bg-[#BCCDB9] text-stone-900"}:Q(t)?{label:"Awaiting sign-off",cls:"bg-amber-100 text-amber-900"}:{label:"Not yet",cls:"bg-stone-100 text-stone-500"},rt=t=>{const s=(t.balanceAmount??0)>0?Bt(Number(t.balanceAmount)):"â";return t.balancePaymentStatus==="paid"?{amount:s,badge:"Paid",cls:"bg-[#BCCDB9] text-stone-900"}:Ge(t)?{amount:s,badge:"Outstanding",cls:"bg-red-100 text-red-700"}:{amount:s,badge:null,cls:""}},Ce=({k:t,label:s,count:o,icon:c,accent:m})=>{const d=x===t;return e.jsxs("button",{type:"button",onClick:()=>{ne(d?null:t),M("list")},"aria-pressed":d,className:`rounded-xl border p-4 text-left transition-colors ${d?"border-primary ring-1 ring-primary bg-primary/5":"border-gray-200 bg-white hover:border-primary/50"}`,children:[e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx(c,{className:`h-5 w-5 ${m}`}),e.jsx("span",{className:"text-2xl font-bold leading-none",children:o})]}),e.jsx("p",{className:"mt-1.5 text-sm text-gray-600",children:s})]})},nt=({j:t})=>{const s=q(t),o=at(t),c=rt(t);return e.jsxs("tr",{onClick:()=>b(t),className:"cursor-pointer border-t border-gray-100 hover:bg-primary/5",children:[e.jsx("td",{className:"px-3 py-2 align-top",onClick:m=>m.stopPropagation(),children:s==="completed"&&!t.leadSource?e.jsx(ze,{checked:F.has(t.quoteId),onCheckedChange:m=>D(t.quoteId,m===!0),"aria-label":`Select ${t.jobNumber}`}):null}),e.jsx("td",{className:"px-3 py-2 align-top whitespace-nowrap text-xs text-gray-500",children:t.jobNumber}),e.jsxs("td",{className:"px-3 py-2 align-top",children:[e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx("span",{className:"font-medium text-gray-900",children:t.customerName||"â"}),t.missingCustomerEmail&&e.jsx(re,{variant:"secondary",className:"bg-red-100 text-red-700",title:"No customer email â they won't get the prep email before fitting",children:"No email"})]}),t.workSummary&&e.jsx("div",{className:"text-xs text-gray-500 truncate max-w-[260px]",children:t.workSummary})]}),e.jsx("td",{className:"px-3 py-2 align-top whitespace-nowrap text-sm text-gray-700",children:Xe(t)}),e.jsx("td",{className:"px-3 py-2 align-top text-sm text-gray-700",children:t.fitterN
18ame&&t.fitterName.trim()?t.fitterName:e.jsx(re,{variant:"secondary",className:"bg-red-100 text-red-700",children:"No fitter"})}),e.jsx("td",{className:"px-3 py-2 align-top text-sm text-gray-600",children:t.generalArea||"â"}),e.jsx("td",{className:"px-3 py-2 align-top whitespace-nowrap",children:e.jsx(re,{variant:"secondary",className:Pt[s],children:Ke[s]})}),e.jsx("td",{className:"px-3 py-2 align-top whitespace-nowrap",children:e.jsx(re,{variant:"secondary",className:o.cls,children:o.label})}),e.jsxs("td",{className:"px-3 py-2 align-top whitespace-nowrap",children:[e.jsx("div",{className:"text-sm text-gray-700",children:c.amount}),c.badge&&e.jsx(re,{variant:"secondary",className:`mt-0.5 ${c.cls}`,children:c.badge})]}),e.jsxs("td",{className:"px-3 py-2 align-top text-right whitespace-nowrap",children:[e.jsx(j,{variant:"ghost",size:"sm",className:"h-7 px-2",title:"Duplicate â start a new job for this customer",onClick:m=>{m.stopPropagation(),G(t)},children:e.jsx(At,{className:"h-4 w-4"})}),e.jsx(qe,{className:"inline h-4 w-4 text-gray-400 ml-1"})]})]})},Je=({rows:t})=>e.jsx("div",{className:"overflow-x-auto rounded-lg border border-gray-200 bg-white",children:e.jsxs("table",{className:"w-full text-left text-sm",children:[e.jsx("thead",{className:"bg-gray-50 text-xs uppercase tracking-wide text-gray-500",children:e.jsxs("tr",{children:[e.jsx("th",{className:"px-3 py-2 font-medium",children:e.jsx(ze,{checked:a,onCheckedChange:s=>{const o=s===!0;B(c=>{const m=new Set(c);return Z.forEach(d=>o?m.add(d.quoteId):m.delete(d.quoteId)),m})},"aria-label":"Select all visible completed jobs with unknown lead source"})}),e.jsx("th",{className:"px-3 py-2 font-medium",children:"Job"}),e.jsx("th",{className:"px-3 py-2 font-medium",children:"Customer"}),e.jsx("th",{className:"px-3 py-2 font-medium",children:"Schedule"}),e.jsx("th",{className:"px-3 py-2 font-medium",children:"Fitter"}),e.jsx("th",{className:"px-3 py-2 font-medium",children:"Area"}),e.jsx("th",{className:"px-3 py-2 font-medium",children:"Status"}),e.jsx("th",{className:"px-3 py-2 font-medium",children:"Sign-off"}),e.jsx("th",{className:"px-3 py-2 font-medium",children:"Balance"}),e.jsx("th",{className:"px-3 py-2"})]})}),e.jsx("tbody",{children:t.map(s=>e.jsx(nt,{j:s},s.quoteId))})]})}),Oe=n.useMemo(()=>{const t=new Map;for(const s of L){const o=s.fitterId?`f${s.fitterId}`:s.fitterName&&s.fitterName.trim()?`n:${s.fitterName.trim()}`:"__none",c=s.fitterName&&s.fitterName.trim()?s.fitterName.trim():"No fitter assigned",m=t.get(o)??{name:c,jobs:[]};m.jobs.push(s),t.set(o,m)}return Array.from(t.values()).sort((s,o)=>s.name==="No fitter assigned"?-1:o.name==="No fitter assigned"?1:s.name.localeCompare(o.name))},[L]),it=n.useMemo(()=>{const t=Pe(w),s=(t.getDay()+6)%7,o=pe(t,-s);return Array.from({length:42},(c,m)=>pe(o,m))},[w]),lt=n.useMemo(()=>{const t=new Map;for(const s of h){const o=xe(s);if(o)for(let c=new Date(o.start);c.getTime()<=o.end.getTime();c=pe(c,1)){const m=_e(c),d=t.get(m)??[];d.push(s),t.set(m,d)}}return t},[h]),Me=({id:t,label:s,icon:o})=>e.jsxs("button",{type:"button",className:`flex items-center gap-1.5 px-4 py-1.5 text-sm ${l===t?"bg-primary text-primary-foreground":"bg-white text-gray-600"}`,onClick:()=>M(t),children:[e.jsx(o,{className:"h-4 w-4"})," ",s]});return e.jsxs("div",{className:"space-y-5",children:[z.length>0&&e.jsx(ue,{className:"border-amber-300 bg-amber-50",children:e.jsxs(Se,{className:"space-y-3 py-4",children:[e.jsxs("div",{className:"flex items-start gap-2 text-amber-950",children:[e.jsx(Ct,{className:"mt-0.5 h-5 w-5 shrink-0"}),e.jsxs("div",{children:[e.jsx("h2",{className:"font-semibold",children:"Meet Your Fitter needs attention"}),e.jsxs("p",{className:"text-sm",children:[z.length," upcoming job",z.length===1?"":"s"," still need checking before the fitting."]})]})]}),e.jsx("div",{className:"divide-y divide-amber-200 rounded-md border border-amber-200 bg-white",children:z.map(t=>e.jsxs("div",{className:"grid gap-2 p-3 md:grid-cols-[minmax(150px,0.7fr)_minmax(260px,1.5fr)_auto] md:items-start",children:[e.jsxs("div",{children:[e.jsxs(Be,{className:"font-semibold text-primary hover:underline",href:`/admin/quotes/${t.quoteId}/job-sheet`,children:[t.jobNumber," · ",t.customerName||"Customer"]}),e.jsx("p",{className:"text-xs text-gray-500",children:Xe(t)})]}),e.jsx("ul",{className:"space-y-1 text-sm text-amber-950",children:t.meetYourFitterWarnings.map(s=>e.jsx("li",{children:s.message},s.reason))}),e.jsxs("div",{className:"flex flex-wrap gap-2 md:justify-end",children:[e.jsx(j,{asChild:!0,size:"sm",variant:"outline",children:e.jsx(Be,{href:`/admin/quotes/${t.quoteId}/job-sheet`,children:"Open job sheet"})}),t.fitterId&&t.meetYourFitterWarnings.some(s=>s.action==="fitterProfile")&&e.jsx(j,{asChild:!0,size:"sm",variant:"outline",children:e.jsx(Be,{href:`/admin/fitters?edit=${t.fitterId}`,children:"Open fitter profile"})})]})]},t.quoteId))})]})}),e.jsxs("div",{className:"grid grid-cols-2 lg:grid-cols-4 gap-3",children:[e.jsx(Ce,{k:"thisWeek",label:"Jobs this week",count:X.thisWeek,icon:St,accent:"text-primary"}),e.jsx(Ce,{k:"noFitter",label:"No fitter yet",count:X.noFitter,icon:Lt,accent:"text-red-500"}),e.jsx(Ce,{k:"awaitingSignoff",label:"Awaiting sign-off",count:X.awaitingSignoff,icon:kt,accent:"text-amber-500"}),e.jsx(Ce,{k:"upcoming",label:"Upcoming starts",count:X.upcoming,icon:ht,accent:"text-stone-500"})]}),e.jsxs("div",{className:"flex items-center justify-between gap-3 flex-wrap",children:[e.jsxs("div",{className:"flex border border-gray-200 rounded-md overflow-hidden w-fit",children:[e.jsx(Me,{id:"list",label:"List",icon:Dt}),e.jsx(Me,{id:"fitter",label:"By fitter",icon:Mt}),e.jsx(Me,{id:"calendar",label:"Calendar",icon:Tt})]}),e.jsxs("div",{className:"flex items-center gap-2",children:[x&&e.jsx(j,{variant:"outline",size:"sm",onClick:()=>ne(null),children:"Clear card filter"}),e.jsxs(j,{size:"sm",onClick:()=>P(!0),children:[e.jsx(dt,{className:"h-4 w-4 mr-1.5"})," Add job sheet"]})]})]}),l!=="calendar"&&e.jsxs("div",{className:"flex flex-wrap gap-2 items-end",children:[e.jsxs("div",{className:"relative flex-1 min-w-[200px]",children:[e.jsx(tt,{className:"h-4 w-4 absolute left-2.5 top-1/2 -translate-y-1/2 text-gray-400"}),e.jsx(g,{className:"pl-8",placeholder:"Search customer, job, areaâ¦",value:I,onChange:t=>ge(t.target.value)})]}),e.jsxs(ee,{value:y,onValueChange:je,children:[e.jsx(te,{className:"w-44",children:e.jsx(se,{placeholder:"All fitters"})}),e.jsxs(ae,{children:[e.jsx(p,{value:"all",children:"All fitters"}),e.jsx(p,{value:"unassigned",children:"No fitter assigned"}),de.map(t=>e.jsx(p,{value:String(t.id),children:t.name},t.id))]})]}),e.jsxs(ee,{value:v,onValueChange:ie,children:[e.jsx(te,{className:"w-40",children:e.jsx(se,{placeholder:"All statuses"})}),e.jsxs(ae,{children:[e.jsx(p,{value:"all",children:"All statuses"}),e.jsx(p,{value:"provisional",children:"Awaiting fitter"}),e.jsx(p,{value:"booked",children:"Booked in"}),e.jsx(p,{value:"completed",children:"Completed"})]})]}),e.jsxs(ee,{value:N,onValueChange:ye,children:[e.jsx(te,{className:"w-44",children:e.jsx(se,{placeholder:"All balances"})}),e.jsxs(ae,{children:[e.jsx(p,{value:"all",children:"All balances"}),e.jsx(p,{value:"outstanding",children:"Balance outstanding"}),e.jsx(p,{value:"paid",children:"Balance paid"})]})]}),e.jsxs(ee,{value:S,onValueChange:t=>{Fe(t),t==="unknown"&&ie("completed"),B(new Set)},children:[e.jsx(te,{className:"w-48",children:e.jsx(se,{placeholder:"All lead sources"})}),e.jsxs(ae,{children:[e.jsx(p,{value:"all",children:"All lead sources"}),e.jsx(p,{value:"unknown",children:"Completed · Unknown source"}),(Qe=r.data)==null?void 0:Qe.map(t=>e.jsxs(p,{value:t.key,children:[t.name,t.archived?" (archived)":""]},t.key))]})]}),e.jsxs("div",{className:"flex flex-col gap-1",children:[e.jsx(u,{htmlFor:"job-from",className:"text-[11px] text-gray-500",children:"From"}),e.jsx(g,{id:"job-from",type:"date",className:"w-[150px]",value:A,max:T||void 0,onChange:t=>oe(t.target.value)})]}),e.jsxs("div",{className:"flex flex-col gap-1",children:[e.jsx(u,{htmlFor:"job-to",className:"text-[11px] text-gray-500",children:"To"}),e.jsx(g,{id:"job-to",type:"date",className:"w-[150px]",value:T,min:A||void 0,onChange:t=>ce(t.target.value)})]}),(A||T)&&e.jsx(j,{variant:"ghost",size:"sm",onClick:()=>{oe(""),ce("")},children:"Clear dates"})]}),l==="list"&&F.size>0&&e.jsx(ue,{children:e.jsxs(Se,{className:"flex flex-wrap items-end gap-3 py-3",children:[e.jsxs("div",{className:"flex flex-col gap-1",children:[e.jsxs(u,{className:"text-xs",children:[F.size," selected"]}),e.jsxs(ee,{value:R,onValueChange:le,children:[e.jsx(te,{className:"w-52",children:e.jsx(se,{placeholder:"Choose lead source"})}),e.jsx(ae,{children:(We=r.data)==null?void 0:We.filter(t=>!t.archived).map(t=>e.jsx(p,{value:t.key,children:t.name},t.key))})]})]}),e.jsx(j,{disabled:!R||O.isPending,onClick:()=>
18O.mutate(),children:O.isPending?"Updatingâ¦":"Apply to selected jobs"}),e.jsx(j,{variant:"ghost",onClick:()=>B(new Set),children:"Clear selection"}),e.jsx("p",{className:"text-xs text-gray-500",children:"Only completed jobs that are still Unknown can be changed here."})]})}),J?e.jsx("div",{className:"text-sm text-gray-500",children:"Loadingâ¦"}):l==="list"?L.length===0?e.jsx(ue,{children:e.jsx(Se,{className:"py-10 text-center text-sm text-gray-500",children:"No jobs match your filters."})}):e.jsxs("div",{className:"space-y-2",children:[e.jsxs("p",{className:"text-xs text-gray-500",children:[L.length," job",L.length===1?"":"s"]}),e.jsx(Je,{rows:L})]}):l==="fitter"?Oe.length===0?e.jsx(ue,{children:e.jsx(Se,{className:"py-10 text-center text-sm text-gray-500",children:"No jobs match your filters."})}):e.jsx("div",{className:"space-y-6",children:Oe.map(t=>e.jsxs("div",{className:"space-y-2",children:[e.jsxs("h3",{className:"text-sm font-semibold uppercase tracking-wide text-gray-500",children:[t.name," (",t.jobs.length,")"]}),e.jsx(Je,{rows:t.jobs})]},t.name))}):e.jsxs(ue,{className:"overflow-hidden",children:[e.jsxs("div",{className:"flex items-center justify-between gap-2 p-3 border-b",children:[e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx(j,{variant:"outline",size:"icon",className:"h-8 w-8",onClick:()=>Y(new Date(w.getFullYear(),w.getMonth()-1,1)),children:e.jsx(Ft,{className:"h-4 w-4"})}),e.jsx(j,{variant:"outline",size:"sm",onClick:()=>Y(Pe(new Date)),children:"Today"}),e.jsx(j,{variant:"outline",size:"icon",className:"h-8 w-8",onClick:()=>Y(new Date(w.getFullYear(),w.getMonth()+1,1)),children:e.jsx(qe,{className:"h-4 w-4"})})]}),e.jsx("span",{className:"font-semibold text-sm",children:w.toLocaleDateString("en-GB",{month:"long",year:"numeric"})})]}),e.jsx("div",{className:"grid grid-cols-7 bg-gray-50 border-b",children:["Mon","Tue","Wed","Thu","Fri","Sat","Sun"].map(t=>e.jsx("div",{className:"px-1 py-1.5 text-[11px] font-medium text-gray-600 text-center",children:t},t))}),e.jsx("div",{className:"grid grid-cols-7",children:it.map((t,s)=>{const o=t.getMonth()===w.getMonth(),c=lt.get(_e(t))??[],m=st(t,new Date);return e.jsxs("div",{className:`min-h-[100px] border-b border-r p-1.5 ${o?"bg-white":"bg-gray-50/50 text-gray-400"} ${s%7===6?"border-r-0":""}`,children:[e.jsx("div",{className:`text-xs mb-1 ${m?"font-bold text-[#5b7458]":""}`,children:t.getDate()}),e.jsxs("div",{className:"space-y-1",children:[c.slice(0,4).map(d=>{const Ue=q(d);return e.jsxs("button",{type:"button",onClick:()=>b(d),className:`w-full text-[11px] rounded px-1 py-0.5 text-left hover:opacity-80 ${qt[Ue]}`,title:`${d.jobNumber} â ${Ke[Ue]}${d.fitterName?` · ${d.fitterName}`:""}`,children:[e.jsx("div",{className:"truncate font-medium",children:d.customerName||d.jobNumber}),e.jsx("div",{className:"truncate opacity-80",children:d.fitterName||d.generalArea||d.workSummary||d.jobNumber})]},d.quoteId)}),c.length>4&&e.jsxs("div",{className:"text-[10px] text-gray-500",children:["+",c.length-4," more"]})]})]},s)})})]}),e.jsx(It,{open:be,onOpenChange:t=>{P(t),t||$(null)},fitters:de,existingJobQuoteIds:U,initialCustomer:ve})]})}function ps(){return e.jsx(ut,{title:"Jobs hub",description:"Every fitting job in one place â see what's on this week, what still needs a fitter, and what's waiting to be signed off.",children:e.jsx(Jt,{})})}export{Jt as JobsHubPanel,ps 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.