PageSourceSearch

https://colara.co/assets/PlannerClients-C0KY4LQv.js

js colara.co collected 2026-10-02 01:21:48 UTC 39,541 bytes, 3 lines download raw bytes

1import{r as u,j as e}from"./vendor-Dz9lJ0A9.js";import{a as ye}from"./date-utils-VPArwFdR.js";import{Q as H,f as B,K as k,m as R,a0 as V,ab as re,B as ae,a1 as G,a2 as J,a3 as X,a4 as Z,P as ee,L as ue,aH as le,a as be,ac as Ne,u as xe,aB as we,al as Ce,e as Q,S as ke,af as de,b9 as he,T as A,z as T,G as M,X as Ie,U as Se,a5 as Pe,E as ge,R as Ee,A as De}from"./index-BDOD5E4V.js";import{B as ve}from"./badge-DV7OMou0.js";import{A as $e,a as Ae,b as Te}from"./avatar-DP601ZBU.js";import{g as Me}from"./image-utils-DbAqQCjZ.js";import{S as Le}from"./switch-CTKr17l3.js";import{I as q}from"./input-uKcRzRZU.js";import{T as oe}from"./textarea-B6S3DUiX.js";import{A as Fe}from"./AiWritingAssist-B2CsnXYN.js";import{L as qe}from"./label-xakz0IwZ.js";import{U as pe}from"./upload-BLxF6oWs.js";import{F as Re}from"./file-spreadsheet-CRVS43wl.js";import{C as ce}from"./circle-check-CJ_VUuj_.js";import{P as Ue}from"./PageHelper-DlLkbNgT.js";import Ke from"./FeatureUnavailable-9PRPlkrj.js";import{E as Oe}from"./empty-state-BuhuU5ze.js";import{u as Y}from"./useSendEmail-VzJS3P-p.js";import{C as Qe}from"./ClientPickerPopover-D6iIaEhv.js";import{P as Be}from"./PageSkeleton-Z6I6d4Km.js";import{d as ne}from"./differenceInCalendarDays-CLfZenBU.js";import{m as _e}from"./framer-motion-B-6Peltb.js";import{L as We}from"./link-2-DxAadr_f.js";import{C as Ye}from"./clock-BGjkcAN7.js";import{M as ze}from"./mail-x-BEECvQjO.js";import{M as fe}from"./mail-open-BaCrHpcx.js";import{M as je}from"./mouse-pointer-click-DrhaqtWj.js";import{S as z}from"./send-D2ID-sFR.js";import{A as He}from"./archive-8I_OaxNc.js";import{p as Ve}from"./parse-WBVh05eE.js";import{d as se}from"./format-TE1zKFYr.js";import{C as Ge}from"./calendar-Djt6tY64.js";import{L as Je}from"./log-in-BKYBixle.js";import{U as Xe}from"./user-check-DNCD2Fkh.js";import"./wand-sparkles-1lgr-9Q1.js";import"./use-dismissed-hints-CS8gEuUL.js";import"./circle-question-mark-Bqs4yPi3.js";import"./lock-B6WKmd2G.js";import"./EmailPreviewModal-C1KlrKnj.js";import"./checkbox-CKtbmYQK.js";import"./check-DtEZOD33.js";import"./skeleton-Dp0qviHj.js";import"./EmailTemplatePicker-DQTQrg-h.js";import"./popover-DLgxV-6d.js";import"./search-_0AhvJvF.js";import"./trash-2-DB6k7OfX.js";import"./list-BLP47b8F.js";import"./save-C1Cv7VS4.js";import"./braces-3gkFdW37.js";import"./crm-shortcodes-C9arKWs7.js";import"./client-name-DrkX_Dts.js";import"./currency-D2nl_7x3.js";import"./TimelineRichEditor-CWcsB4h6.js";import"./purify.es-D7kaK67O.js";import"./use-upload-BhuNLqHD.js";import"./timeline-notes-BAXXUWUX.js";import"./list-ordered-q1jHSUws.js";import"./heading-2-CkGv6oen.js";import"./link-qDdbFJaq.js";import"./text-align-start-DRmXArhb.js";import"./image-CRRQUQpN.js";import"./command-7zEmp0OB.js";import"./paperclip-C2dgC7YH.js";import"./undo-2-DuE24aMz.js";import"./pen-line-BmI8eKDA.js";import"./getTimezoneOffsetInMilliseconds-Dg5lkPpW.js";import"./en-US-B1YEHn5a.js";import"./addDays-B35i7aG5.js";(function(){try{var t=typeof window<"u"?window:typeof global<"u"?global:typeof globalThis<"u"?globalThis:typeof self<"u"?self:{};t.SENTRY_RELEASE={id:"8dd115ac638cf6a2a2cf5668d0686c96509328ed"};var v=new t.Error().stack;v&&(t._sentryDebugIds=t._sentryDebugIds||{},t._sentryDebugIds[v]="11f6e2a5-cfdd-49c6-90c6-b2bb3d0c06a3",t._sentryDebugIdIdentifier="sentry-dbid-11f6e2a5-cfdd-49c6-90c6-b2bb3d0c06a3")}catch{}})();function Ze(){const[t,v]=u.useState(!1),[r,j]=u.useState([]),[y,N]=u.useState([]),[D,g]=u.useState(""),[l,w]=u.useState(null),{toast:I}=H();function b(){j([]),N([]),g(""),w(null)}function f(a){const p=[];let c="",n=!1;for(let i=0;i<a.length;i++){const d=a[i];n?d==='"'&&a[i+1]==='"'?(c+='"',i++):d==='"'?n=!1:c+=d:d==='"'?n=!0:d===","?(p.push(c.trim()),c=""):c+=d}return p.push(c.trim()),p}function P(a){const p=a.map(d=>d.toLowerCase().replace(/[^a-z0-9]/g,""));let c=-1,n=-1,i=-1;for(let d=0;d<p.length;d++){const m=p[d];c===-1&&(m.includes("name")||m.includes("couple")||m.includes("bride")||m.includes("client"))&&(c=d),n===-1&&(m.includes("email")||m.includes("mail"))&&(n=d),i===-1&&(m.includes("date")||m.includes("wedding"))&&!m.includes("name")&&!m.includes("email")&&(i=d)}return c===-1&&(c=0),n===-1&&a.length>1&&(n=1),{nameIdx:c,emailIdx:n,dateIdx:i}}function h(a){const p=a.target.files?.[0];if(!p)return;g(p.name),w(null);const c=new FileReader;c.onload=n=>{const d=(n.target?.result).split(/\r?\n/).filter(s=>s.trim());if(d.length<2){I({title:"Invalid CSV",description:"The file needs a header row and at least one data row.",variant:"destructive"});return}const m=d.map(f);N(m.slice(0,6));const E=m[0],{nameIdx:$,emailIdx:S,dateIdx:L}=P(E),F=m.slice(1).filter(s=>s.some(o=>o.trim())).map(s=>({coupleNames:s[$]||"",email:S>=0&&s[S]||"",weddingDate:L>=0&&s[L]||""})).filter(s=>s.coupleNames.trim());j(F)},c.readAsText(p)}const C=B({mutationFn:async()=>(await R("POST","/api/weddings/bulk-import",{clients:r})).json(),onSuccess:a=>{w(a),k.refetchQueries({queryKey:["/api/weddings"]}),I({title:"Import complete",description:a.errors>0?`${a.created} of ${a.total} wedding tile${a.created!==1?"s":""} created. ${a.errors} could not be imported (check details).`:`${a.created} wedding tile${a.created!==1?"s":""} created successfully.`})},onError:a=>{const p=a.message?.toLowerCase().includes("limit reached")||a.message?.toLowerCase().includes("subscribe to add more");I(p?{title:"Upgrade Required",description:"Too many active brides for your subscription. Please upgrade to the unlimited plan to import more clients.",variant:"destructive"}:{title:"Import failed",description:a.message||"Please try again.",variant:"destructive"})}});return e.jsxs(V,{open:t,onOpenChange:a=>{a||b(),v(a)},children:[e.jsx(re,{asChild:!0,children:e.jsxs(ae,{variant:"outline",size:"sm",className:"tracking-wider up
1percase text-[10px]","data-testid":"button-bulk-import",children:[e.jsx(pe,{className:"w-3.5 h-3.5 mr-1.5"}),"Import Clients CSV"]})}),e.jsxs(G,{className:"max-w-lg max-h-[90vh] overflow-y-auto",children:[e.jsxs(J,{children:[e.jsx(X,{className:"font-display font-normal",children:"Import Clients from CSV"}),e.jsx(Z,{children:"Upload a CSV with your clients to create wedding tiles in bulk. You can send invitations later."})]}),e.jsxs("div",{className:"space-y-4 py-2",children:[e.jsxs("p",{className:"text-sm text-muted-foreground leading-relaxed",children:["Your CSV should have columns for ",e.jsx("span",{className:"font-medium text-foreground",children:"Name"})," (required),"," ",e.jsx("span",{className:"font-medium text-foreground",children:"Email"}),", and"," ",e.jsx("span",{className:"font-medium text-foreground",children:"Wedding Date"}),". The system will automatically detect column headers."]}),e.jsxs("div",{className:"space-y-2",children:[e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsx(qe,{children:"Choose CSV File"}),e.jsx("button",{onClick:()=>{const a="Name,Email,Wedding Date",p="Jane & John Doe,[email protected],2026-06-15",c=new Blob([a+`
2`+p+`
3`],{type:"text/csv"}),n=URL.createObjectURL(c),i=document.createElement("a");i.href=n,i.download="colara-clients-template.csv",i.click(),URL.revokeObjectURL(n)},className:"text-[10px] tracking-[0.12em] uppercase text-muted-foreground hover:text-foreground transition-colors","data-testid":"button-download-clients-csv-template",children:"Download Template"})]}),e.jsx(q,{type:"file",accept:".csv,text/csv",onChange:h,"data-testid":"input-bulk-csv-file"})]}),y.length>0&&!l&&e.jsxs("div",{className:"space-y-3",children:[e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx(Re,{className:"w-4 h-4 text-muted-foreground"}),e.jsx("span",{className:"text-sm font-medium",children:D}),e.jsxs(ve,{variant:"secondary",children:[r.length," client",r.length!==1?"s":""," detected"]})]}),e.jsx("div",{className:"overflow-x-auto border rounded-md",children:e.jsxs("table",{className:"w-full text-xs","data-testid":"table-bulk-csv-preview",children:[e.jsx("thead",{children:e.jsx("tr",{className:"border-b bg-muted/50",children:y[0]?.map((a,p)=>e.jsx("th",{className:"px-3 py-2 text-left font-medium",children:a},p))})}),e.jsx("tbody",{children:y.slice(1,6).map((a,p)=>e.jsx("tr",{className:"border-b last:border-0",children:a.map((c,n)=>e.jsx("td",{className:"px-3 py-2 max-w-[150px] truncate",children:c},n))},p))})]})}),e.jsxs("p",{className:"text-xs text-muted-foreground",children:["Showing preview of first ",Math.min(y.length-1,5)," row",y.length-1!==1?"s":"",". Each row will create a new wedding tile. No invitations will be sent — you can invite couples later from each wedding."]}),e.jsx(ee,{className:"w-full tracking-wider uppercase",onClick:()=>C.mutate(),disabled:C.isPending||r.length===0,"data-testid":"button-bulk-import-confirm",children:C.isPending?e.jsxs(e.Fragment,{children:[e.jsx(ue,{className:"w-4 h-4 animate-spin mr-2"})," Importing..."]}):e.jsxs(e.Fragment,{children:[e.jsx(pe,{className:"w-4 h-4 mr-2"})," Import ",r.length," Client",r.length!==1?"s":""]})})]}),l&&e.jsxs("div",{className:"space-y-3",children:[e.jsxs("div",{className:"flex items-center gap-2 text-sm",children:[e.jsx(ce,{className:"w-5 h-5 text-green-600"}),e.jsxs("span",{className:"font-medium",children:[l.created," wedding tile",l.created!==1?"s":""," created"]})]}),l.errors>0&&e.jsxs("div",{className:"space-y-2",children:[e.jsxs("div",{className:"flex items-center gap-2 text-sm text-destructive",children:[e.jsx(le,{className:"w-4 h-4"}),e.jsxs("span",{children:[l.errors," of ",l.total," row",l.errors!==1?"s":""," could not be imported"]})]}),l.results.some(a=>a.status==="limit_reached")&&l.weddingLimit!=null&&e.jsxs("div",{className:"bg-amber-50 dark:bg-amber-950/30 border border-amber-200 dark:border-amber-800 rounded-md p-3 text-xs text-amber-800 dark:text-amber-200",children:["Your current plan allows ",l.weddingLimit," active wedding",l.weddingLimit!==1?"s":"",". Upgrade your plan to import more clients."]}),e.jsxs("div",{className:"text-xs text-muted-foreground space-y-0.5 max-h-32 overflow-y-auto",children:[l.results.filter(a=>a.status==="error"||a.status==="limit_reached").slice(0,5).map((a,p)=>e.jsxs("p",{children:[e.jsx("span",{className:"font-medium text-foreground",children:a.coupleNames}),": ",a.error]},p)),l.results.filter(a=>a.status==="error"||a.status==="limit_reached").length>5&&e.jsxs("p",{className:"text-muted-foreground italic",children:["...and"," ",l.results.filter(a=>a.status==="error"||a.status==="limit_reached").length-5," ","more"]})]})]}),e.jsx("p",{className:"text-xs text-muted-foreground",children:"You can send invitations to your couples from each wedding's page when you're ready."}),e.jsx(ae,{variant:"outline",className:"w-full",onClick:()=>{b(),v(!1)},"data-testid":"button-bulk-import-done",children:"Done"})]})]})]})]})}function W(t){if(!t)return null;const v=new Date;v.setHours(12,0,0,0);
3const r=["MMMM d, yyyy","MMMM dd, yyyy","MM/dd/yyyy","yyyy-MM-dd","MMM d, yyyy"];for(const y of r)try{const N=Ve(t,y,v);if(se(N))return N.setHours(12,0,0,0),N}catch{}if(/^\d{4}-\d{2}-\d{2}$/.test(t)){const y=new Date(t+"T12:00:00");return se(y)?y:null}const j=new Date(t);return se(j)?j:null}function ie({wedding:t,onToggleActive:v,engagement:r}){const{setSelectedWeddingId:j}=Ee(),[,y]=xe(),{toast:N}=H(),D=W(t.weddingDate),g=D?ne(D,new Date):null,l=(t.coupleNames||"W").split(/[+&]/).map(x=>x.trim()[0]||"").join("").toUpperCase(),[w,I]=u.useState(!1),[b,f]=u.useState(t.coupleEmail||""),[P,h]=u.useState(t.coupleNames||""),[C,a]=u.useState(""),[p,c]=u.useState(""),[n,i]=u.useState(""),[d,m]=u.useState(null),[E,$]=u.useState(null);u.useEffect(()=>{w&&(f(t.coupleEmail||""),h(t.coupleNames||""),c(""),i(""))},[w,t.coupleEmail,t.coupleNames]);const S=()=>{k.refetchQueries({queryKey:["/api/pending-couple-invites"]}),k.refetchQueries({queryKey:["/api/client-engagement"]}),k.refetchQueries({queryKey:["/api/weddings"]}),k.refetchQueries({queryKey:[`/api/weddings/${t.id}/couple-accounts`]}),k.refetchQueries({queryKey:["/api/planner/couple-account-statuses"]})},L=Y({type:"couple_invite",params:{inviteId:d??0},title:"Send couple invitation",onSent:()=>{S(),E&&U.openPreview()}}),U=Y({type:"partner_invite",params:{inviteId:E??0},title:"Send co-owner invitation",onSent:S}),F=B({mutationFn:async()=>{const O=await(await R("POST","/api/invite/couple",{email:b.trim(),name:P.trim()||void 0,message:C.trim()||void 0,weddingId:t.id,deferEmail:!0})).json();let me;const te=p.trim().toLowerCase();return te&&te!==b.trim().toLowerCase()&&(me=await(await R("POST","/api/invite/partner",{email:te,name:n.trim()||void 0,weddingId:t.id,deferEmail:!0})).json()),{coupleResult:O,partnerResult:me}},onSuccess:({coupleResult:x,partnerResult:O})=>{$(O?.inviteId??null),x.inviteId?(m(x.inviteId),L.openPreview()):(N({title:x.status==="added"?"Client added":"Already invited",description:x.message}),O?.inviteId&&U.openPreview()),S(),f(""),h(""),a(""),c(""),i(""),I(!1)},onError:x=>{N({title:"Failed to send invitation",description:x.message||"Please try again.",variant:"destructive"})}}),s=()=>{j(t.id),y(`/dashboard?weddingId=${t.id}`)},o=t.isActive!==!1,_=r&&!r.coupleLoggedIn&&!r.inviteAccepted,K=r?.inviteSentAt;return e.jsxs(e.Fragment,{children:[e.jsx("div",{className:`group cursor-pointer rounded-xl px-5 py-5 transition-all duration-200 hover:bg-muted/15 border border-transparent hover:border-border/10 ${o?"":"opacity-50"}`,onClick:s,"data-testid":`card-client-${t.id}`,children:e.jsxs("div",{className:"flex items-start gap-4",children:[e.jsxs($e,{className:"h-11 w-11 shrink-0","data-testid":`avatar-client-${t.id}`,children:[t.couplePhotoUrl?e.jsx(Ae,{src:Me(t.couplePhotoUrl,"thumbnail"),alt:t.coupleNames||"Wedding"}):null,e.jsx(Te,{className:"font-display text-sm bg-primary/8 text-primary",children:l})]}),e.jsxs("div",{className:"flex-1 min-w-0",children:[e.jsx("h3",{className:"font-display text-[17px] font-normal text-foreground truncate","data-testid":`text-client-name-${t.id}`,children:t.coupleNames||"Unnamed Client"}),e.jsxs("div",{className:"flex flex-wrap items-center gap-2 mt-1",children:[t.weddingDate&&e.jsxs("span",{className:"flex items-center gap-1.5 text-[12px] text-muted-foreground/70","data-testid":`text-client-date-${t.id}`,children:[e.jsx(Ge,{className:"w-3 h-3"}),t.weddingDate]}),g!==null&&g>=0&&o&&e.jsxs("span",{className:"text-[10px] tracking-[0.1em] text-muted-foreground","data-testid":`badge-client-countdown-${t.id}`,children:[g," ",g===1?"day":"days"," away"]}),g!==null&&g<0&&e.jsx("span",{className:"text-[10px] tracking-[0.1em] text-muted-foreground/70",children:"Completed"}),!o&&e.jsx("span",{className:"text-[10px] tracking-[0.1em] text-muted-foreground/70","data-testid":`badge-inactive-${t.id}`,children:"Inactive"})]}),r&&e.jsx(he,{delayDuration:200,children:e.jsxs("div",{className:"flex flex-wrap items-center gap-1.5 mt-2","data-testid":`engagement-badges-${t.id}`,children:[r.inviteSentAt&&e.jsxs(A,{children:[e.jsx(T,{asChild:!0,children:e.jsxs("span",{className:`inline-flex items-center gap-1 text-[11px] tracking-[0.05em] uppercase px-1.5 py-0.5 rounded ${r.emailBounced?"text-red-600/70 dark:text-red-400/70":r.emailDelivered?"text-green-600/60 dark:text-green-400/60":"text-amber-600/60 dark:text-amber-400/60"}`,"data-testid":r.emailBounced?`badge-email-bounced-${t.id}`:`badge-email-sent-${t.id}`,children:[r.emailBounced?e.jsx(le,{className:"w-2.5 h-2.5"}):r.emailDelivered?e.jsx(ce,{className:"w-2.5 h-2.5"}):e.jsx(de,{className:"w-2.5 h-2.5"}),r.emailBounced?"Bounced":r.emailDelivered?"Delivered":"Sent"]})}),e.jsx(M,{children:e.jsx("p",{children:r.emailBounced?"The invite email bounced and was never received. Resend to a different address.":`Invite email ${r.emailDelivered?"was delivered":"was sent"}`})})]}),r.emailOpened&&e.jsxs(A,{children:[e.jsx(T,{asChild:!0,children:e.jsxs("span",{className:"inline-flex items-center gap-1 text-[11px] tracking-[0.05em] uppercase px-1.5 py-0.5 rounded text-blue-600/60 dark:text-blue-400/60","data-testid":`badge-email-opened-${t.id}`,children:[e.jsx(fe,{className:"w-2.5 h-2.5"})," Opened"]})}),e.jsx(M,{children:e.jsx("p",{children:"They opened the invite email"})})]}),r.emailClicked&&e.jsxs(A,{children:[e.jsx(T,{asChild:!0,children:e.jsxs("span",{className:"inline-flex items-center gap-1 text-[11px] tracking-[0.05em] uppercase px-1.5 py-0.5 rounded text-purple-600/60 dark:text-purple-400/60","data-testid":`badge-email-clicked-${t.id}`,children:[e.jsx(je,{className:"w-2.5 h-2.5"})," Clicked"]})}),e.jsx(M,{children:e.jsx("p",{children:"They clicked a link in the email"})})]}),r.coupleLoggedIn&&e.jsxs(A,{children:[e.jsx(T,{asChild:!0,children:e.jsxs("span",{className:"inline-flex items-center gap-1 text-[11px] tracking-[0.05em] uppercase px-1.5 py-0.5 rounded text-emerald-600/60 dark:text-emerald-400/60","data-testid":`badge-logged-in-${t.id}`,children:[e.jsx(Je,{className:"w-2.5 h-2.5"})," Logged In"]})}),e.jsx(M,{children:e.jsxs("p",{children:["They have logged into the app",r.lastActiveAt?` (last seen: ${ye(r.lastActiveAt)})`:""]})})]}),r.inviteAccepted&&e.jsxs(A,{children:[e.jsx(T,{asChild:!0,children:e.jsxs("span",{className:"inline-flex items-center gap-1 text-[11px] tracking-[0.05em] uppercase px-1.5 py-0.5 rounded text-green-600/60 dark:text-green-400/60","data-testid":`badge-accepted-${t.id}`,children:[e.jsx(Xe,{className:"w-2.5 h-2.5"})," Accepted"]})}),e.jsx(M,{children:e.jsx("p",{children:"They accepted the invite and set up their account"})})]}),_&&e.jsxs("button",{className:"text-[11px] tracking-[0.1em] uppercase text-primary/80 hover:text-primary transition-colors inline-flex items-center gap-1",onClick:x=>{x.stopPropagation(),I(!0)},"data-testid":`button-send-invite-${t.id}`,children:[e.jsx(z,{className:"w-2.5 h-2.5"})," ",K?"Resend":"Send Invite"]})]})}),t.venueName&&e.jsx("p",{className:"text-[11px] text-muted-foreground mt-1 truncate",children:t.venueName})]}),e.jsx("div",{className:"flex items-center gap-3 shrink-0",onClick:x=>x.stopPropagation(),children:e.jsxs("div",{className:"flex flex-col items-center gap-1",children:[e.jsx(Le,{checked:o,onCheckedChange:x=>v(t.id,x),"data-testid":`switch-active-${t.id}`}),e.jsx("span",{className:"text-[11px] tracking-[0.1em] uppercase text-muted-foreground
3/70",children:o?"Active":"Off"})]})}),e.jsx(De,{className:"w-3.5 h-3.5 text-muted-foreground group-hover:text-muted-foreground/70 transition-colors shrink-0 mt-1.5"})]})}),e.jsx(V,{open:w,onOpenChange:I,children:e.jsxs(G,{onClick:x=>x.stopPropagation(),children:[e.jsxs(J,{children:[e.jsxs(X,{className:"font-display font-normal",children:[K?"Resend Invite to":"Send Invite to"," ",t.coupleNames||"Couple"]}),e.jsx(Z,{children:"Send an email inviting your client to join Colara and collaborate on their wedding planning."})]}),e.jsxs("div",{className:"space-y-4 pt-2",children:[e.jsxs("div",{className:"space-y-2",children:[e.jsx("label",{className:"text-sm text-muted-foreground",children:"Email address"}),e.jsx(q,{value:b,onChange:x=>f(x.target.value),placeholder:"[email protected]",type:"email","data-testid":`input-card-invite-email-${t.id}`})]}),e.jsxs("div",{className:"space-y-2",children:[e.jsx("label",{className:"text-sm text-muted-foreground",children:"Their name (optional)"}),e.jsx(q,{value:P,onChange:x=>h(x.target.value),placeholder:"Name","data-testid":`input-card-invite-name-${t.id}`})]}),e.jsxs("div",{className:"space-y-2",children:[e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsx("label",{className:"text-sm text-muted-foreground",children:"Second partner (optional)"}),e.jsx(Qe,{onSelect:({email:x,name:O})=>{c(x),i(O)},excludeEmail:b,testId:`button-pick-client-partner-${t.id}`})]}),e.jsxs("div",{className:"grid grid-cols-2 gap-3",children:[e.jsx(q,{type:"email",value:p,onChange:x=>c(x.target.value),placeholder:"[email protected]","data-testid":`input-card-invite-partner-email-${t.id}`}),e.jsx(q,{value:n,onChange:x=>i(x.target.value),placeholder:"Name (optional)","data-testid":`input-card-invite-partner-name-${t.id}`})]}),e.jsx("p",{className:"text-xs text-muted-foreground",children:"They get their own login to this same wedding as a co-owner."})]}),e.jsxs("div",{className:"space-y-2",children:[e.jsx("label",{className:"text-sm text-muted-foreground",children:"Personal note (optional)"}),e.jsx(oe,{value:C,onChange:x=>a(x.target.value),placeholder:"Add a personal note that appears highlighted in the email...",rows:2,"data-testid":`input-card-invite-message-${t.id}`})]}),e.jsxs(ee,{onClick:()=>F.mutate(),disabled:!b.trim()||F.isPending,className:"w-full","data-testid":`button-preview-card-invite-${t.id}`,children:[e.jsx(ge,{className:"w-4 h-4 mr-2"})," Preview & Send"]})]})]})}),L.modal,U.modal]})}function et({open:t,onOpenChange:v,hideTrigger:r=!1}={}){const{toast:j}=H(),[y,N]=u.useState(!1),D=t!==void 0,g=D?t:y,l=n=>{D||N(n),v?.(n)},[w,I]=u.useState(""),[b,f]=u.useState(""),[P,h]=u.useState(""),[C,a]=u.useState(null),p=Y({type:"couple_invite",params:{inviteId:C??0},title:"Send couple invitation",onSent:()=>k.refetchQueries({queryKey:["/api/pending-couple-invites"]})}),c=B({mutationFn:async()=>(await R("POST","/api/invite/couple",{email:w.trim(),name:b.trim()||void 0,message:P.trim()||void 0,deferEmail:!0})).json(),onSuccess:n=>{n.status==="added"?(j({title:"Client added",description:n.message}),k.refetchQueries({queryKey:["/api/weddings"]})):n.status==="already_invited"?j({title:"Already invited",description:n.message}):n.inviteId&&(a(n.inviteId),p.openPreview()),k.refetchQueries({queryKey:["/api/pending-couple-invites"]}),I(""),f(""),h(""),l(!1)},onError:n=>{j({title:"Failed to send invitation",description:n.message||"Please try again.",variant:"destructive"})}});return e.jsxs(e.Fragment,{children:[e.jsxs(V,{open:g,onOpenChange:l,children:[!r&&e.jsx(re,{asChild:!0,children:e.jsxs("button",{className:"h-8 px-4 text-[11px] tracking-[0.2em] uppercase text-muted-foreground/70 hover:text-foreground border border-border/15 hover:border-border/30 rounded-lg transition-colors inline-flex items-center gap-1.5","data-testid":"button-invite-couple",children:[e.jsx(z,{className:"w-3 h-3"}),"Invite Couple"]})}),e.jsxs(G,{children:[e.jsxs(J,{children:[e.jsx(X,{className:"font-display font-normal",children:"Invite a Couple to Colara"}),e.jsx(Z,{children:"Send an email inviting your client to join Colara so you can collaborate on their wedding planning."})]}),e.jsxs("div",{className:"space-y-4 pt-2",children:[e.jsxs("div",{className:"space-y-2",children:[e.jsx("label",{className:"text-sm text-muted-foreground",children:"Email address"}),e.jsx(q,{value:w,onChange:n=>I(n.target.value),placeholder:"[email protected]",type:"email","data-testid":"input-invite-couple-email"})]}),e.jsxs("div",{className:"space-y-2",children:[e.jsx("label",{className:"text-sm text-muted-foreground",children:"Their name (optional)"}),e.jsx(q,{value:b,onChange:n=>
3f(n.target.value),placeholder:"Name","data-testid":"input-invite-couple-name"})]}),e.jsxs("div",{className:"space-y-2",children:[e.jsx("label",{className:"text-sm text-muted-foreground",children:"Personal note (optional)"}),e.jsx(oe,{value:P,onChange:n=>h(n.target.value),placeholder:"Add a personal note that appears highlighted in the email...",rows:2,"data-testid":"input-invite-couple-message"})]}),e.jsxs(ee,{onClick:()=>c.mutate(),disabled:!w.trim()||c.isPending,className:"w-full","data-testid":"button-preview-couple-invite",children:[e.jsx(ge,{className:"w-4 h-4 mr-2"})," Preview & Send"]})]})]})]}),p.modal]})}function tt({weddings:t}){const[v,r]=u.useState(!1),[j,y]=u.useState(""),{toast:N}=H(),{data:D=[]}=Q({queryKey:["/api/pending-couple-invites"]}),g=new Set(D.map(i=>i.email.toLowerCase())),l=u.useMemo(()=>t.filter(i=>i.isActive!==!1&&!i.ownerIsCouple),[t]),[w,I]=u.useState({}),[b,f]=u.useState(new Set);u.useEffect(()=>{if(v){const i=new Set;l.forEach(d=>i.add(d.id)),f(i),I({})}},[v,l]);const P=i=>{f(d=>{const m=new Set(d);return m.has(i)?m.delete(i):m.add(i),m})},h=()=>{b.size===l.length?f(new Set):f(new Set(l.map(i=>i.id)))},C=l.filter(i=>b.has(i.id)&&w[i.id]?.trim()).map(i=>({email:w[i.id].trim(),name:i.coupleNames||void 0,weddingId:i.id})),[a,p]=u.useState([]),c=Y({type:"couple_bulk_invite",params:{inviteIds:a.join(",")},title:"Send client invitations",onSent:()=>{k.refetchQueries({queryKey:["/api/weddings"]}),k.refetchQueries({queryKey:["/api/pending-couple-invites"]})}}),n=B({mutationFn:async()=>(await R("POST","/api/weddings/bulk-invite",{invites:C,message:j.trim()||void 0,deferEmail:!0})).json(),onSuccess:i=>{k.refetchQueries({queryKey:["/api/weddings"]}),k.refetchQueries({queryKey:["/api/pending-couple-invites"]}),r(!1);const d=(i?.results||[]).map(m=>m.inviteId).filter(m=>typeof m=="number");if(d.length===0){N({title:"Nothing to send",description:"Everyone selected has already been invited or joined."});return}p(d),c.openPreview()},onError:i=>{N({title:"Failed to send invitations",description:i.message||"Please try again.",variant:"destructive"})}});return l.length===0?null:e.jsxs(e.Fragment,{children:[e.jsxs(V,{open:v,onOpenChange:r,children:[e.jsx(re,{asChild:!0,children:e.jsxs("button",{className:"h-8 px-4 text-[11px] tracking-[0.2em] uppercase text-muted-foreground/70 hover:text-foreground border border-border/15 hover:border-border/30 rounded-lg transition-colors inline-flex items-center gap-1.5","data-testid":"button-bulk-invite-all",children:[e.jsx(de,{className:"w-3 h-3"}),"Invite All"]})}),e.jsxs(G,{className:"max-w-lg max-h-[90vh] overflow-y-auto",children:[e.jsxs(J,{children:[e.jsx(X,{className:"font-display font-normal",children:"Invite All Clients"}),e.jsx(Z,{children:"Send invitation emails to your couples who haven't joined Colara yet. Enter an email for each wedding you'd like to invite."})]}),e.jsxs("div",{className:"space-y-4 pt-2",children:[e.jsxs("div",{className:"flex items-center justify-between gap-2",children:[e.jsx(ae,{variant:"ghost",size:"sm",onClick:h,className:"text-xs px-0 h-auto","data-testid":"button-toggle-select-all",children:b.size===l.length?"Deselect all":"Select all"}),e.jsxs(ve,{variant:"secondary",children:[l.length," ",l.length===1?"wedding":"weddings"," without a linked couple"]})]}),e.jsx("div",{className:"space-y-3 max-h-[40vh] overflow-y-auto pr-1",children:l.map(i=>{const d=b.has(i.id),m=w[i.id]||"",E=m&&g.has(m.trim().toLowerCase());return e.jsxs("div",{className:`flex items-start gap-3 p-3 rounded-md border transition-colors ${d?"border-primary/30 bg-primary/5":"border-border opacity-60"}`,"data-testid":`bulk-invite-row-${i.id}`,children:[e.jsx("input",{type:"checkbox",checked:d,onChange:()=>P(i.id),className:"mt-1.5 accent-primary","data-testid":`checkbox-invite-${i.id}`}),e.jsxs("div",{className:"flex-1 min-w-0 space-y-1.5",children:[e.jsx("p",{className:"text-sm font-medium truncate",children:i.coupleNames||"Unnamed Client"}),i.weddingDate&&e.jsx("p",{className:"text-xs text-muted-foreground",children:i.weddingDate}),d&&e.jsxs("div",{children:[e.jsx(q,{type:"email",placeholder:"[email protected]",value:m,onChange:$=>I(S=>({...S,[i.id]:$.target.value})),className:"h-8 text-sm","data-testid":`input-invite-email-${i.id}`}),E&&e.jsx("p",{className:"text-xs text-amber-600 mt-1",children:"Already has a pending invitation"})]})]})]},i.id)})}),e.jsxs("div",{className:"space-y-2",children:[e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsx("label",{className:"text-sm text-muted-foreground",children:"Personal message (optional — included in all emails)"}),e.jsx(Fe,{field:"client_invite_message",currentText:j,onTextGenerated:y,context:{question:"This note goes to several client couples at once, so keep it general — no individual names."},mode:"both",size:"xs"})]}),e.jsx(oe,{value:j,onChange:i=>y(i.target.value),placeholder:"Add a personal note to your clients...",rows:2,"data-testid":"input-bulk-invite-message"})]}),e.jsx(ee,{onClick:()=>n.mutate(),disabled:C.length===0||n.isPending,className:"w-full tracking-wider up
3percase","data-testid":"button-send-bulk-invites",children:n.isPending?e.jsxs(e.Fragment,{children:[e.jsx(ue,{className:"w-4 h-4 animate-spin mr-2"})," Sending..."]}):e.jsxs(e.Fragment,{children:[e.jsx(z,{className:"w-4 h-4 mr-2"})," Send ",C.length," Invitation",C.length!==1?"s":""]})}),C.length===0&&b.size>0&&e.jsx("p",{className:"text-xs text-muted-foreground text-center",children:"Enter email addresses for the selected weddings to enable sending."})]})]})]}),c.modal]})}function vs(){const{data:t}=be(),{previewRole:v}=Ne(),[,r]=xe(),{toast:j}=H(),y=we(),[N,D]=u.useState(!1),g=t?.role==="admin"||t?.role==="planner"||t?.role==="photographer",l=Ce(t?.role);v||t?.role;const w=v==="couple"||!v&&l;u.useEffect(()=>{g&&R("POST","/api/claim-pending-invites").catch(()=>{})},[g]);const{data:I,isLoading:b}=Q({queryKey:["/api/weddings"],enabled:g||l}),{data:f=[]}=Q({queryKey:["/api/pending-couple-invites"],enabled:g}),{data:P}=Q({queryKey:["/api/client-engagement"],enabled:g}),{data:h}=Q({queryKey:["/api/wedding-usage"],enabled:g}),{data:C}=Q({queryKey:["/api/pending-invite-email-status"],enabled:g&&f.length>0}),a=B({mutationFn:async s=>(await R("DELETE",`/api/pending-couple-invite/${s}`)).json(),onSuccess:()=>{j({title:"Invitation cancelled"}),k.refetchQueries({queryKey:["/api/pending-couple-invites"]})},onError:()=>{j({title:"Couldn’t complete that",description:"Failed to cancel invitation",variant:"destructive"})}}),[p,c]=u.useState(null),[n,i]=u.useState(!1),d=Y({type:"couple_invite",params:{inviteId:p??0},title:"Resend couple invitation",onSent:()=>{k.refetchQueries({queryKey:["/api/pending-invite-email-status"]})}}),m=B({mutationFn:async({id:s,isActive:o})=>{await R("PATCH",`/api/weddings/${s}/active`,{isActive:o})},onSuccess:()=>{k.refetchQueries({queryKey:["/api/weddings"]}),k.refetchQueries({queryKey:["/api/wedding-usage"]})},onError:s=>{j({title:"Couldn’t complete that",description:Pe(s,"Failed to update wedding status"),variant:"destructive"})}});if(w)return e.jsx(Ke,{feature:"The Clients dashboard"});if(!g&&!l&&t)return r("/"),null;if(b)return e.jsx(Be,{variant:"cards"});const E=[...I||[]].sort((s,o)=>{const _=W(s.weddingDate),K=W(o.weddingDate);return!_&&!K?0:_?K?_.getTime()-K.getTime():-1:1}),$=E.filter(s=>s.isActive!==!1),S=E.filter(s=>s.isActive===!1),L=$.filter(s=>{const o=W(s.weddingDate);return!o||ne(o,new Date)>=0}),U=$.filter(s=>{const o=W(s.weddingDate);return o&&ne(o,new Date)<0}),F=(s,o)=>{m.mutate({id:s,isActive:o})};return e.jsxs("div",{className:"max-w-3xl mx-auto px-4 sm:px-6 py-10 space-y-10",children:[e.jsx(Ue,{title:"What is this?",description:"Your Client Hub gives you a bird's-eye view of every couple you're working with. See wedding dates, planning progress, and jump into any client's workspace with one click.",guideSlug:"planner-clients",storageKey:"planner-clients",steps:["Review your client list — each card shows the couple's key details.","Click a client card to enter their workspace and manage their wedding.","Use filters to sort by date, status, or search for a specific couple."]}),e.jsxs(_e.div,{initial:{opacity:0,y:20},animate:{opacity:1,y:0},transition:{duration:.4},children:[e.jsxs("div",{className:"text-center mb-10",children:[e.jsx("p",{className:"text-[10px] tracking-[0.4em] uppercase text-muted-foreground font-medium mb-4",children:"Dashboard"}),e.jsx("h1",{className:"font-display text-3xl md:text-4xl font-light tracking-tight text-foreground","data-testid":"text-clients-title",children:"My Weddings"}),e.jsxs("p",{className:"text-[13px] text-muted-foreground mt-2.5 font-light",children:[$.length," active",S.length>0&&` · ${S.length} inactive`]}),h&&h.limited&&h.limit!=null&&e.jsxs("div",{className:"mt-5 max-w-sm mx-auto","data-testid":"wedding-usage-bar",children:[e.jsxs("div",{className:"flex items-center justify-between mb-1.5",children:[e.jsx("span",{className:"text-[11px] tracking-[0.1em] uppercase text-muted-foreground/70 font-medium",children:"Wedding Slots"}),e.jsxs("span",{className:"text-[12px] font-medium text-foreground/80","data-testid":"text-wedding-slots",children:[h.used," of ",h.limit," used"]})]}),e.jsx("div",{className:"h-2 rounded-full bg-muted/30 overflow-hidden",children:e.jsx("div",{className:`h-full rounded-full transition-all duration-500 ${h.used>=h.limit?"bg-red-400 dark:bg-red-500":h.used>=h.limit-1?"bg-amber-400 dark:bg-amber-500":"bg-primary"}`,style:{width:`${Math.min(h.used/h.limit*100,100)}%`}})}),h.used>=h.limit?e.jsxs("p",{className:"text-[11px] text-red-500/80 mt-1.5 flex items-center justify-center gap-1",children:[e.jsx(le,{className:"w-3 h-3"}),"You've reached your limit. Upgrade or deactivate a wedding to add more."]}):h.limit-h.used===1?e.jsx("p",{className:"text-[11px] text-amber-600/70 dark:text-amber-400/70 mt-1.5 text-center",children:"1 slot remaining"}):null,e.jsxs("a",{href:"/billing",className:"inline-flex items-center gap-1 text-[11px] tracking-[0.05em] text-primary hover:underline mt-2","data-testid":"link-upgrade-plan",children:[e.jsx(ke,{className:"w-3 h-3"})," Upgrade for unlimited weddings"]})]}),e.jsx("div",{className:"mt-6 flex items-center justify-center gap-3 flex-wrap",children:g&&!v&&e.jsxs(e.Fragment,{children:[e.jsx(et,{open:n,onOpenChange:i}),e.jsx(tt,{weddings:E}),e.jsx(Ze,{})]})})]}),(()=>{const s=E.filter(o=>o.isActive!==!1&&!o.ownerIsCouple);return s.length===0?null:e.jsxs("div",{className:"mt-10 rounded-xl border border-primary/15 bg-primary/5 px-5 py-4 flex items-center gap-4 flex-wrap","data-testid":"banner-unlinked-couples",children:[e.jsx("div",{className:"w-9 h-9 rounded-lg bg-primary/10 flex items-center justify-center shrink-0",children:e.jsx(We,{className:"w-4 h-4 text-primary"})}),e.jsxs("div",{className:"flex-1 min-w-0",children:[e.jsxs("p",{className:"text-sm font-medium text-foreground",children:[s.length," ",s.length===1?"wedding":"weddings"," not yet linked to a couple"]}),e.jsx("p",{className:"text-xs text-muted-foreground mt-0.5",children:"Send an invitation so your clients can access their wedding workspace on Colara."})]})]})})(),f&&f.length>0&&e.jsxs("div",{className:"mt-10 space-y-2","data-testid":"section-pending-couple-invites",children:[e.jsx("p",{className:"text-[11px] tracking-[0.25em] uppercase text-muted-foreground
3/70 font-medium mb-3 pl-1",children:"Pending Invitations"}),e.jsx("div",{className:"space-y-0.5",children:f.map(s=>e.jsx("div",{className:"group rounded-xl px-5 py-4 hover:bg-muted/10 transition-colors","data-testid":`card-pending-couple-${s.id}`,children:e.jsxs("div",{className:"flex items-center gap-4",children:[e.jsx("div",{className:"w-9 h-9 rounded-lg bg-muted/25 flex items-center justify-center shrink-0",children:e.jsx(de,{className:"w-4 h-4 text-muted-foreground/70"})}),e.jsxs("div",{className:"flex-1 min-w-0",children:[e.jsx("p",{className:"text-[15px] font-medium text-foreground truncate","data-testid":`text-pending-couple-name-${s.id}`,children:s.name||s.email}),s.name&&e.jsx("p",{className:"text-[13px] text-muted-foreground/70 truncate",children:s.email}),e.jsxs("div",{className:"flex flex-wrap items-center gap-1.5 mt-1",children:[e.jsxs("span",{className:"text-[11px] tracking-[0.08em] uppercase text-muted-foreground inline-flex items-center gap-1","data-testid":`badge-pending-couple-${s.id}`,children:[e.jsx(Ye,{className:"w-3 h-3"})," Pending"]}),C?.[s.email.toLowerCase()]&&(()=>{const o=C[s.email.toLowerCase()];return e.jsxs(he,{delayDuration:200,children:[o.bounced&&e.jsxs(A,{children:[e.jsx(T,{asChild:!0,children:e.jsxs("span",{className:"inline-flex items-center gap-1 text-[11px] tracking-[0.05em] uppercase text-red-500/70","data-testid":`badge-invite-bounced-${s.id}`,children:[e.jsx(ze,{className:"w-3 h-3"})," Bounced"]})}),e.jsx(M,{children:e.jsx("p",{children:"Email bounced — check the address"})})]}),!o.bounced&&o.delivered&&e.jsxs(A,{children:[e.jsx(T,{asChild:!0,children:e.jsxs("span",{className:"inline-flex items-center gap-1 text-[11px] tracking-[0.05em] uppercase text-green-600/70 dark:text-green-400/70","data-testid":`badge-invite-delivered-${s.id}`,children:[e.jsx(ce,{className:"w-3 h-3"})," Delivered"]})}),e.jsx(M,{children:e.jsx("p",{children:"Email was delivered to their inbox"})})]}),o.opened&&e.jsxs(A,{children:[e.jsx(T,{asChild:!0,children:e.jsxs("span",{className:"inline-flex items-center gap-1 text-[11px] tracking-[0.05em] uppercase text-blue-600/70 dark:text-blue-400/70","data-testid":`badge-invite-opened-${s.id}`,children:[e.jsx(fe,{className:"w-3 h-3"})," Opened"]})}),e.jsx(M,{children:e.jsx("p",{children:"They opened the email"})})]}),o.clicked&&e.jsxs(A,{children:[e.jsx(T,{asChild:!0,children:e.jsxs("span",{className:"inline-flex items-center gap-1 text-[11px] tracking-[0.05em] uppercase text-purple-600/70 dark:text-purple-400/70","data-testid":`badge-invite-clicked-${s.id}`,children:[e.jsx(je,{className:"w-3 h-3"})," Clicked"]})}),e.jsx(M,{children:e.jsx("p",{children:"They clicked a link in the email"})})]})]})})()]})]}),e.jsxs("div",{className:"flex items-center gap-1",children:[e.jsxs("button",{onClick:o=>{o.stopPropagation(),c(s.id),d.openPreview()},className:"text-[11px] tracking-[0.12em] uppercase text-muted-foreground hover:text-foreground transition-colors inline-flex items-center gap-1 px-2 py-1","data-testid":`button-resend-couple-invite-${s.id}`,children:[e.jsx(z,{className:"w-3 h-3"}),"Resend"]}),e.jsx("button",{disabled:a.isPending&&a.variables===s.id,onClick:async o=>{o.stopPropagation(),await y({title:"Cancel this invitation?",description:"The invite link will stop working. You can send a new invitation any time.",confirmLabel:"Cancel invite",testId:"confirm-cancel-couple-invite"})&&a.mutate(s.id)},className:"p-1 text-muted-foreground hover:text-red-400 transition-colors disabled:opacity-30","data-testid":`button-cancel-couple-invite-${s.id}`,"aria-label":"Cancel couple invite",children:e.jsx(Ie,{className:"w-3.5 h-3.5"})})]})]})},s.id))})]}),E.length===0&&(!f||f.length===0)&&e.jsx(Oe,{icon:Se,title:"Your client weddings, all in one place",description:"Invite a couple to track timelines, budgets, and vendors for every wedding you manage.",primaryAction:g&&!v?{label:"Invite Your First Couple",icon:z,onClick:()=>i(!0),testId:"button-empty-invite-couple"}:void 0,testId:"card-no-clients"}),E.length>0&&e.jsxs("div",{className:"mt-10 space-y-10",children:[L.length>0&&e.jsxs("div",{"data-testid":"section-upcoming-weddings",children:[e.jsx("p",{className:"text-[11px] tracking-[0.3em] uppercase text-muted-foreground font-medium mb-2 pl-1",children:"Upcoming"}),e.jsx("div",{className:"divide-y divide-border/6",children:L.map(s=>e.jsx(ie,{wedding:s,onToggleActive:F,engagement:P?.[s.id]},s.id))})]}),U.length>0&&e.jsxs("div",{"data-testid":"section-past-weddings",children:[e.jsx("p",{className:"text-[11px] tracking-[0.3em] uppercase text-muted-foreground
3/70 font-medium mb-2 pl-1",children:"Past"}),e.jsx("div",{className:"divide-y divide-border/6",children:U.map(s=>e.jsx(ie,{wedding:s,onToggleActive:F,engagement:P?.[s.id]},s.id))})]}),S.length>0&&e.jsxs("div",{"data-testid":"section-inactive-weddings",children:[e.jsxs("button",{className:"flex items-center gap-2 text-[10px] tracking-[0.15em] uppercase text-muted-foreground/70 hover:text-muted-foreground/70 transition-colors pl-1",onClick:()=>D(!N),"data-testid":"button-toggle-inactive",children:[e.jsx(He,{className:"w-3 h-3"}),N?"Hide":"Show"," ",S.length," inactive"]}),N&&e.jsx("div",{className:"divide-y divide-border/6 mt-2",children:S.map(s=>e.jsx(ie,{wedding:s,onToggleActive:F,engagement:P?.[s.id]},s.id))})]})]})]}),d.modal]})}export{vs 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.