1import{c4 as y,co as Z,bl as re,bN as e,aX as se,aC as oe,br as de,c3 as ce,D as he,bn as ue}from"./index-MaNUTrZd.js";import{P as me,I as xe}from"./PayBlocksEditor-C4napqEV.js";function Y(o){if(!o)return"no card on file";const b=String(o.brand||"card");return b.charAt(0).toUpperCase()+b.slice(1)+(o.last4?" â¢"+o.last4:"")}const S=(o,b="cad")=>de(o||0,b||"cad"),V=()=>({description:"",qty:1,unit_price_cents:0}),te={monthly:"Monthly",quarterly:"Quarterly",annual:"Annual"};function Q(o){if(!o)return"â";const b=/^(\d{4})-(\d{2})-(\d{2})/.exec(o);return b?new Date(Number(b[1]),Number(b[2])-1,Number(b[3])).toLocaleDateString("en-US",{month:"short",day:"numeric",year:"numeric"}):o}const ne={active:{label:"Active",color:"#1A5FCC"},paused:{label:"Paused",color:"#f59e0b"},ended:{label:"Ended",color:"#64748b"}},be={active:{label:"Account OK",color:"#1A5FCC"},past_due:{label:"Past due",color:"#f59e0b"},restricted:{label:"Restricted",color:"#dc2626"},suspended:{label:"Suspended",color:"#7f1d1d"}},ie={issued:"sent",due_14:"14 days before",due_7:"7 days before",due_1:"day before",due_today:"on the day",overdue_1:"1 day late",overdue_7:"7 days late",overdue_14:"14 days late",restricted:"restricted",restored:"restored",paid:"paid"};function pe({cfg:o}){const b=(o.reminder_days||[]).slice().sort((v,s)=>s-v),E=(o.overdue_days||[]).slice().sort((v,s)=>v-s),f=o.reminders_enabled===!1,w=(o._notifications||[]).filter(v=>v.kind!=="issued"),R=w[0],H=w.filter(v=>v.status==="failed").length;return f?e.jsx("span",{className:"text-[13px] text-slate-400",children:"â reminders off"}):e.jsxs("div",{className:"text-[13px] leading-relaxed",children:[e.jsx("div",{className:"text-slate-700",children:b.length?e.jsxs(e.Fragment,{children:["before: ",e.jsx("b",{children:b.join(" · ")}),"d"]}):e.jsx("span",{className:"text-slate-400",children:"no before-due reminders"})}),e.jsx("div",{className:E.length?"text-amber-700":"text-slate-400",children:E.length?e.jsxs(e.Fragment,{children:["after: ",e.jsx("b",{children:E.join(" · ")}),"d"]}):"no chasing when late"}),R?e.jsxs("div",{className:"mt-1 text-slate-500",children:["last: ",ie[R.kind]||R.kind," · ",Q(String(R.sent_at).slice(0,10)),H>0&&e.jsxs("span",{className:"text-red-600 font-bold",children:[" · ",H," failed"]})]}):e.jsx("div",{className:"mt-1 text-slate-400",children:"none sent yet"})]})}function ve(){var ae;const[o,b]=y.useState(""),[E,f]=y.useState(!1),[w,R]=y.useState([]),[H,v]=y.useState([]),[s,O]=y.useState({logins:[],care:[],recipients:[]}),[M,D]=y.useState(null),[W,$]=y.useState(!0),[A,N]=y.useState(null),[k,p]=y.useState(null),[L,I]=y.useState(null),u=t=>{p(t),setTimeout(()=>p(null),4e3)};y.useEffect(()=>{(async()=>{const{data:{user:t}}=await Z.auth.getUser();if(!(t!=null&&t.id)){N("Not signed in."),$(!1),f(!0);return}b(t.id);const{data:r}=await Z.from("profiles").select("business_name, business_phone, email, address, logo_url").eq("id",t.id).maybeSingle();D(r?{...r,brand:re(r,t.id)}:null);try{const h=await(await fetch("/.netlify/functions/billing-configs",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({action:"pickable",actor_user_id:t.id})})).json().catch(()=>({}));h!=null&&h.ok&&O({logins:h.logins||[],care:h.care||[],recipients:h.recipients||[]})}catch{}const{data:m}=await Z.from("profiles").select("id, full_name, business_name, email, account_status").order("business_name",{ascending:!0}).limit(500);v(m||[]),f(!0),await P(t.id)})()},[]);const[x,T]=y.useState("");async function F(t,r){if(!o)return;const m=(t==null?void 0:t.label)||(t==null?void 0:t._client_name)||"this arrangement";if(!r){const c=t.auto_send?`Create this month's invoice for ${m} AND EMAIL IT to ${t.bill_email||"the client"}?`:`Create this month's invoice for ${m} as a DRAFT? Nothing is sent â you review it in Business Hub â Invoices.`;if(!window.confirm(c))return}T(t.id+(r?":dry":"")),N(null),p(null);try{const c=await fetch("/.netlify/functions/billing-run",{method:"POST",headers:se(),body:JSON.stringify({actor_user_id:o,config_id:t.id,dry_run:r})}),h=await c.json().catch(()=>({}));if(!c.ok||h.error)throw new Error(h.detail||h.message||h.error||`HTTP ${c.status}`);const C=(h.results||[])[0];C?r?p(`Dry run · ${C.period_key}
1: would ${C.status==="skipped"?`SKIP â ${C.reason}`:`create ${S(C.total_cents||0,t.currency)} due ${C.due_on}`}. Nothing was written.`):C.status==="sent"?p(`â ${C.invoice_number} created and emailed · ${S(C.total_cents||0,t.currency)} due ${C.due_on}`):C.status==="created"?p(`â ${C.invoice_number} created as a DRAFT · ${S(C.total_cents||0,t.currency)}. Review it in Business Hub â Invoices, then send.`):p(`${C.status}: ${C.reason||""}`):p("Nothing to do â that arrangement is not due and has nothing to bill."),r||await P(o)}catch(c){N((c==null?void 0:c.message)||"Run failed.")}T("")}async function q(t,r){if(!o)return;const m=(t==null?void 0:t.label)||(t==null?void 0:t._client_name)||"this arrangement";if(!(!r&&!window.confirm(`Send today's reminders for ${m} now? Anyone already emailed about the same invoice today will not be emailed again.`))){T(t.id+(r?":rdry":":rem")),N(null),p(null);try{const c=await fetch("/.netlify/functions/billing-run",{method:"POST",headers:se(),body:JSON.stringify({actor_user_id:o,what:"reminders",config_id:t.id,dry_run:r})}),h=await c.json().catch(()=>({}));if(!c.ok||h.error)throw new Error(h.detail||h.message||h.error||`HTTP ${c.status}`);const C=(h.results||[]).map(G=>`${G.status==="sent"?"â":G.status==="failed"?"â":"·"} ${G.kind} â ${G.invoices.map(le=>le.invoice_number).join(", ")}${G.reason?` â ${G.reason}`:""}`);!h.due&&!h.marked_overdue?p(`Nothing on the ladder today â ${h.scanned} open invoice${h.scanned===1?"":"s"} checked, none of them sitting on a reminder day.`):p(r?`Preview · ${h.due} on the ladder today. Nothing was sent. 2${C.join(` 3`)}`:`${h.sent} sent · ${h.skipped} already done · ${h.failed} failed${h.marked_overdue?` · ${h.marked_overdue} marked overdue`:""} 4${C.join(` 5`)}`),r||await P(o)}catch(c){N((c==null?void 0:c.message)||"Reminder run failed.")}T("")}}const[i,d]=y.useState("");async function g(t,r,m){if(!o)return null;d(t.id+":"+r),N(null),p(null);try{const c=await fetch("/.netlify/functions/billing-card",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({actor_user_id:o,config_id:t.id,action:r,...m||{}})}),h=await c.json().catch(()=>({}));if(!c.ok||h.error)throw new Error(h.message||h.detail||h.error||`HTTP ${c.status}`);return d(""),h}catch(c){return N((c==null?void 0:c.message)||"Card action failed."),d(""),null}}async function B(t){const r=await g(t,"link");if(!(r!=null&&r.url))return;let m=!1;try{await navigator.clipboard.writeText(r.url),m=!0}catch{}p(`Card link for ${t._client_name||"this client"}${m?" â copied to your clipboard":""}: 6${r.url} 7 8Send it however you like. It is a Stripe page: they type the card there, we never see or store the number. Stripe checkout links expire after 24 hours.`)}async function a(t){var c;const r=!((c=t._card)!=null&&c.auto_charge);if(r&&!window.confirm(`Charge ${t._client_name||"this client"}'s ${Y(t._card)} automatically on day ${t.bill_day} of every month? 9 10The invoice is still created and emailed first â they always see what they are paying for. 11If the card declines they get the pay link exactly as they do today, and you get an alert. 12 13Have you already charged one invoice by hand and watched it land?`))return;const m=await g(t,"toggle",{auto_charge:r});m!=null&&m.ok&&(p(r?`â Auto-charge ON for ${t._client_name}. Day ${t.bill_day} of each month, ${Y(t._card)}.`:`Auto-charge is off for ${t._client_name}. Invoices still go out with the pay link, as before.`),await P(o))}async function l(t){const r=t._last_invoice;if(!r){N("No invoice to charge yet â run the arrangement first, then charge the invoice it makes.");return}const m=(r.total_cents||0)-(r.amount_paid_cents||0);if(m<=0){N(`${r.invoice_number} is already paid.`);return}if(!window.confirm(`THIS CHARGES A REAL CARD, NOW. 14 15${t._client_name} 16${r.invoice_number} · ${S(m,r.currency)} 17on ${Y(t._card)} 18 19Money moves immediately. Continue?`))return;const c=await g(t,"charge_now");c!=null&&c.ok&&(p(`â Charged ${S(c.charged_cents||m,r.currency)} to ${c.card} · ${r.invoice_number} is now ${c.invoice_status||"paid"}.`),await P(o))}async function n(t){if(!window.confirm(`Remove ${Y(t._card)} for ${t._client_name}? 20 21It is detached at Stripe too. Auto-charge stops; invoices carry on going out with the pay link.`))return;const r=await g(t,"remove");r!=null&&r.ok&&(p(`Card removed for ${t._client_name}.`),await P(o))}async function _(t){const r=window.prompt(`Charge a custom amount to ${t._client_name||"this client"}'s ${Y(t._card)}. 22 23Amount in ${String(t.currency||"cad").toUpperCase()} (e.g. 2.00):`,"2.00");if(r===null)return;const m=Math.round(Number(String(r).replace(/[^0-9.]/g,""))*100);if(!Number.isFinite(m)||m<50){N("Enter an amount of at least $0.50.");return}const c=window.prompt("What is this charge for? (goes on the invoice line)","Card-on-file test charge")||"Card-on-file test charge";if(!window.confirm(`THIS CHARGES A REAL CARD, NOW. 24 25${t._client_name} 26${S(m,t.currency)} â "${c}" 27on ${Y(t._card)} 28 29A real invoice is created and marked paid. Money moves immediately. Continue?`))return;const h=await g(t,"charge_custom",{amount_cents:m,description:c});if(h){if(!h.ok){N(`${h.invoice_number?h.invoice_number+": ":""}${h.message||h.error||"Charge failed."}`);return}p(`â Charged ${S(h.charged_cents||m,t.currency)} to ${h.card} · ${h.invoice_number} created and ${h.invoice_status||"paid"}
29. Check Business Hub â Invoices and your Stripe dashboard â that is the whole loop.`),await P(o)}}async function P(t){$(!0),N(null);try{const r=await fetch("/.netlify/functions/billing-configs",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({action:"list",actor_user_id:t})}),m=await r.json().catch(()=>({}));if(!r.ok||m.error)throw new Error(m.message||m.error||`HTTP ${r.status}`);R(m.configs||[])}catch(r){N((r==null?void 0:r.message)||"Could not load billing configs.")}$(!1)}function z(){const t=new Date,r=t.getFullYear(),m=t.getMonth()+1,c=t.getDate();I({status:"active",currency:"cad",cycle:"monthly",bill_day:Math.min(c,28),net_days:14,tax_pct:0,start_on:`${r}-${String(m).padStart(2,"0")}-${String(c).padStart(2,"0")}`,line_items:[V()],auto_send:!1,send_email:!0,send_sms:!1,reminders_enabled:!0,reminder_days:[14,7,1],overdue_days:[],grace_days:7,auto_restrict:!1,pay_blocks:{...oe(),schedule:[],note:""}})}const X=y.useMemo(()=>w.filter(t=>t.status==="active").reduce((t,r)=>{const m=r.cycle==="annual"?12:r.cycle==="quarterly"?3:1;return t+Math.round((r._total_cents||0)/m)},0),[w]);return E?e.jsxs("div",{className:"space-y-6 p-2 md:p-4 max-w-[1400px] mx-auto",children:[e.jsxs("div",{className:"rounded-2xl p-6 md:p-8 text-white relative overflow-hidden",style:{background:"linear-gradient(135deg,#1F6FEB 0%,#1A5FCC 50%,#1A5FCC 100%)"},children:[e.jsxs("div",{className:"flex items-start justify-between gap-4 flex-wrap",children:[e.jsxs("div",{children:[e.jsx("div",{className:"text-[12px] uppercase tracking-widest font-bold opacity-80",children:"Admin · B2539"}),e.jsx("h1",{className:"font-display font-extrabold text-3xl md:text-4xl",children:"ð Recurring Billing"}),e.jsx("p",{className:"text-sm opacity-90 mt-1 max-w-2xl",children:"Bill the same clients the same amount every month without remembering to. Set it up here, check the dates, and nothing goes out until you say so."})]}),e.jsx("button",{onClick:z,className:"px-5 py-2.5 rounded-xl font-bold text-sm bg-white text-slate-900 hover:bg-slate-100",children:"+ New recurring bill"})]}),w.length>0&&e.jsxs("div",{className:"mt-5 flex gap-6 flex-wrap text-sm",children:[e.jsxs("div",{children:[e.jsx("span",{className:"opacity-70",children:"Active arrangements"})," ",e.jsx("b",{className:"ml-1",children:w.filter(t=>t.status==="active").length})]}),e.jsxs("div",{children:[e.jsx("span",{className:"opacity-70",children:"Normalised monthly"})," ",e.jsx("b",{className:"ml-1",children:S(X,((ae=w[0])==null?void 0:ae.currency)||"cad")})]})]})]}),e.jsxs("div",{className:"rounded-xl bg-amber-50 border border-amber-200 text-amber-900 px-4 py-3 text-sm",children:[e.jsx("b",{children:"Phase 5 of 7 â the generator and the reminders are both live."})," Each arrangement becomes a real invoice on its bill day, and ",e.jsx("b",{children:"Run now"})," does it immediately: auto-send ",e.jsx("b",{children:"off"})," produces a ",e.jsx("b",{children:"draft"})," you review in Business Hub â Invoices, auto-send ",e.jsx("b",{children:"on"})," emails the client with a pay link. Reminders then go out on their own each morning â ",e.jsx("b",{children:"only the days listed under âbefore dueâ"}),", which is where every new arrangement starts. Chasing a late invoice needs the",e.jsx("b",{children:" âdays after dueâ"})," box filled in per client, deliberately, and it ships empty.",e.jsxs("div",{className:"mt-1.5",children:["Still not deployed: ",e.jsx("b",{children:"account restriction"})," (phase 7). Nothing gets switched off for anybody, whatever the auto-restrict switch says â and no reminder ever suggests otherwise."]})]}),e.jsxs("div",{className:"rounded-xl bg-emerald-50 border border-emerald-200 text-emerald-900 px-4 py-3 text-sm",children:[e.jsx("b",{children:"New: card on file."})," ",e.jsx("b",{children:"Get card link"})," gives you a Stripe page to send the client â they enter the card there, we never see or store the number. Once it is saved, ",e.jsx("b",{children:"Charge now"})," takes the last invoice off that card immediately, and ",e.jsx("b",{children:"auto-charge"})," does the same thing on its own on the bill day, every month.",e.jsxs("div",{className:"mt-1.5",children:[e.jsx("b",{children:"Charge one by hand and watch it land before you turn auto-charge on."})," That is the whole safety procedure: the first time a client's real card is charged should be something you watched happen. Auto-charge is off on every card until you switch it on per client."]}),e.jsx("div",{className:"mt-1.5",children:"The invoice is always created and emailed first â a charged one arrives saying it is paid, so nobody is charged for something they never saw. If a card declines, the client gets the pay link exactly as they do today and you get an alert. A decline changes nothing else: nothing is restricted, nothing stops."})]}),e.jsxs("details",{className:"rounded-xl border border-slate-200 bg-white overflow-hidden",open:!w.length,children:[e.jsxs("summary",{className:"cursor-pointer select-none px-4 py-3 font-display font-bold text-slate-900 flex items-center gap-2 hover:bg-slate-50",children:[e.jsx("span",{children:"ð"})," How this works â read once",e.jsx("span",{className:"ml-auto text-[13px] font-semibold text-slate-400 uppercase tracking-widest",children:"click to open / close"})]}),e.jsxs("div",{className:"px-5 pb-5 pt-1 text-sm text-slate-700 leading-relaxed border-t border-slate-100",children:[e.jsxs("p",{className:"mt-3 mb-4 text-slate-600",children:["An ",e.jsx("b",{children:"arrangement"})," is one client you bill the same amount every month. Set it up once and the invoice makes itself on the day you pick â same invoice as one you'd type by hand: pay link, PDF, Stripe, e-Transfer, the lot."]}),e.jsx("ol",{className:"space-y-3 list-none pl-0 m-0",children:[["1","Create it",e.jsxs(e.Fragment,{children:["Hit ",e.jsx("b",{children:"+ New recurring bill"}),". Pick the client from the list, or just type their name, email and address if they have no login. Add the line items, set the amount, pick which day of the month to bill, and Save."]})],["2","Check the dates",e.jsxs(e.Fragment,{children:["Hit ",e.jsx("b",{children:"Dry run"}),". It tells you exactly what it would create â the amount, the peri
29od and the due date â and ",e.jsx("b",{children:"writes nothing"}),". This is where you catch a wrong bill day, before a client ever sees it."]})],["3","Make one for real",e.jsxs(e.Fragment,{children:["Hit ",e.jsx("b",{children:"Run â draft"}),". That creates the invoice as a ",e.jsx("b",{children:"draft"}),". Go and look at it in ",e.jsx("b",{children:"Business Hub â Invoices"}),". Nothing has been sent to anyone."]})],["4","Then let it run itself",e.jsxs(e.Fragment,{children:["Happy with the draft? Open the arrangement and turn ",e.jsx("b",{children:"auto-send on"}),". From then on it creates ",e.jsx("i",{children:"and emails"})," the invoice on its bill day, every month, without anyone remembering to. The button turns ",e.jsx("b",{className:"text-red-600",children:"red"})," and reads ",e.jsx("b",{children:"Run + send"})," so the two can never be confused."]})]].map(([t,r,m])=>e.jsxs("li",{className:"flex gap-3",children:[e.jsx("span",{className:"shrink-0 w-6 h-6 rounded-full bg-brand-500 text-white text-xs font-bold grid place-items-center mt-0.5",children:t}),e.jsxs("span",{children:[e.jsxs("b",{className:"text-slate-900",children:[r,"."]})," ",m]})]},t))}),e.jsxs("div",{className:"mt-5 grid md:grid-cols-2 gap-3",children:[e.jsxs("div",{className:"rounded-lg bg-emerald-50 border border-emerald-200 px-3.5 py-3 text-[14.5px] text-emerald-900",children:[e.jsx("div",{className:"font-bold mb-1",children:"You cannot bill someone twice"}),"That guard is a database constraint, not code that might have a bug in it. Press Run twice for the same month â or let the nightly job and your click land together â and the second one just tells you the period is already billed."]}),e.jsxs("div",{className:"rounded-lg bg-slate-50 border border-slate-200 px-3.5 py-3 text-[14.5px] text-slate-700",children:[e.jsx("div",{className:"font-bold mb-1 text-slate-900",children:"Nothing sends by accident"}),"Every new arrangement starts with ",e.jsx("b",{children:"auto-send off"}),". It makes drafts until you deliberately turn it on, so no client receives anything you have not looked at first."]})]}),e.jsxs("div",{className:"mt-3 rounded-lg bg-amber-50 border border-amber-200 px-3.5 py-3 text-[14.5px] text-amber-900",children:[e.jsx("div",{className:"font-bold mb-1",children:"Not built yet: chasing and restricting"}),"Nothing reminds a client that an invoice is overdue, and nothing restricts an account for non-payment â those are phases 5â7. The ",e.jsx("b",{children:"auto-restrict"})," switch on each row is stored but no code reads it, so turning it on today does nothing at all. Chasing an unpaid invoice is still a human job."]}),e.jsxs("div",{className:"mt-4 text-[13.5px] text-slate-500",children:["Full reasoning, schema and the remaining phases: ",e.jsx("code",{className:"bg-slate-100 px-1.5 py-0.5 rounded",children:"docs/RECURRING-BILLING-SPEC.md"})," in the repo."]})]})]}),k&&e.jsx("div",{className:"rounded-xl bg-emerald-50 border border-emerald-200 text-emerald-900 px-4 py-3 text-sm font-bold whitespace-pre-line",children:k}),A&&e.jsxs("div",{className:"rounded-xl bg-red-50 border border-red-200 text-red-800 px-4 py-3 text-sm",children:["â ",A]}),e.jsxs("div",{className:"bg-white border border-slate-200 rounded-xl overflow-hidden",children:[e.jsxs("div",{className:"px-4 py-3 border-b border-slate-200 flex items-center gap-3 flex-wrap",children:[e.jsx("div",{className:"font-display font-bold text-slate-900",children:"Arrangements"}),e.jsxs("div",{className:"text-xs text-slate-500",children:[w.length," total"]})]}),W?e.jsx("div",{className:"p-10 text-center text-slate-500",children:"Loadingâ¦"}):w.length?e.jsx("div",{className:"overflow-x-auto",children:e.jsxs("table",{className:"w-full text-sm",children:[e.jsx("thead",{className:"bg-slate-50 text-[13px] uppercase tracking-widest text-slate-500 font-bold",children:e.jsxs("tr",{children:[e.jsx("th",{className:"px-4 py-3 text-left",children:"Client"}),e.jsx("th",{className:"px-4 py-3 text-left",children:"Arrangement"}),e.jsx("th",{className:"px-4 py-3 text-right",children:"Amount"}),e.jsx("th",{className:"px-4 py-3 text-left",children:"Next bill"}),e.jsx("th",{className:"px-4 py-3 text-left",children:"Last invoice"}),e.jsx("th",{className:"px-4 py-3 text-left",children:"Reminders"}),e.jsx("th",{className:"px-4 py-3 text-left",children:"Card on file"}),e.jsx("th",{className:"px-4 py-3 text-left",children:"Switches"}),e.jsx("th",{className:"px-4 py-3 text-right",children:"Run"})]})}),e.jsx("tbody",{className:"divide-y divide-slate-100",children:w.map(t=>{const r=ne[t.status||"active"]||ne.active,m=t._account_status&&t._account_status!=="active"?be[t._account_status]:null;return e.jsxs("tr",{className:"hover:bg-slate-50 cursor-pointer",onClick:()=>I({...t}),children:[e.jsxs("td",{className:"px-4 py-3",children:[e.jsx("div",{className:"font-bold text-slate-900",children:t._client_name||"â"}),e.jsx("div",{className:"text-xs text-slate-500",children:t._client_email||"no email set"}),m&&e.jsx("span",{className:"inline-block mt-1 text-[12px] font-bold tracking-widest uppercase px-2 py-0.5 rounded-full text-white",style:{background:m.color},children:m.label})]}),e.jsxs("td",{className:"px-4 py-3",children:[e.jsx("div",{className:"text-slate-800",children:t.label||"â"}),e.jsxs("div",{className:"text-xs text-slate-500",children:[te[t.cycle||"monthly"]," · day ",t.bill_day," · net ",t.net_days]}),e.jsx("span",{className:"inline-block mt-1 text-[12px] font-bold tracking-widest uppercase px-2 py-0.5 rounded-full text-white",style:{background:r.color},children:r.label})]}),e.jsx("td",{className:"px-4 py-3 text-right font-mono font-bold text-slate-900",children:S(t._total_cents||0,t.currency)}),e.jsxs("td",{className:"px-4 py-3",children:[e.jsx("div",{className:t._next_is_past?"font-bold text-amber-700":"text-slate-800",children:Q(t.next_run_on)}),t._next_is_past&&e.jsx("div",{className:"text-[13px] text-amber-700",children:"in the past â will be rescheduled forward"})]}),e.jsx("td",{className:"px-4 py-3",children:t._last_invoice?e.jsxs(e.Fragment,{children:[e.jsx("div",{className:"text-slate-800 font-mono text-xs",children:t._last_invoice.invoice_number}),e.jsxs("div",{className:"text-xs text-slate-500",children:[t._last_invoice.status," · ",S(t._last_invoice.total_cents,t._last_invoice.currency)]})]}):e.jsx("span",{className:"text-slate-400",children:"none yet"})}),e.jsx("td",{className:"px-4 py-3",children:e.jsx(pe,{cfg:t})}),e.jsx("td",{className:"px-4 py-3 align-top",onClick:c=>c.stopPropagation(),children:t._card?e.jsxs("div",{className:"text-[13px] space-y-1.5",children:[e.jsxs("div",{className:"font-bold text-slate-800",children:["ð³ ",Y(t._card)]}),t._card.exp_month&&t._card.exp_year&&e.jsxs("div",{className:"text-slate-400",children:["exp ",String(t._card.exp_month).padStart(2,"0"),"/",String(t._card.exp_year).slice(-2)]}),e.jsx("button",{onClick:()=>a(t),disabled:!!i,className:`block font-bold px-2.5 py-1 rounded-full disabled:opacity-40 ${t._card.auto_charge?"bg-emerald-100 text-emerald-800 border border-emerald-300":"bg-slate-100 text-slate-500 border border-slate-200"}`,title:t._card.auto_charge?`Charged on day ${t.bill_day} every month`:"Saved, but nothing is charged automatically",children:i===t.id+":toggle"?"â¦":t._card.auto_charge?`â auto-charge day ${t.bill_day}`:"â auto-charge off"}),t._card.last_charge&&e.jsx("div",{className:t._card.last_charge.ok?"text-emerald-700":"text-red-700",children:t._card.last_charge.ok?`â charged ${S(t._card.last_charge.amount_cents||0,t.currency)}`:`â ${String(t._card.last_charge.error||"declined").slice(0,60)}`}),e.jsxs("div",{className:"flex gap-1.5 pt-0.5 flex-wrap",children:[e.jsx("button",{onClick:()=>l(t),disabled:!!i||!t._last_invoice,className:"font-bold px-2 py-1 rounded-lg border border-slate-200 text-slate-600 hover:border-teal disabled:opacity-40",title:t._last_invoice?`Charge ${t._last_invoice.invoice_number} to this card right now`:"No invoice yet â run the arrangement first",children:i===t.id+":charge_now"?"â¦":"Charge now"}),e.jsx("button",{onClick:()=>_(t),disabled:!!i,className:"font-bold px-2 py-1 rounded-lg border border-emerald-200 text-emerald-700 hover:border-emerald-400 disabled:opacity-40",title:"Charge any amount to this card right now â a real invoice is created and marked paid, so the money has a paper trail. Built for testing your own card end-to-e
29nd.",children:i===t.id+":charge_custom"?"â¦":"â¡ Customâ¦"}),e.jsx("button",{onClick:()=>B(t),disabled:!!i,className:"font-bold px-2 py-1 rounded-lg border border-slate-200 text-slate-500 hover:border-teal disabled:opacity-40",title:"Send them a link to replace the card",children:"Replace"}),e.jsx("button",{onClick:()=>n(t),disabled:!!i,className:"font-bold px-2 py-1 rounded-lg border border-slate-200 text-slate-400 hover:border-red-300 hover:text-red-600 disabled:opacity-40",children:"Remove"})]})]}):e.jsxs("div",{className:"text-[13px]",children:[e.jsx("div",{className:"text-slate-400 mb-1.5",children:"no card"}),e.jsx("button",{onClick:()=>B(t),disabled:!!i,className:"font-bold px-2.5 py-1.5 rounded-lg border border-slate-200 text-slate-600 hover:border-teal disabled:opacity-40",title:"A Stripe page they can enter a card on. We never see the number.",children:i===t.id+":link"?"â¦":"Get card link"})]})}),e.jsx("td",{className:"px-4 py-3",children:e.jsxs("div",{className:"flex flex-col gap-1 text-[13px]",children:[e.jsx("span",{className:t.auto_send?"text-teal-700 font-bold":"text-slate-400",children:t.auto_send?"â auto-send":"â drafts only"}),e.jsx("span",{className:t.auto_restrict?"text-red-700 font-bold":"text-slate-400",children:t.auto_restrict?"â auto-restrict (phase 7)":"â never restrict"})]})}),e.jsxs("td",{className:"px-4 py-3 text-right whitespace-nowrap",onClick:c=>c.stopPropagation(),children:[e.jsx("button",{onClick:()=>F(t,!0),disabled:!!x,className:"text-[13px] font-bold px-2.5 py-1.5 rounded-lg border border-slate-200 text-slate-600 hover:border-teal disabled:opacity-40",title:"Say what would happen. Writes n
29othing.",children:x===t.id+":dry"?"â¦":"Dry run"}),e.jsx("button",{onClick:()=>F(t,!1),disabled:!!x||t.status!=="active",className:`ml-1.5 text-[13px] font-bold px-2.5 py-1.5 rounded-lg text-white disabled:opacity-40 ${t.auto_send?"bg-red-600 hover:bg-red-700":"bg-brand-500 hover:bg-brand-600"}`,title:t.auto_send?"Creates the invoice AND emails the client":"Creates a draft. Sends nothing.",children:x===t.id?"â¦":t.auto_send?"Run + send":"Run â draft"}),e.jsxs("div",{className:"mt-1.5 flex justify-end gap-1.5",children:[e.jsx("button",{onClick:()=>q(t,!0),disabled:!!x,className:"text-[13px] font-bold px-2.5 py-1.5 rounded-lg border border-slate-200 text-slate-600 hover:border-teal disabled:opacity-40",title:"Who would be emailed today, and what it would say. Sends nothing.",children:x===t.id+":rdry"?"â¦":"Preview reminders"}),e.jsx("button",{onClick:()=>q(t,!1),disabled:!!x||t.reminders_enabled===!1||t.status!=="active",className:"text-[13px] font-bold px-2.5 py-1.5 rounded-lg text-white bg-amber-600 hover:bg-amber-700 disabled:opacity-40",title:"Sends today's reminders for this arrangement, now.",children:x===t.id+":rem"?"â¦":"Send now"})]})]})]},t.id)})})]})}):e.jsxs("div",{className:"p-10 text-center text-slate-500",children:[e.jsx("div",{className:"text-3xl mb-2",children:"ð"}),e.jsx("div",{className:"font-bold text-slate-700",children:"No recurring bills yet."}),e.jsx("div",{className:"text-sm mt-1",children:"Start with one client you invoice for the same amount every month."})]})]}),L&&e.jsx(ge,{cfg:L,uid:o,clients:H,pick:s,biz:M,onClose:()=>I(null),onSaved:t=>{I(null),u(t),P(o)},onSavedStay:(t,r)=>{u(r),P(o),I(t)}},L.id||"new")]}):e.jsx("div",{className:"p-10 text-center text-slate-500",children:"Loadingâ¦"})}function ge({cfg:o,uid:b,clients:E,pick:f,biz:w,onClose:R,onSaved:H,onSavedStay:v}){var g,B;const[s,O]=y.useState({...o,line_items:(g=o.line_items)!=null&&g.length?o.line_items:[V()]}),[M,D]=y.useState(!1),[W,$]=y.useState(null),[A,N]=y.useState(null),[k,p]=y.useState(!1),[L,I]=y.useState(!1),u=a=>O(l=>({...l,...a})),x="w-full px-3 py-2 border border-slate-300 rounded-lg text-sm outline-none focus:border-teal-500",T=s.line_items||[],F=y.useMemo(()=>{let a=0;for(const n of T)a+=(Number(n.qty)||0)*(Number(n.unit_price_cents)||0);const l=Math.round(a*((Number(s.tax_pct)||0)/100));return{subtotal:a,tax:l,total:a+l}},[T,s.tax_pct]);async function q(){p(!0),$(null);try{const a=await fetch("/.netlify/functions/billing-configs",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({action:"preview",actor_user_id:b,count:3,config:s})}),l=await a.json().catch(()=>({}));if(!a.ok||l.error)throw new Error(l.message||l.error||`HTTP ${a.status}`);N(l.runs||[])}catch(a){$((a==null?void 0:a.message)||"Preview failed")}p(!1)}async function i(){D(!0),$(null);try{const a=await fetch("/.netlify/functions/billing-configs",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({action:"save",actor_user_id:b,config:s})}),l=await a.json().catch(()=>({}));if(!a.ok||l.error)throw new Error(l.message||l.error||`HTTP ${a.status}`);l.created&&v&&l.config?(D(!1),v(l.config,"â Created â nothing has been sent. The drawer stayed open: attach their invoices, get the card link, then arm it.")):H(l.created?"Recurring bill created. Nothing has been sent.":"Saved.")}catch(a){$((a==null?void 0:a.message)||"Save failed"),D(!1)}}async function d(){D(!0),$(null);try{const a=await fetch("/.netlify/functions/billing-configs",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({action:"delete",actor_user_id:b,id:s.id})}),l=await a.json().catch(()=>({}));if(!a.ok||l.error)throw new Error(l.message||l.error||`HTTP ${a.status}`);H("Ended. Its history is kept.")}catch(a){$((a==null?void 0:a.message)||"Could not end it"),D(!1)}}return ce.createPortal(e.jsx("div",{className:"fixed inset-0 z-[100060] bg-slate-900/60 backdrop-blur-sm flex items-stretch justify-end",onClick:R,children:e.jsxs("div",{className:"bg-white w-full max-w-3xl h-full flex flex-col",onClick:a=>a.stopPropagation(),children:[e.jsxs("div",{className:"shrink-0 text-white p-5 flex items-start justify-between gap-3",style:{background:"#1F6FEB"},children:[e.jsxs("div",{className:"flex-1 min-w-0",children:[e.jsx("div",{className:"text-[12px] uppercase tracking-widest font-bold opacity-80",children:s.id?"Edit arrangement":"New recurring bill"}),e.jsx("div",{className:"font-display text-xl font-extrabold truncate",children:s.label||s._client_name||"Untitled"}),e.jsxs("div",{className:"text-xs opacity-90 mt-0.5",children:[S(F.total,s.currency)," · ",te[s.cycle||"monthly"]]})]}),e.jsx("button",{onClick:R,className:"text-white/80 hover:text-white text-2xl leading-none shrink-0",children:"Ã"})]}),e.jsxs("div",{className:"flex-1 min-h-0 overflow-y-auto p-5 space-y-5",style:{overscrollBehavior:"contain",WebkitOverflowScrolling:"touch",paddingBottom:"max(20px, env(safe-area-inset-bottom, 0px))"},children:[e.jsx("div",{className:"rounded-xl border border-slate-200 p-4",children:e.jsxs("div",{className:"flex items-center gap-2 flex-wrap",children:[e.jsx("div",{className:"text-[12px] font-bold tracking-widest text-slate-500 uppercase mr-2",children:"Type"}),[["rec","ð Recurring","bills every cycle on the bill day"],["once","1ï¸â£ One-time","bills once on the start date, then closes itself"]].map(([a,l,n])=>{const _=!!s.start_on&&s.end_on===s.start_on,P=a==="once"?_:!_;return e.jsx("button",{onClick:()=>
29{if(a==="once"){const z=s.start_on||new Date().toLocaleDateString("en-CA"),X=Math.min(Number(z.slice(8,10))||1,28);u({start_on:z,end_on:z,bill_day:X,cycle:"monthly"})}else u({end_on:null})},className:`px-3 py-2 rounded-xl text-[13.5px] font-extrabold border-2 transition ${P?"bg-teal-600 border-teal-600 text-white":"bg-white border-slate-200 text-slate-500 hover:border-teal-300"}`,title:n,children:l},a)}),e.jsx("span",{className:"text-[13px] text-slate-400 ml-1",children:s.start_on&&s.end_on===s.start_on?"One invoice, on the start date â collect their usage or old invoices in a single charge.":"An invoice every cycle until you end it."})]})}),s.id&&e.jsxs("div",{className:"rounded-xl border border-teal-200 bg-teal-50/50 p-4 text-[13.5px] text-slate-700 leading-relaxed",children:[e.jsx("div",{className:"text-[12px] font-bold tracking-widest text-teal-700 uppercase mb-1.5",children:"What happens next"}),s.next_run_on?e.jsxs(e.Fragment,{children:["Invoice ",e.jsx("b",{children:S(F.total,s.currency)})," is created ",s.start_on&&s.end_on===s.start_on?e.jsxs(e.Fragment,{children:["once, on ",e.jsx("b",{children:s.next_run_on}),", then this closes itself"]}):e.jsxs(e.Fragment,{children:["on ",e.jsx("b",{children:s.next_run_on})," and every ",te[s.cycle||"monthly"].toLowerCase()," after"]}),"."," ",s.auto_send?e.jsxs(e.Fragment,{children:["It is ",e.jsx("b",{children:"emailed automatically"}),s.send_sms?" (+SMS)":""," with a pay link."]}):e.jsxs(e.Fragment,{children:["It lands as a ",e.jsx("b",{children:"draft"})," â you review it in Business Hub â Invoices before anything is sent."]})," ","With a card on file + auto-charge armed on the row, the card is charged that morning (heads-up emails go 7 days and 1 day before), and any invoices attached above are collected right after."]}):e.jsxs(e.Fragment,{children:["This arrangement is ",s.status||"active"," with no next run scheduled",s.status==="ended"?" â it has finished":"","."]})," ","Use ",e.jsx("b",{children:"ð Preview the next 3"})," below to see the exact dates, or ",e.jsx("b",{children:"â¶ Run now"})," on the row to make this month's invoice immediately."]}),e.jsx(U,{title:"Who is being billed",children:e.jsxs("div",{className:"grid sm:grid-cols-2 gap-3",children:[e.jsxs("div",{className:"sm:col-span-2",children:[e.jsx(j,{children:"Client"}),e.jsxs("select",{className:x+" bg-white",value:s.client_user_id?"u:"+s.client_user_id:s.client_care_id?"c:"+s.client_care_id:s.bill_email&&f.recipients.some(a=>String(a.bill_email||"").toLowerCase()===String(s.bill_email).toLowerCase())?"r:"+String(s.bill_email).toLowerCase():"",onChange:a=>{const l=a.target.value;if(!l){u({client_user_id:null,client_care_id:null});return}if(l.startsWith("u:")){const n=(f.logins.length?f.logins:E).find(_=>_.id===l.slice(2));u({client_user_id:l.slice(2),client_care_id:null,bill_company:(n==null?void 0:n.name)||(n==null?void 0:n.business_name)||s.bill_company,bill_to:(n==null?void 0:n.full_name)||s.bill_to,bill_email:(n==null?void 0:n.email)||s.bill_email,bill_phone:(n==null?void 0:n.phone)||s.bill_phone,bill_address:(n==null?void 0:n.address)||s.bill_address,label:s.label||(n==null?void 0:n.name)||(n==null?void 0:n.business_name)||""})}else if(l.startsWith("c:")){const n=f.care.find(_=>_.id===l.slice(2));u({client_care_id:l.slice(2),client_user_id:null,bill_to:(n==null?void 0:n.name)||s.bill_to,bill_email:(n==null?void 0:n.email)||s.bill_email,bill_phone:(n==null?void 0:n.phone)||s.bill_phone,bill_address:(n==null?void 0:n.address)||s.bill_address,label:s.label||(n==null?void 0:n.name)||""})}else if(l.startsWith("r:")){const n=f.recipients.find(_=>String(_.bill_email||"").toLowerCase()===l.slice(2));u({client_user_id:null,client_care_id:null,bill_company:(n==null?void 0:n.bill_company)||s.bill_company,bill_to:(n==null?void 0:n.bill_to)||s.bill_to,bill_email:(n==null?void 0:n.bill_email)||s.bill_email,bill_phone:(n==null?void 0:n.bill_phone)||s.bill_phone,bill_address:(n==null?void 0:n.bill_address)||s.bill_address,label:s.label||(n==null?void 0:n.bill_company)||(n==null?void 0:n.bill_to)||""})}},children:[e.jsx("option",{value:"",children:"â none / type the details below â"}),f.recipients.length>0&&e.jsx("optgroup",{label:"ð¼ Your billing list â invoiced before",children:f.recipients.map(a=>e.jsx("option",{value:"r:"+String(a.bill_email).toLowerCase(),children:(a.bill_company||a.bill_to||a.bill_email)+` â ${a.invoice_count} invoice${a.invoice_count===1?"":"s"}`+(a.open_cents>0?` · ${S(a.open_cents,a.currency)} open`:"")},"r"+a.bill_email))}),f.care.length>0&&e.jsx("optgroup",{label:"Command Center clients",children:f.care.map(a=>e.jsx("option",{value:"c:"+a.id,children:a.name},"c"+a.id))}),f.logins.length>0&&e.jsx("optgroup",{label:"All sign-in accounts (incl. test accounts)",children:[...f.logins].sort((a,l)=>String(a.name).localeCompare(String(l.name))).map(a=>e.jsx("option",{value:"u:"+a.id,children:a.name},"u"+a.id))}),!f.logins.length&&E.map(a=>e.jsx("option",{value:"u:"+a.id,children:a.business_name||a.full_name||a.email},a.id))]}),e.jsx(J,{children:"Every client you can bill, from all three places they live: accounts with a login, Command Center clients, and anyone you have already invoiced by email. Picking one prefills everything below â or skip it and type the details by hand."})]}),e.jsxs("div",{children:[e.jsx(j,{children:"Company"}),e.jsx("input",{className:x,value:s.bill_company||"",onChange:a=>u({bill_company:a.target.value}),placeholder:"Peace Services Inc."})]}),e.jsxs("div",{children:[e.jsx(j,{children:"Attention / contact"}),e.jsx("input",{className:x,value:s.bill_to||"",onChange:a=>u({bill_to:a.target.value}),placeholder:"Accounts payable"})]}),e.jsxs("div",{children:[e.jsx(j,{children:"Billing email"}),e.jsx("input",{className:x,value:s.bill_email||"",onChange:a=>u({bill_email:a.target.value}),placeholder:"[email protected]"})]}),e.jsxs("div",{children:[e.jsx(j,{children:"Phone"}),e.jsx("input",{className:x,value:s.bill_phone||"",onChange:a=>u({bill_phone:a.target.value}),placeholder:"(647) 000-0000"})]}),e.jsxs("div",{className:"sm:col-span-2",children:[e.jsx(j,{children:"Billing address"}),e.jsx("input",{className:x,value:s.bill_address||"",onChange:a=>u({bill_address:a.target.value}),placeholder:"123 Main St, Suite 4, Mississauga ON L5V 2B4"}),e.jsx(J,{children:"B2537 â an invoice with no postal address is one an accounts department cannot file, and a recurring invoice is the one most likely to reach a clerk who has never spoken to us."})]}),e.jsxs("div",{className:"sm:col-span-2",children:[e.jsx(j,{children:"Label (for your list, not the client's)"}),e.jsx("input",{className:x,value:s.label||"",onChange:a=>u({label:a.target.value}
29),placeholder:"Peace Services â Receptionist + Marketing"})]})]})}),e.jsx(ye,{cfgId:s.id||"",careId:s.client_care_id||"",billEmail:s.bill_email||"",billCompany:s.bill_company||"",uid:b,currency:s.currency||"CAD",onUseLines:a=>{const l=Array.isArray(a.line_items)&&a.line_items.length?a.line_items.map(n=>({description:String(n.description||""),qty:Number(n.qty)||1,unit_price_cents:Math.round(Number(n.unit_price_cents)||0),...n.list_price_cents==null?{}:{list_price_cents:Math.round(Number(n.list_price_cents)||0)}})):[{description:`As per ${a.invoice_number}`,qty:1,unit_price_cents:Number(a.total_cents)||0}];u({line_items:l,currency:String(a.currency||s.currency||"cad").toLowerCase()})}}),e.jsxs(U,{title:"What they are billed for",children:[e.jsxs("div",{className:"hidden sm:grid grid-cols-12 gap-2 px-1 text-[12px] font-bold tracking-wider text-slate-400 uppercase mb-1",children:[e.jsx("div",{className:"sm:col-span-6",children:"Description"}),e.jsx("div",{className:"sm:col-span-2 text-center",children:"Qty"}),e.jsx("div",{className:"sm:col-span-3 text-right",children:"Price"}),e.jsx("div",{className:"sm:col-span-1"})]}),e.jsx("div",{className:"space-y-2",children:T.map((a,l)=>e.jsxs("div",{className:"grid grid-cols-12 gap-2 items-start",children:[e.jsx("input",{value:a.description,placeholder:"What are you charging for?",onChange:n=>u({line_items:T.map((_,P)=>P===l?{..._,description:n.target.value}:_)}),className:"col-span-12 sm:col-span-6 px-3 py-2 border border-slate-300 rounded-lg text-sm outline-none focus:border-teal-500"}),e.jsx("input",{type:"number",min:0,value:a.qty,placeholder:"Qty",onChange:n=>u({line_items:T.map((_,P)=>P===l?{..._,qty:Number(n.target.value)}:_)}),className:"col-span-3 sm:col-span-2 px-3 py-2 border border-slate-300 rounded-lg text-sm outline-none focus:border-teal-500"}),e.jsx("input",{type:"number",step:"0.01",value:a.unit_price_cents/100||"",placeholder:"Price",title:"A discount or credit is a negative amount, e.g. -500",onChange:n=>u({line_items:T.map((_,P)=>P===l?{..._,unit_price_cents:Math.round(Number(n.target.value)*100)}:_)}),className:"col-span-7 sm:col-span-3 px-3 py-2 border border-slate-300 rounded-lg text-sm outline-none focus:border-teal-500"}),e.jsx("button",{onClick:()=>u({line_items:T.length>1?T.filter((n,_)=>_!==l):[V()]}),title:"Remove line",className:"col-span-2 sm:col-span-1 px-2 py-2 rounded-lg text-slate-400 hover:text-red-600 hover:bg-red-50 text-lg leading-none",children:"Ã"})]},l))}),e.jsx("button",{onClick:()=>u({line_items:[...T,V()]}),className:"mt-2 text-sm font-bold text-teal-700 hover:text-teal-900",children:"+ Add a line"}),e.jsxs("div",{className:"grid sm:grid-cols-2 gap-3 mt-4",children:[e.jsxs("div",{children:[e.jsx(j,{children:"Currency"}),e.jsxs("select",{className:x+" bg-white",value:s.currency||"cad",onChange:a=>u({currency:a.target.value}),children:[e.jsx("option",{value:"cad",children:"CAD"}),e.jsx("option",{value:"usd",children:"USD"}),e.jsx("option",{value:"gbp",children:"GBP"}),e.jsx("option",{value:"aud",children:"AUD"})]})]}),e.jsxs("div",{children:[e.jsx(j,{children:"Tax %"}),e.jsx("input",{type:"number",step:"0.001",className:x,value:s.tax_pct??0,onChange:a=>u({tax_pct:Number(a.target.value)})})]})]}),e.jsxs("div",{className:"rounded-xl bg-slate-50 border border-slate-200 p-3 mt-3 space-y-1",children:[e.jsx(ee,{k:"Subtotal",v:S(F.subtotal,s.currency)}),e.jsx(ee,{k:`Tax (${s.tax_pct||0}%)`,v:S(F.tax,s.currency)}),e.jsx("div",{className:"pt-1 border-t border-slate-200",children:e.jsx(ee,{k:"Every invoice",v:S(F.total,s.currency)})})]})]}),e.jsxs(U,{title:"When",children:[e.jsxs("div",{className:"grid sm:grid-cols-2 gap-3",children:[e.jsxs("div",{children:[e.jsx(j,{children:"Cycle"}),e.jsxs("select",{className:x+" bg-white",value:s.cycle||"monthly",onChange:a=>u({cycle:a.target.value}),children:[e.jsx("option",{value:"monthly",children:"Monthly"}),e.jsx("option",{value:"quarterly",children:"Quarterly"}),e.jsx("option",{value:"annual",children:"Annual"})]})]}),e.jsxs("div",{children:[e.jsx(j,{children:"Bill on day"}),e.jsx("input",{type:"number",min:1,max:28,className:x,value:s.bill_day??1,onChange:a=>u({bill_day:Number(a.target.value)})}),e.jsx(J,{children:'1â28 only. "The 30th" does not exist in February, and a billing system that skips a month once a year is one nobody trusts again.'})]}),e.jsxs("div",{children:[e.jsx(j,{children:"Payment terms (net days)"}),e.jsx("input",{type:"number",min:0,className:x,value:s.net_days??14,onChange:a=>u({net_days:Number(a.target.value)})})]}),e.jsxs("div",{children:[e.jsx(j,{children:"Status"}),e.jsxs("select",{className:x+" bg-white",value:s.status||"active",onChange:a=>u({status:a.target.value}),children:[e.jsx("option",{value:"active",children:"Active"}),e.jsx("option",{value:"paused",children:"Paused"}),e.jsx("option",{value:"ended",children:"Ended"})]})]}),e.jsxs("div",{children:[e.jsx(j,{children:"Starts"}),e.jsx("input",{type:"date",className:x,value:s.start_on||"",onChange:a=>u({start_on:a.target.value})})]}),e.jsxs("div",{children:[e.jsx(j,{children:"Ends (optional)"}),e.jsx("input",{type:"date",className:x,value:s.end_on||"",onChange:a=>u({end_on:a.target.value||null})})]})]}),e.jsx("button",{onClick:q,disabled:k,className:"mt-3 px-5 py-2.5 rounded-xl font-bold text-sm border-2 border-slate-300 text-slate-800 hover:border-teal-500 disabled:opacity-40",children:k?"Workingâ¦":"ð Preview the next 3"}
29),e.jsx(J,{children:"Writes nothing. This is the button that catches a wrong bill day before a client sees it."}),A&&e.jsx("div",{className:"mt-3 rounded-xl border border-slate-200 overflow-hidden",children:e.jsxs("table",{className:"w-full text-sm",children:[e.jsx("thead",{className:"bg-slate-50 text-[13px] uppercase tracking-widest text-slate-500 font-bold",children:e.jsxs("tr",{children:[e.jsx("th",{className:"px-3 py-2 text-left",children:"Period"}),e.jsx("th",{className:"px-3 py-2 text-left",children:"Invoice created"}),e.jsx("th",{className:"px-3 py-2 text-left",children:"Due"}),e.jsx("th",{className:"px-3 py-2 text-right",children:"Amount"})]})}),e.jsx("tbody",{className:"divide-y divide-slate-100",children:A.map(a=>e.jsxs("tr",{className:a.past_end?"opacity-40":"",children:[e.jsx("td",{className:"px-3 py-2 font-mono text-xs",children:a.period_key}),e.jsx("td",{className:"px-3 py-2",children:Q(a.run_on)}),e.jsxs("td",{className:"px-3 py-2",children:[Q(a.due_on),a.past_end&&e.jsx("span",{className:"ml-2 text-[13px] text-slate-500",children:"after end date"})]}),e.jsx("td",{className:"px-3 py-2 text-right font-mono",children:S(F.total,s.currency)})]},a.period_key))})]})})]}
29),e.jsxs(U,{title:"How it goes out",children:[e.jsx(K,{on:!!s.auto_send,onChange:a=>u({auto_send:a}),label:"Send it automatically",hint:"Off means the generator creates a DRAFT and stops. Use off for the first cycle of any new client â look at what it produced before a client does."}),e.jsx(K,{on:s.send_email!==!1,onChange:a=>u({send_email:a}),label:"By email",hint:""}),e.jsx(K,{on:!!s.send_sms,onChange:a=>u({send_sms:a}),label:"Also by SMS",hint:""}),e.jsxs("div",{className:"mt-3",children:[e.jsx(j,{children:"Notes (copied onto every invoice)"}),e.jsx("textarea",{className:x,rows:2,value:s.notes||"",onChange:a=>u({notes:a.target.value})})]})]}),e.jsxs(U,{title:"Chasing it",children:[e.jsx(K,{on:s.reminders_enabled!==!1,onChange:a=>u({reminders_enabled:a}),label:"Send reminders",hint:"Live. One email per client per day, at 10am Toronto â two invoices hitting a reminder day together arrive as one email, never two."}),e.jsxs("div",{className:"grid sm:grid-cols-2 gap-3 mt-2",children:[e.jsxs("div",{children:[e.jsx(j,{children:"Days before due"}),e.jsx("input",{className:x,value:(s.reminder_days||[]).join(", "),onChange:a=>u({reminder_days:a.target.value.split(",").map(l=>Number(l.trim())).filter(l=>Number.isFinite(l)&&l>=0)}),placeholder:"14, 7, 1"}),e.jsx(J,{children:"14, 7 and 1 only. A heads-up, no urgency language. The due date itself always gets one."})]}),e.jsxs("div",{children:[e.jsx(j,{children:"Days after due"}),e.jsx("input",{className:x,value:(s.overdue_days||[]).join(", "),onChange:a=>u({overdue_days:a.target.value.split(",").map(l=>Number(l.trim())).filter(l=>Number.isFinite(l)&&l>=0)}),placeholder:"leave empty at first"}),e.jsx(J,{children:"Empty by default, and that is the recommendation: watch the before-ladder run for a cycle first, then add 1, 7, 14. These emails are factual and never threaten â nothing gets switched off."})]})]}),!!(s._notifications||[]).length&&e.jsxs("div",{className:"mt-3 rounded-xl border border-slate-200 overflow-hidden",children:[e.jsx("div",{className:"px-3 py-2 bg-slate-50 text-[13px] uppercase tracking-widest text-slate-500 font-bold",children:"Already sent"}),e.jsx("div",{className:"divide-y divide-slate-100 max-h-52 overflow-y-auto",children:(s._notifications||[]).map(a=>e.jsxs("div",{className:"px-3 py-2 text-xs flex items-center justify-between gap-3",children:[e.jsxs("span",{className:a.status==="failed"?"text-red-700 font-bold":"text-slate-700",children:[a.status==="failed"?"â":"â"," ",ie[a.kind]||a.kind,a.error&&e.jsxs("span",{className:"font-normal text-red-600",children:[" â ",a.error]})]}),e.jsx("span",{className:"text-slate-400 whitespace-nowrap",children:Q(String(a.sent_at).slice(0,10))})]},a.id))})]})]}),e.jsxs(U,{title:"If they don't pay",children:[e.jsxs("div",{className:"rounded-xl bg-slate-50 border border-slate-200 p-3 text-xs text-slate-600 leading-relaxed mb-3",children:["Restricting an account stops it ",e.jsx("b",{children:"consuming new paid capacity"})," â no new generations, campaigns or add-on purchases. It does ",e.jsx("b",{children:"not"})," stop their phone being answered, take their website down, or block inbound conversations. That line is deliberate: their customer calls, nobody picks up, that caller books with a competitor â and paying on Friday does not bring Tuesday's caller back."]}),e.jsxs("div",{children:[e.jsx(j,{children:"Grace days past due before restricting"}),e.jsx("input",{type:"number",min:0,className:x,value:s.grace_days??7,onChange:a=>u({grace_days:Number(a.target.value)})})]}),e.jsx("div",{className:"mt-3",children:e.jsx(K,{on:!!s.auto_restrict,onChange:a=>u({auto_restrict:a}),label:"Let the system restrict this client automatically",hint:"Recommended: leave this off until the reminder ladder has run clean for two full cycles. Nothing reads this flag until phase 7."})})]}),e.jsx(U,{title:"Ways to pay",children:e.jsx(me,{blocks:s.pay_blocks||(s.bill_email?he(s.bill_email):ue()),onChange:a=>u({pay_blocks:a}),currency:s.currency||"cad"})}),e.jsxs("div",{children:[e.jsx("button",{onClick:()=>I(a=>!a),className:"text-sm font-bold text-teal-700 hover:text-teal-900",children:L?"â¾ Hide the invoice preview":"⸠Show the invoice they will receive"}),e.jsxs("div",{className:"text-[13px] text-slate-400 mt-0.5",children:["This is the invoice this arrangement will ",e.jsx("b",{children:"create each cycle"}),', built from the line items above â use ð â line items to make it match one you already sent. Already-sent invoices open from their number in "Their invoices" above, exactly as the client sees them.']}),L&&e.jsx("div",{className:"mt-3 border border-slate-200 rounded-xl overflow-hidden",children:e.jsx(xe,{id:"billing-cfg-preview",doc:{lines:T,notes:s.notes||"",taxPct:Number(s.tax_pct)||0,billTo:s.bill_company||s.bill_to||"",billEmail:s.bill_email||"",billPhone:s.bill_phone||"",dueDate:((B=A==null?void 0:A[0])==null?void 0:B.due_on)||"",currency:s.currency||"cad",pay:s.pay_blocks||null},biz:w})})]}),W&&e.jsxs("div",{className:"rounded-xl bg-red-50 border border-red-200 text-red-800 px-4 py-3 text-sm",children:["â ",W]})]}),e.jsxs("div",{className:"shrink-0 border-t border-slate-200 p-4 flex items-center gap-3 flex-wrap",children:[e.jsx("button",{onClick:i,disabled:M,className:"px-5 py-2.5 rounded-xl text-white font-bold text-sm disabled:opacity-40",style:{background:"#1F6FEB"},children:M?"Savingâ¦":s.id?"Save":"Create it"}),e.jsx("button",{onClick:R,className:"px-5 py-2.5 rounded-xl font-bold text-sm border-2 border-slate-300 text-slate-800 hover:border-slate-400",children:"Cancel"}),s.id&&e.jsx("button",{onClick:d,disabled:M,className:"ml-auto px-4 py-2 rounded-xl font-bold text-sm text-red-700 hover:bg-red-50 disabled:opacity-40",title:"Ends the arrangement and keeps its history. Nothing is deleted.",children:"End this arrangement"})]})]})}),document.body)}function ye({cfgId:o,careId:b,billEmail:E,billCompany:f,uid:w,currency:R,onUseLines:H}){const[v,s]=y.useState(null),[O,M]=y.useState(void 0),[D,W]=y.useState(new Set),[$,A]=y.useState(null),[N,k]=y.useState(null),p=(i,d)=>fetch(`/.netlify/functions/${i}`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({actor_user_id:w,...d})}).then(g=>g.json());y.useEffect(()=>{(async()=>{try{const[i,d]=await Promise.all([b||E||f?p("billing-configs",{action:"outstanding",client_care_id:b,bill_email:E,bill_company:f}):Promise.resolve({invoices:[]}),o?p("billing-card",{action:"status",config_id:o}):Promise.resolve({card:null})]);s(Array.isArray(i==null?void 0:i.invoices)?i.invoices:[]);const g=(d==null?void 0:d.card)||null;
29M(g),W(new Set(Array.isArray(g==null?void 0:g.collect_invoice_ids)?g.collect_invoice_ids:[]))}catch{s([]),M(null)}})()},[o,b,E,f]);const L=i=>`${R.toUpperCase()} $${(i/100).toFixed(2)}`;async function I(i){W(new Set(i)),A("save"),k(null);const d=await p("billing-card",{action:"collect",config_id:o,invoice_ids:Array.from(i)});d!=null&&d.ok?k(i.size?`${i.size} invoice${i.size===1?"":"s"} will be collected on the next bill day (heads-up notices include them).`:"Nothing attached."):k((d==null?void 0:d.message)||(d==null?void 0:d.error)||"Could not save â is a card on file?"),A(null)}async function u(i){if(!window.confirm(`Charge ${L(i.balance_cents)} for ${i.invoice_number} to the saved card RIGHT NOW?`))return;A(i.id),k(null);const d=await p("billing-card",{action:"charge_now",config_id:o,invoice_id:i.id});if(d!=null&&d.ok){k(`â ${i.invoice_number} â ${L(d.charged_cents)} charged to ${d.card||"the card"}.`),s(B=>(B||[]).filter(a=>a.id!==i.id));const g=new Set(D);g.delete(i.id),W(g)}else k(`â ${i.invoice_number}: ${(d==null?void 0:d.message)||(d==null?void 0:d.error)||"charge failed"}`);A(null)}const x=i=>(Array.isArray(O==null?void 0:O.collect_schedule)?O.collect_schedule:[]).find(d=>d.invoice_id===i)||null;async function T(i){const d=x(i.id);if(d&&window.confirm(`${i.invoice_number} is scheduled to charge on ${d.on}.${d.error?` 30Last attempt failed: ${d.error}`:""} 31 32OK = remove the schedule · Cancel = keep it`)){A(i.id);const n=await p("billing-card",{action:"schedule_collect",config_id:o,invoice_id:i.id,on:null});n!=null&&n.ok&&(M(n.card),k(`${i.invoice_number} â schedule removed.`)),A(null);return}if(d)return;const g=new Date;g.setDate(g.getDate()+1);const B=`${g.getFullYear()}-${String(g.getMonth()+1).padStart(2,"0")}-${String(g.getDate()).padStart(2,"0")}`,a=window.prompt(`Charge ${i.invoice_number} (${L(i.balance_cents)}) to the saved card on which date? 33YYYY-MM-DD â it runs that morning (10am Toronto), and the paid invoice emails automatically.`,B);if(!a)return;if(!/^\d{4}-\d{2}-\d{2}$/.test(a.trim())){k("Date must look like 2026-08-15.");return}A(i.id),k(null);const l=await p("billing-card",{action:"schedule_collect",config_id:o,invoice_id:i.id,on:a.trim()});l!=null&&l.ok?(M(l.card),k(`ð ${i.invoice_number} will be charged ${L(i.balance_cents)} on ${a.trim()}.`)):k((l==null?void 0:l.message)||(l==null?void 0:l.error)||"Could not schedule."),A(null)}const F=!!(b||E||f),q=(v||[]).filter(i=>D.has(i.id)).reduce((i,d)=>i+(d.balance_cents||0),0);return e.jsxs(U,{title:"Their invoices â attach, charge, or reuse",children:[!F&&e.jsx("div",{className:"text-[13.5px] text-slate-500",children:"Pick a client above (or type their billing email / company) â everything they still owe appears here."}),F&&v===null&&e.jsx("div",{className:"text-[13.5px] text-slate-400",children:"Looking up their unpaid invoicesâ¦"}),F&&v!==null&&!v.length&&e.jsx("div",{className:"text-[13.5px] text-slate-500",children:"No unpaid invoices for this client. ð"}),F&&!!(v!=null&&v.length)&&e.jsxs("div",{className:"space-y-1.5",children:[!o&&e.jsxs("div",{className:"text-[13.5px] text-sky-800 bg-sky-50 border border-sky-100 rounded-lg px-3 py-2",children:[v.length," unpaid invoice",v.length===1?"":"s"," found · ",L(v.reduce((i,d)=>i+(d.balance_cents||0),0))," open. Tick the ones this arrangement should collect â ",e.jsx("b",{children:"Create it"})," below, then send the card link; they ride the card from then on."]}),o&&O===null&&e.jsxs("div",{className:"text-[13.5px] text-amber-700 bg-amber-50 border border-amber-100 rounded-lg px-3 py-2",children:["No card on file on this arrangement yet â use ",e.jsx("b",{children:"Get card link"})," on its row first; attaching and charging both ride the card."]}),v.map(i=>{var d,g,B,a,l;return e.jsxs("div",{className:"flex items-center gap-2.5 bg-slate-50 border border-slate-200 rounded-xl px-3 py-2 flex-wrap",children:[e.jsx("input",{type:"checkbox",disabled:!O||!!$,checked:D.has(i.id),onChange:n=>{const _=new Set(D);n.target.checked?_.add(i.id):_.delete(i.id),I(_)},className:"w-4 h-4 accent-teal-600",title:O?"Collect this on the next bill day, on the card":"Save the arrangement + get a card on file first"}),e.jsxs("div",{className:"min-w-0",children:[e.jsxs("a",{href:`/pay/${i.id}`,target:"_blank",rel:"noopener",title:"Open the actual invoice â exactly what the client sees",className:"text-[13.5px] font-extrabold text-teal-700 hover:text-teal-900 hover:underline no-underline",children:[i.invoice_number," â"]}),e.jsxs("span",{className:"text-[13px] text-slate-400 ml-2",children:[i.due_date?`due ${i.due_date}`:""," · ",i.status]})]}),e.jsx("span",{className:"ml-auto text-[13.5px] font-black text-slate-900 tabular-nums",children:L(i.balance_cents)}),H&&e.jsx("button",{disabled:!!$,onClick:()=>{H(i),k(`Line items copied from ${i.invoice_number} â edit them below.`)},className:"px-2.5 py-1.5 rounded-lg border border-slate-300 text-slate-600 text-[13px] font-extrabold hover:border-teal-500 hover:text-teal-700 disabled:opacity-40",title:"Copy this invoice's line items into the arrangement below",children:"ð â line items"}),e.jsx("button",{disabled:!O||!!$,onClick:()=>T(i),className:`px-2.5 py-1.5 rounded-lg text-[13px] font-extrabold disabled:opacity-40 ${x(i.id)?(d=x(i.id
33))!=null&&d.error?"bg-rose-100 text-rose-700 border border-rose-300":"bg-sky-100 text-sky-800 border border-sky-300":"border border-slate-300 text-slate-600 hover:border-sky-500 hover:text-sky-700"}`,title:x(i.id)?(g=x(i.id))!=null&&g.error?`Last attempt failed: ${(B=x(i.id))==null?void 0:B.error} â retried daily; click to unschedule`:"Click to unschedule":"Charge this invoice on a specific date",children:$===i.id?"â¦":x(i.id)?`ð ${(a=x(i.id))==null?void 0:a.on}${(l=x(i.id))!=null&&l.error?" â ":""}`:"ð Schedule"}),e.jsx("button",{disabled:!O||!!$,onClick:()=>u(i),className:"px-2.5 py-1.5 rounded-lg bg-emerald-600 text-white text-[13px] font-extrabold disabled:opacity-40 hover:bg-emerald-700",children:$===i.id?"â¦":"â¡ Charge now"})]},i.id)}),D.size>0&&e.jsxs("div",{className:"text-[13.5px] font-bold text-teal-800 bg-teal-50 border border-teal-100 rounded-lg px-3 py-2",children:[D.size," attached · ",L(q)," will be collected on the next bill day, and the client sees them the moment they sign in."]}),e.jsx("div",{className:"text-[13px] text-slate-400",children:"Checked invoices are collected automatically on the next bill day â only after that month's own charge succeeds â and the 7-day / 24-hour heads-up emails state the full amount including them."})]}),N&&e.jsx("div",{className:"mt-2 text-[13.5px] font-bold text-slate-600",children:N})]})}function U({title:o,children:b}){return e.jsxs("div",{className:"rounded-xl border border-slate-200 p-4",children:[e.jsx("div",{className:"text-[12px] font-bold tracking-widest text-slate-500 uppercase mb-3",children:o}),b]})}function j({children:o}){return e.jsx("div",{className:"text-[12px] font-bold uppercase tracking-widest text-slate-500 mb-1",children:o})}function J({children:o}){return e.jsx("div",{className:"text-[13px] text-slate-500 mt-1 leading-relaxed",children:o})}function ee({k:o,v:b}){return e.jsxs("div",{className:"flex items-center justify-between text-xs text-slate-600",children:[e.jsx("span",{children:o}),e.jsx("span",{className:"font-bold text-slate-800",children:b})]})}function K({on:o,onChange:b,label:E,hint:f}){return e.jsxs("label",{className:"flex items-start gap-3 py-1.5 cursor-pointer",children:[e.jsx("input",{type:"checkbox",checked:o,onChange:w=>b(w.target.checked),className:"mt-0.5 w-4 h-4 accent-teal-600"}),e.jsxs("div",{className:"min-w-0",children:[e.jsx("div",{className:"text-sm font-bold text-slate-800",children:E}),f?e.jsx("div",{className:"text-[13px] text-slate-500 leading-relaxed",children:f}):null]})]})}export{ve 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.