PageSourceSearch

https://www.smallbizagent.ai/assets/index-Bd0tcrmD.js

js smallbizagent.ai collected 2026-10-05 15:05:26 UTC 15,629 bytes, 6 lines download raw bytes

1import{r as l,j as e,e as he,u as T}from"./vendor-query-CnAhr2B8.js";import{c as ge,al as fe,a9 as G,D as pe,ab as ye,u as je,g as ve,a as we,aj as be,f as Ne,ay as De,d as U,B as b,h as Se,at as ke,F as Ce,C as Me,az as M,L as _,O as R,Q as q,R as H,T as Q,V as g,b as Pe,e as W,au as $e}from"./index-CLUtDhnY.js";import{D as Le}from"./data-table-Db5LAlAB.js";import{E as Ie}from"./export-button-DuGsAr6q.js";import{F as Te}from"./feature-tip-D5lKzg4O.js";import{u as Je,f as ze,c as Y,g as Ae,d as Fe}from"./use-business-hours-BVDO-KPC.js";import{A as Be}from"./activity-Cj-zUpsQ.js";import{S as Ee}from"./StaffFilterPills-D-OTmmZf.js";import{C as Ke}from"./circle-plus-7x-fFnx-.js";import"./vendor-ui-CYA7SV0W.js";import"./vendor-charts-CPMWc2oJ.js";import"./vendor-forms-UY-il7MQ.js";import"./table-2PZjRc6u.js";import"./chevrons-right-BKODknhn.js";import"./lightbulb-CC4qJ2Zf.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 Ve=ge("List",[["path",{d:"M3 12h.01",key:"nlz23k"}],["path",{d:"M3 18h.01",key:"1tta3j"}],["path",{d:"M3 6h.01",key:"1rqtza"}],["path",{d:"M8 12h13",key:"1za7za"}],["path",{d:"M8 18h13",key:"1lx6n3"}],["path",{d:"M8 6h13",key:"ik3vkj"}]]);function $({label:o,value:r,icon:N,pulse:c,valueColor:d,compact:u}){return e.jsxs("div",{className:"flex items-center gap-2 flex-shrink-0",children:[e.jsxs("div",{className:"relative",children:[e.jsx(N,{className:u?"h-3.5 w-3.5 text-gray-400":"h-4 w-4 text-gray-400"}),c&&Number(r)>0&&e.jsx("span",{className:"absolute -top-0.5 -right-0.5 h-2 w-2 rounded-full bg-green-500 animate-pulse"})]}),e.jsxs("div",{className:"flex flex-col leading-tight",children:[e.jsx("span",{className:`uppercase tracking-wider text-gray-400 font-medium ${u?"text-[9px]":"text-[10px]"}`,children:o}),e.jsx("span",{className:`font-bold tabular-nums ${u?"text-base":"text-lg"} ${d||"text-gray-900"}`,children:r})]})]})}function Ue({jobs:o,labels:r}){const c=fe(),d=l.useMemo(()=>{const u=new Date;u.setHours(0,0,0,0);const y=new Date(u);y.setDate(y.getDate()+1);const f=o.filter(i=>{var m;if((m=i.appointment)!=null&&m.startDate){const x=new Date(i.appointment.startDate);return x>=u&&x<y}if(i.scheduledDate){const x=new Date(i.scheduledDate);return x>=u&&x<y}return!1}),p=f.filter(i=>i.status!=="cancelled").length,D=f.filter(i=>i.status==="in_progress").length,P=f.filter(i=>i.status==="waiting_parts").length,j=f.filter(i=>i.status==="completed").reduce((i,m)=>m.lineItems?i+m.lineItems.reduce((x,S)=>x+(S.amount||0),0):i,0);return{booked:p,onSite:D,waitingParts:P,earned:j}},[o]);return e.jsxs("div",{className:"flex items-center gap-4 sm:gap-6 overflow-x-auto py-2 px-1 no-scrollbar",children:[e.jsx($,{label:`Booked ${r.entityLabel}`,value:d.booked,icon:G,compact:c}),e.jsx($,{label:r.earnedLabel,value:d.earned>0?`$${d.earned.toLocaleString()}`:"$0",icon:pe,compact:c}),e.jsx($,{label:r.activeLabel,value:d.onSite,icon:Be,pulse:!0,valueColor:d.onSite>0?"text-green-600":void 0,compact:c}),e.jsx($,{label:"Waiting Parts",value:d.waitingParts,icon:ye,valueColor:d.waitingParts>0?"text-yellow-600":void 0,compact:c})]})}function _e(o){const r=new Date(o),N=r.getDay();r.setDate(r.getDate()-N);const c=[];for(let d=0;d<7;d++)c.push(new Date(r)),r.setDate(r.getDate()+1);return c}function Re(o,r){return o.getFullYear()===r.getFullYear()&&o.getMonth()===r.getMonth()&&o.getDate()===r.getDate()}const O={pending:"Pending",in_progress:"In Progress",waiting_parts:"Waiting Parts",completed:"Completed",cancelled:"Cancelled"};function it(){const[,o]=je(),[r,N]=l.useState(""),[c,d]=l.useState(""),{user:u}=ve();we(),he();const y=u==null?void 0:u.businessId,{data:f}=T({queryKey:["/api/business"],enabled:!!y}),p=be(f==null?void 0:f.industry),[D,P]=l.useState(p?"calendar":"list"),[j,i]=l.useState(()=>new Date),[m,x]=l.useState("week"),[S,L]=l.useState(new Set),[X,Z]=l.useState(!1),{hourStart:k,hourEnd:I,labels:J}=Je(),z={businessId:y};r&&(z.status=r);const{data:h=[],isLoading:A,isError:ee,error:te,refetch:se}=T({queryKey:["/api/jobs",z]}),F={emergency:0,urgent:1,routine:2},ae=l.useMemo(()=>[...c?h.filter(s=>(s.urgency||"")===c):h].sort((s,a)=>{const n=F[s.urgency||""]??3,v=F[a.urgency||""]??3;return n-v}),[h,c]),{data:C=[]}=T({queryKey:["/api/staff"],enabled:!!y});if(!X&&C.length>0){const t=new Set(C.filter(s=>s.active!==!1).map(s=>s.id));t.add(null),L(t),Z(!0)}const re=l.useMemo(()=>{const t=new Map;return h.forEach(s=>
6{if(s.status!=="cancelled"){const a=s.staffId||null;t.set(a,(t.get(a)||0)+1)}}),t},[h]);l.useMemo(()=>{},[f,p]);const B=t=>{const s=Y(t);return e.jsx(U,{className:`${s.bg} ${s.text} hover:${s.bg} border-0`,children:O[t]||t})},ne=()=>i(new Date),ie=()=>{const t=new Date(j);t.setDate(t.getDate()-(m==="week"?7:1)),i(t)},le=()=>{const t=new Date(j);t.setDate(t.getDate()+(m==="week"?7:1)),i(t)},oe=l.useMemo(()=>_e(j),[j]),E=l.useMemo(()=>h.filter(t=>!(t.status==="cancelled"||!S.has(t.staffId||null))),[h,S]),K=l.useCallback(t=>{var V;let s,a;if((V=t.appointment)!=null&&V.startDate)s=new Date(t.appointment.startDate),a=new Date(t.appointment.endDate);else if(t.scheduledDate)s=new Date(t.scheduledDate+"T09:00:00"),a=new Date(s.getTime()+60*60*1e3);else return null;const n=s.getHours()*60+s.getMinutes(),v=a.getHours()*60+a.getMinutes(),w=(n-k*60)/60*64,xe=Math.max((v-n)/60*64,28);return{startDate:s,endDate:a,topPx:w,heightPx:xe}},[k]),ce=[{header:"Job",accessorKey:"title",cell:t=>e.jsxs("div",{children:[e.jsx("div",{className:"font-medium",children:t.title}),t.description&&e.jsx("div",{className:"text-sm text-gray-500 truncate max-w-[200px]",children:t.description})]})},{header:"Customer",accessorKey:"customer",cell:t=>{var s,a,n;return e.jsxs("div",{children:[e.jsxs("div",{className:"font-medium",children:[(s=t.customer)==null?void 0:s.firstName," ",(a=t.customer)==null?void 0:a.lastName]}),e.jsx("div",{className:"text-sm text-gray-500",children:Ne(((n=t.customer)==null?void 0:n.phone)||"")})]})}},{header:"Scheduled",accessorKey:"scheduledDate",cell:t=>{var s;if((s=t.appointment)!=null&&s.startDate){const a=new Date(t.appointment.startDate);return e.jsxs("div",{children:[e.jsx("div",{className:"font-medium",children:a.toLocaleDateString()}),e.jsx("div",{className:"text-sm text-gray-500",children:a.toLocaleTimeString([],{hour:"numeric",minute:"2-digit"})})]})}return t.scheduledDate?De(t.scheduledDate):"Not scheduled"}},{header:J.providerLabel,accessorKey:"staff",cell:t=>{var s,a;return t.staff?e.jsxs("div",{className:"flex items-center",children:[e.jsxs("div",{className:"h-8 w-8 rounded-full bg-gray-100 flex items-center justify-center text-gray-800 font-medium mr-2",children:[(s=t.staff.firstName)==null?void 0:s[0],(a=t.staff.lastName)==null?void 0:a[0]]}),e.jsxs("span",{children:[t.staff.firstName," ",t.staff.lastName]})]}):e.jsx("span",{className:"text-gray-500",children:"Unassigned"})}},{header:"Status",accessorKey:"status",cell:t=>B(t.status)},{header:"Urgency",accessorKey:"urgency",cell:t=>{if(!t.urgency)return e.jsx("span",{className:"text-muted-foreground",children:"—"});const s={emergency:"border-red-500 text-red-600",urgent:"border-amber-500 text-amber-600",routine:"border-slate-400 text-slate-600"},a=t.urgency.charAt(0).toUpperCase()+t.urgency.slice(1);return e.jsx(U,{variant:"outline",className:s[t.urgency]||"border-slate-400 text-slate-600","data-testid":`job-urgency-${t.id}`,children:a})}},{header:"Actions",accessorKey:"actions",cell:t=>e.jsx(b,{variant:"outline",size:"sm",onClick:s=>{s.stopPropagation(),o(`/jobs/${t.id}`)},children:"View Details"})}],de=({job:t})=>{const s=K(t);if(!s)return null;const a=Y(t.status);t.staffId?Ae(t.staffId,C):Fe[0];const n=t.customer?`${t.customer.firstName} ${t.customer.lastName||""}`.trim():"";return e.jsxs("div",{className:M("absolute left-1 right-1 rounded-md border-l-[3px] px-1.5 py-0.5 overflow-hidden cursor-pointer hover:ring-2 hover:ring-primary/30 transition-shadow z-10",a.bg,a.border),style:{top:`${s.topPx}px`,height:`${s.heightPx}px`},onClick:()=>o(`/jobs/${t.id}`),children:[e.jsxs("div",{className:"flex items-center gap-1 min-w-0",children:[e.jsx("div",{className:M("h-1.5 w-1.5 rounded-full flex-shrink-0",a.dot)}),e.jsx("span",{className:"text-[11px] font-semibold truncate",children:n})]}),s.heightPx>36&&e.jsx("div",{className:"text-[10px] text-gray-600 truncate",children:t.title}),s.heightPx>52&&e.jsxs("div",{className:"text-[10px] text-gray-500 truncate",children:[O[t.status],t.staff?` • ${t.staff.firstName}`:""]})]})},ue=({date:t,jobs:s})=>{const a=Re(t,new Date),n=t.toLocaleDateString("en-US",{weekday:"short"}),v=t.getDate().toString();return e.jsxs("div",{className:"flex-1 min-w-[120px] border-r border-gray-100 last:border-r-0",children:[e.jsxs("div",{className:M("sticky top-0 z-20 bg-white border-b text-center py-1.5",a&&"bg-primary/5"),children:[e.jsx("div",{className:"text-[11px] text-gray-500 uppercase",children:n}),e.jsx("div",{className:M("text-lg font-bold",a?"text-primary":"text-gray-900"),children:v})]}),e.jsx("div",{className:"relative",style:{height:`${(I-k)*64}px`},children:s.map(w=>e.jsx(de,{job:w},w.id))})]})},me=()=>{const t=l.useMemo(()=>{const a=new Map;
6return E.forEach(n=>{const v=K(n);if(!v)return;const w=v.startDate.toDateString();a.has(w)||a.set(w,[]),a.get(w).push(n)}),a},[E]),s=m==="week"?oe:[j];return e.jsxs("div",{className:"bg-white rounded-lg shadow-sm border overflow-hidden",children:[e.jsxs("div",{className:"flex items-center justify-between px-4 py-2 border-b",children:[e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx(b,{variant:"outline",size:"sm",onClick:ie,children:e.jsx($e,{className:"h-4 w-4"})}),e.jsx(b,{variant:"outline",size:"sm",onClick:ne,children:"Today"}),e.jsx(b,{variant:"outline",size:"sm",onClick:le,children:e.jsx(W,{className:"h-4 w-4"})}),e.jsx("span",{className:"text-sm font-medium text-gray-700 ml-2",children:j.toLocaleDateString("en-US",{month:"long",year:"numeric"})})]}),e.jsxs("div",{className:"flex items-center gap-1",children:[e.jsx(b,{variant:m==="day"?"default":"ghost",size:"sm",onClick:()=>x("day"),children:"Day"}),e.jsx(b,{variant:m==="week"?"default":"ghost",size:"sm",onClick:()=>x("week"),children:"Week"})]})]}),e.jsxs("div",{className:"flex overflow-x-auto",children:[e.jsxs("div",{className:"w-14 flex-shrink-0 border-r border-gray-200",children:[e.jsx("div",{className:"h-[52px] border-b"})," ",e.jsx("div",{className:"relative",style:{height:`${(I-k)*64}px`},children:Array.from({length:I-k},(a,n)=>e.jsx("div",{className:"absolute right-2 text-[11px] text-gray-400 -translate-y-1/2",style:{top:`${n*64}px`},children:ze(k+n)},n))})]}),s.map(a=>e.jsx(ue,{date:a,jobs:t.get(a.toDateString())||[]},a.toDateString()))]})]})};return e.jsxs(Se,{title:p?"Schedule":"Jobs",children:[ee&&e.jsx(ke,{error:te,onRetry:()=>se()}),e.jsx(Te,{tipId:"jobs-workflow",title:p?"Your job schedule":"Track jobs from start to finish",description:p?"View your schedule, update job status, and your customers get automatic SMS updates.":"Assign technicians, update status as work progresses, and convert completed jobs into invoices with one click.",icon:Ce}),e.jsx(Ue,{jobs:h,labels:J}),C.length>
60&&D==="calendar"&&e.jsx(Ee,{staffMembers:C.filter(t=>t.active!==!1),visibleStaffIds:S,onToggle:t=>{const s=new Set(S);s.has(t)?s.delete(t):s.add(t),L(s)},onShowAll:()=>{const t=new Set(C.filter(s=>s.active!==!1).map(s=>s.id));t.add(null),L(t)},appointmentCounts:re}),e.jsxs("div",{className:"flex justify-between items-center mb-4",children:[e.jsxs("div",{children:[e.jsx("h2",{className:"text-2xl font-bold",children:p?"Job Schedule":"Job Management"}),e.jsx("p",{className:"text-gray-500",children:p?"Manage your daily jobs and crew":"Manage all your ongoing and completed jobs"})]}),e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsxs("div",{className:"flex items-center border rounded-lg overflow-hidden",children:[e.jsx("button",{onClick:()=>P("calendar"),className:M("px-3 py-1.5 text-sm transition-colors",D==="calendar"?"bg-primary text-white":"bg-white text-gray-600 hover:bg-gray-50"),children:e.jsx(Me,{className:"h-4 w-4"})}),e.jsx("button",{onClick:()=>P("list"),className:M("px-3 py-1.5 text-sm transition-colors",D==="list"?"bg-primary text-white":"bg-white text-gray-600 hover:bg-gray-50"),children:e.jsx(Ve,{className:"h-4 w-4"})})]}),e.jsx(Ie,{endpoint:"/api/export/jobs",filename:"jobs.csv"}),e.jsx(_,{href:"/jobs/new",children:e.jsxs(b,{className:"flex items-center",children:[e.jsx(Ke,{className:"mr-2 h-4 w-4"}),"New Job"]})})]})]}),D==="calendar"?A?e.jsxs("div",{className:"bg-white rounded-lg shadow-sm border p-12 text-center",children:[e.jsx("div",{className:"animate-spin h-8 w-8 border-2 border-primary border-t-transparent rounded-full mx-auto"}),e.jsx("p",{className:"text-gray-500 mt-4",children:"Loading schedule..."})]}):e.jsx(me,{}):e.jsxs("div",{className:"bg-white rounded-lg shadow-sm border overflow-hidden",children:[e.jsxs("div",{className:"p-4 border-b flex justify-between items-center",children:[e.jsx("h3",{className:"text-lg font-medium",children:"All Jobs"}),e.jsx("div",{className:"w-64",children:e.jsxs(R,{value:r,onValueChange:N,children:[e.jsx(q,{children:e.jsx(H,{placeholder:"Filter by status"})}),e.jsxs(Q,{children:[e.jsx(g,{value:"all",children:"All Jobs"}),e.jsx(g,{value:"pending",children:"Pending"}),e.jsx(g,{value:"in_progress",children:"In Progress"}),e.jsx(g,{value:"waiting_parts",children:"Waiting Parts"}),e.jsx(g,{value:"completed",children:"Completed"}),e.jsx(g,{value:"cancelled",children:"Cancelled"})]})]})}),e.jsx("div",{className:"w-64",children:e.jsxs(R,{value:c||"all",onValueChange:t=>d(t==="all"?"":t),children:[e.jsx(q,{"data-testid":"job-urgency-filter",children:e.jsx(H,{placeholder:"Filter by urgency"})}),e.jsxs(Q,{children:[e.jsx(g,{value:"all",children:"All Urgencies"}),e.jsx(g,{value:"emergency",children:"Emergency"}),e.jsx(g,{value:"urgent",children:"Urgent"}),e.jsx(g,{value:"routine",children:"Routine"})]})]})})]}),A?e.jsx(Pe,{rows:6}):h&&h.length>0?e.jsx(Le,{columns:ce,data:ae,onRowClick:t=>o(`/jobs/${t.id}`),mobileCard:t=>{var s,a,n;return e.jsxs("div",{className:"p-4 flex items-center justify-between",children:[e.jsxs("div",{className:"min-w-0 flex-1",children:[e.jsx("div",{className:"font-medium truncate",children:t.title}),e.jsxs("div",{className:"text-sm text-muted-foreground",children:[(s=t.customer)==null?void 0:s.firstName," ",(a=t.customer)==null?void 0:a.lastName]}),e.jsxs("div",{className:"flex items-center gap-2 mt-1",children:[B(t.status),t.staff&&e.jsxs("span",{className:"text-xs text-muted-foreground",children:[t.staff.firstName," ",(n=t.staff.lastName)==null?void 0:n[0],"."]})]})]}),e.jsx(W,{className:"h-5 w-5 text-muted-foreground flex-shrink-0 ml-2"})]})}}):e.jsxs("div",{className:"flex flex-col items-center justify-center p-12 text-center",children:[e.jsx("div",{className:"mx-auto flex h-16 w-16 items-center justify-center rounded-full bg-primary/10 mb-4",children:e.jsx(G,{className:"h-8 w-8 text-primary"})}),e.jsx("h3",{className:"text-lg font-semibold text-foreground",children:r?"No matching jobs":"Track your work with jobs"}),e.jsx("p",{className:"mt-2 text-sm text-muted-foreground max-w-md mx-auto",children:r?`No jobs with status "${r}". Try a different filter.`:"Create jobs to track service requests, assign technicians, and manage your workflow from start to finish. Jobs can be converted into invoices when complete."}),!r&&e.jsx(_,{href:"/jobs/new",children:e.jsx(b,{className:"mt-6",children:"Create Job"})})]})]})]})}export{it 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.