1import{y as b,u as z,r as m,c as W,d as p,j as e,i as G,$ as J,I as X,f as T,l as P,m as q,n as L,o as I,B as j,e as g,q as l}from"./index-BAQ_1EVN.js";import{B as Z}from"./BoardingAdminLayout-DFVMBTaB.js";import{S as ee,a as te,b as se,c as ae,d as O}from"./select-C1kRCNcz.js";import{T as M}from"./textarea-V1HxuLEn.js";import{C as ne}from"./ClientProfileLink-BVkOUhyu.js";import{D as ie}from"./download-CnN7NBLF.js";import{S as oe}from"./search-DlhNkk7_.js";import{F as le}from"./filter-BuNUUCZa.js";import{C as R}from"./circle-check-big-CbfGy0eT.js";import{C as _}from"./circle-x-DPG4VCOY.js";import{p as K}from"./parseISO-BUbOC3tV.js";import"./index-BdQq_4o_.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 de=b("Flag",[["path",{d:"M4 15s1-1 4-1 5 2 8 2 4-1 4-1V3s-1 1-4 1-5-2-8-2-4 1-4 1z",key:"i9b6wo"}],["line",{x1:"4",x2:"4",y1:"22",y2:"15",key:"1cm3nv"}]]);/** 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 ce=b("Play",[["polygon",{points:"6 3 20 12 6 21 6 3",key:"1oa8hb"}]]);/** 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 re=b("StickyNote",[["path",{d:"M16 3H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V8Z",key:"qazsjp"}],["path",{d:"M15 3v4a2 2 0 0 0 2 2h4",key:"40519r"}]]),Q="#4A3C3A",y="#8BB381",me={pending_approval:"bg-[#8E9FAD]/20 text-[#B9C5CF]",a
16pproved:"bg-[#C9A96E]/20 text-[#DCC49A]",declined:"bg-[#C08262]/15 text-[#DBA98F]/70",pending_payment:"bg-[#C9A96E]/20 text-[#DCC49A]",confirmed:"bg-[#8FA382]/20 text-[#B7C4AC]",active:"bg-[#8FA382]/20 text-[#B7C4AC]",completed:"bg-muted text-muted-foreground",cancelled:"bg-[#C08262]/20 text-[#DBA98F]",waitlisted:"bg-[#A08BA5]/20 text-[#C6B5CA]"},V={pending_approval:"Awaiting Approval",approved:"Approved â Deposit Due",declined:"Declined",pending_payment:"Pending Payment",confirmed:"Confirmed",active:"Active (In House)",completed:"Completed",cancelled:"Cancelled",waitlisted:"Waitlisted"};function Ae(){const{toast:n}=z(),[A,$]=m.useState(""),[h,U]=m.useState("all"),[k,f]=m.useState(null),[w,S]=m.useState(""),[D,x]=m.useState(null),[B,C]=m.useState(""),{data:N,isLoading:H}=W({queryKey:["/api/boarding-admin/bookings"]}),F=p({mutationFn:t=>g("POST",`/api/boarding-admin/bookings/${t}/approve`),onSuccess:async t=>{const s=await t.json().catch(()=>({}));l.invalidateQueries({queryKey:["/api/boarding-admin/bookings"]}),l.invalidateQueries({queryKey:["/api/boarding-admin/stats"]}),n({title:"Request approved",description:s.emailSent?"The customer has been emailed a link to pay the deposit.":"Approved, but the email could not be sent â please contact the customer directly."})},onError:t=>{var i;const s=String((t==null?void 0:t.message)||""),c=s.indexOf("{");let o="Please try again.";if(c>=0)try{o=((i=JSON.parse(s.slice(c)))==null?void 0:i.message)||o}catch{}n({title:"Couldn't approve",description:o,variant:"destructive"})}}),v=p({mutationFn:({id:t,reason:s})=>g("POST",`/api/boarding-admin/bookings/${t}/decline`,{reason:s}),onSuccess:async t=>{const s=await t.json().catch(()=>({}));l.invalidateQueries({queryKey:["/api/boarding-admin/bookings"]}),l.invalidateQueries({queryKey:["/api/boarding-admin/stats"]}),x(null),C(""),n({title:"Request declined",description:s.emailSent?"The customer has been notified by email.":"Declined, but the email could not be sent â please contact the customer directly."})},onError:()=>n({title:"Failed to decline request",variant:"destructive"})}),u=p({mutationFn:({id:t,status:s})=>g("PATCH",`/api/boarding-admin/bookings/${t}/status`,{status:s}),onSuccess:()=>{l.invalidateQueries({queryKey:["/api/boarding-admin/bookings"]}),l.invalidateQueries({queryKey:["/api/boarding-admin/stats"]}),n({title:"Status updated"})},onError:()=>n({title:"Failed to update status",variant:"destructive"})}),E=p({mutationFn:({id:t,notes:s})=>g("PATCH",`/api/boarding-admin/bookings/${t}/notes`,{notes:s}),onSuccess:()=>{l.invalidateQueries({queryKey:["/api/boarding-admin/bookings"]}),n({title:"Notes saved"}),f(null)},onError:()=>n({title:"Failed to save notes",variant:"destructive"})}),d=(N==null?void 0:N.filter(t=>{var i,r;const s=A.toLowerCase(),c=!s||((i=t.dogName)==null?void 0:i.toLowerCase().includes(s))||((r=t.ownerName)==null?void 0:r.toLowerCase().includes(s)),o=h==="all"||t.status===h;return c&&o}))??[],Y=()=>{if(!d.length)return;const t=["ID","Dog","Owner","Check-in","Check-out","Nights","Status","Total (£)","Deposit (£)","Deposit Paid"],s=d.map(a=>[a.id,a.dogName,a.ownerName,a.checkInDate,a.checkOutDate,a.nights,a.status,(a.totalPrice/100).toFixed(2),(a.depositAmount/100).toFixed(2),a.depositPaid?"Yes":"No"]),c=[t,...s].map(a=>a.join(",")).join(` 17`),o=new Blob([c],{type:"text/csv"}),i=URL.createObjectURL(o),r=document.createElement("a");r.href=i,r.download="boarding-bookings.csv",r.click(),URL.revokeObjectURL(i)};return e.jsxs(Z,{children:[e.jsxs("div",{className:"boarding-page p-4 sm:p-6 space-y-6 min-h-full",children:[e.jsxs("div",{className:"flex items-start justify-between flex-wrap gap-4",children:[e.jsxs("div",{children:[e.jsxs("h1",{className:"text-2xl md:text-3xl font-bold text-[#4A3C3A] flex items-center gap-2",children:[e.jsx(G,{className:"h-5 w-5",style:{color:y}}),"Bookings"]}),e.jsxs("p",{className:"text-[#6B5E58] text-sm mt-0.5",children:[d.length," booking",d.length!==1?"s":""]})]}),e.jsxs("div",{className:"flex items-center gap-3",children:[e.jsxs("button",{onClick:Y,className:"boarding-secondary hidden sm:flex items-center gap-2 px-3 py-2 rounded-xl text-sm transition-colors",children:[e.jsx(ie,{className:"h-4 w-4"})," Export CSV"]}),e.jsx(J,{href:"/boarding-admin/bookings/new",className:"flex items-center gap-2 px-4 py-2 rounded-xl text-sm font-medium transition-all hover:opacity-90",style:{background:y,color:Q},children:"New Booking"})]})]}),e.jsxs("div",{className:"flex gap-3 flex-wrap",children:[e.jsxs("div",{className:"relative flex-1 min-w-48",children:[e.jsx(oe,{className:"absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-[#6B5E58]"}),e.jsx(X,{placeholder:"Search bookings...",value:A,onChange:t=>$(t.target.value),className:"boarding-field pl-9"})]}),e.jsxs(ee,{value:h,onValueChange:U,children:[e.jsxs(te,{className:"boarding-field w-full sm:w-48",children:[e.jsx(le,{className:"h-4 w-4 mr-2 text-[#6B5E58]"}),e.jsx(se,{})]}),e.jsxs(ae,{className:"bg-[#f6f1e7] border-[#e1d5c6]",children:[e.jsx(O,{value:"all",className:"text-[#4A3C3A] hover:bg-[#e1d5c6]/50",children:"All Statuses"}
17),Object.entries(V).map(([t,s])=>e.jsx(O,{value:t,className:"text-[#4A3C3A] hover:bg-[#e1d5c6]/50",children:s},t))]})]})]}),H?e.jsx("div",{className:"space-y-2",children:Array.from({length:5}).map((t,s)=>e.jsx("div",{className:"h-16 rounded-xl animate-pulse",style:{background:"rgba(255,255,255,0.04)"}},s))}):d.length===0?e.jsx("div",{className:"text-center py-16 text-[#6B5E58]",children:"No bookings found"}):e.jsx("div",{className:"space-y-2",children:d.map(t=>e.jsx("div",{className:"boarding-panel rounded-xl overflow-hidden",children:e.jsxs("div",{className:"flex items-center gap-4 p-4 flex-wrap",children:[e.jsxs("div",{className:"flex-1 min-w-0",children:[e.jsxs("div",{className:"flex items-center gap-2 flex-wrap",children:[e.jsx("span",{className:"text-[#4A3C3A] font-semibold text-sm",children:t.dogName}),e.jsx("span",{className:`px-2 py-0.5 rounded-full text-xs font-medium ${me[t.status]??"bg-muted text-muted-foreground"}`,children:V[t.status]??t.status}),t.depositPaid&&e.jsx("span",{className:"px-2 py-0.5 rounded-full text-xs font-medium bg-[#8FA382]/20 text-[#B7C4AC]",children:"Deposit paid"})]}),e.jsxs("p",{className:"text-[#6B5E58] text-xs mt-0.5",children:[t.ownerName," · ",T(K(t.checkInDate),"d MMM")," â ",T(K(t.checkOutDate),"d MMM")," · ",t.nights," night",t.nights!==1?"s":""]}),e.jsxs("p",{className:"text-[#6B5E58] text-xs",children:["Total: £",(t.totalPrice/100).toFixed(2)," · Deposit: £",(t.depositAmount/100).toFixed(2)]}),t.adminNotes&&e.jsx("p",{className:"text-[#DCC49A]/60 text-xs mt-1 italic",children:t.adminNotes}),e.jsx(ne,{dogId:t.dogId,className:"mt-2 inline-flex min-h-8 items-center text-xs font-medium text-[#DCC49A] underline underline-offset-4","data-testid":`link-manage-booking-client-${t.id}`,children:"Manage client"})]}),e.jsxs("div",{className:"flex items-center gap-2 flex-shrink-0 flex-wrap",children:[t.status==="pending_approval"&&e.jsxs(e.Fragment,{children:[e.jsxs("button",{onClick:()=>F.mutate(t.id),disabled:F.isPending,className:"flex items-center gap-1 px-3 py-1.5 rounded-lg text-xs font-medium bg-[#8FA382]/20 text-[#B7C4AC] hover:bg-[#8FA382]/30 transition-colors disabled:opacity-50",children:[e.jsx(R,{className:"h-3.5 w-3.5"})," Approve"]}),e.jsxs("button",{onClick:()=>{x(t.id),C("")},className:"flex items-center gap-1 px-3 py-1.5 rounded-lg text-xs font-medium bg-[#C08262]/15 text-[#DBA98F] hover:bg-[#C08262]/25 transition-colors",children:[e.jsx(_,{className:"h-3.5 w-3.5"})," Decline"]})]}),t.status==="approved"&&e.jsx("span",{className:"text-xs text-[#6B5E58] px-2",children:"Awaiting customer deposit"}),t.status==="pending_payment"&&e.jsxs("button",{onClick:()=>u.mutate({id:t.id,status:"confirmed"}),className:"flex items-center gap-1 px-3 py-1.5 rounded-lg text-xs font-medium bg-[#C9A96E]/20 text-[#DCC49A] hover:bg-[#C9A96E]/30 transition-colors",children:[e.jsx(R,{className:"h-3.5 w-3.5"})," Confirm"]}),t.status==="confirmed"&&e.jsxs("button",{onClick:()=>u.mutate({id:t.id,status:"active"}),className:"flex items-center gap-1 px-3 py-1.5 rounded-lg text-xs font-medium bg-[#8FA382]/20 text-[#B7C4AC] hover:bg-[#8FA382]/30 transition-colors",children:[e.jsx(ce,{className:"h-3.5 w-3.5"})," Check In"]}),t.status==="active"&&e.jsxs("button",{onClick:()=>u.mutate({id:t.id,status:"completed"}),className:"boarding-secondary flex items-center gap-1 px-3 py-1.5 rounded-lg text-xs font-medium transition-colors",children:[e.jsx(de,{className:"h-3.5 w-3.5"})," Check Out"]}),(t.status==="pending_payment"||t.status==="confirmed"||t.status==="approved")&&e.jsxs("button",{onClick:()=>u.mutate({id:t.id,status:"cancelled"}),className:"flex items-center gap-1 px-3 py-1.5 rounded-lg text-xs font-medium bg-[#C08262]/15 text-[#DBA98F] hover:bg-[#C08262]/25 transition-colors",children:[e.jsx(_,{className:"h-3.5 w-3.5"})," Cancel"]}),e.jsxs("button",{onClick:()=>{f(t.id),S(t.adminNotes||"")},className:"boarding-secondary flex items-center gap-1 px-3 py-1.5 rounded-lg text-xs font-medium transition-colors",children:[e.jsx(re,{className:"h-3.5 w-3.5"})," Notes"]})]})]})},t.id))})]}),e.jsx(P,{open:D!==null,onOpenChange:()=>x(null),children:e.jsxs(q,{className:"boarding-admin-dialog",children:[e.jsx(L,{children:e.jsx(I,{className:"text-[#4A3C3A]",children:"Decline Boarding Request"})}),e.jsx("p",{className:"text-[#6B5E58] text-sm",children:"The customer will be emailed to let them know. No payment was taken. You can add an optional reason to include in the email:"}),e.jsx(M,{value:B,onChange:t=>C(t.target.value),placeholder:"Optional reason, e.g. We're at capacity for those dates...",className:"boarding-field min-h-24"}),e.jsxs("div",{className:"flex gap-2 justify-end",children:[e.jsx(j,{variant:"ghost",onClick:()=>x(null),className:"text-[#6B5E58] hover:text-[#4A3C3A] hover:bg-[#e1d5c6]/50",children:"Keep Request"}),e.jsx(j,{onClick:()=>v.mutate({id:D,reason:B}),disabled:v.isPending,className:"bg-[#B0705C] hover:bg-[#C08262] text-white font-semibold",children:v.isPending?"Declining...":"Decline Request"})]})]})}),e.jsx(P,{open:k!==null,onOpenChange:()=>f(null),children:e.jsxs(q,{className:"boarding-admin-dialog",children:[e.jsx(L,{children:e.jsx(I,{className:"text-[#4A3C3A]",children:"Admin Notes"})}),e.jsx(M,{value:w,onChange:t=>S(t.target.value),placeholder:"Add notes about this booking...",className:"boarding-field min-h-32"}),e.jsx(j,{onClick:()=>E.mutate({id:k,notes:w}),disabled:E.isPending,style:{background:y,color:Q},className:"font-semibold",children:"Save Notes"})]})})]})}export{Ae 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.