1import{f as Le,a as f,u as te,d as P,j as e}from"./vendor-query-BEzYIaxM.js";import{F as Oe,C as T,d as F,e as q,f as B,h as E,B as b,I as $,k as N,S as Ke,m as Me,n as _e,o as Qe,p as Ge,q as be,aq as Ve,ar as He,O as Xe}from"./index-4XUKY91J.js";import{L as I}from"./label-CU7a6Lsd.js";import{B as H}from"./badge-B9piaVIr.js";import{T as Ye,a as We,b as ne,c as se}from"./tabs-D84XT4ZY.js";import{T as Ie,a as ke,b as ae,c as h,d as Ae,e as p}from"./table-C2aLMExA.js";import{A as U,b as G,a as L}from"./alert-DvQO5wQO.js";import{A as Ze}from"./admin-page-shell-D7gSCGmQ.js";import{C as Je}from"./confirm-destructive-action-BsoTAjjf.js";import{n as d}from"./notify-Doa-sG6h.js";import{s as ze}from"./reduced-motion-BjiZ4d_5.js";import{a4 as k,b4 as et,T as xe,cL as he,cv as ve,q as tt,b as pe}from"./lucide-icons-BcDNsZ1p.js";import"./vendor-radix-SunhD6jA.js";import"./admin-layout-CzYCNwhO.js";import"./command-DXwy8Hra.js";import"./popover-ljbi_JEA.js";import"./scroll-area-DHA5Fx9Q.js";import"./skeleton-Cj33XB9S.js";import"./use-presence-SIX3gqAX.js";import"./use-notifications-realtime-ChWF4XCC.js";import"./vendor-datefns-CFeoIw4J.js";import"./dialog-Nu8zs8Jt.js";import"./sheet-PcEElq1v.js";import"./separator-3GHXNH8N.js";import"./page-header-4ocXZdIC.js";import"./empty-state-DMNFyhsu.js";import"./error-state-Ctw9gyuf.js";import"./skeleton-shimmer-Dks2TZu5.js";import"./alert-dialog-DB5InOc2.js";import"./textarea-C8NaaKCW.js";import"./admin-destructive-action-DWQ8EfT6.js";const nt=["AED","AFN","ALL","AMD","ANG","AOA","ARS","AUD","AWG","AZN","BAM","BBD","BDT","BGN","BHD","BIF","BMD","BND","BOB","BRL","BSD","BTN","BWP","BYN","BZD","CAD","CDF","CHF","CLP","CNY","COP","CRC","CUP","CVE","CZK","DJF","DKK","DOP","DZD","EGP","ERN","ETB","EUR","FJD","FKP","GBP","GEL","GHS","GIP","GMD","GNF","GTQ","GYD","HKD","HNL","HRK","HTG","HUF","IDR","ILS","INR","IQD","IRR","ISK","JMD","JOD","JPY","KES","KGS","KHR","KMF","KPW","KRW","KWD","KYD","KZT","LAK","LBP","LKR","LRD","LSL","LYD","MAD","MDL","MGA","MKD","MMK","MNT","MOP","MRU","MUR","MVR","MWK","MXN","MYR","MZN","NAD","NGN","NIO","NOK","NPR","NZD","OMR","PAB","PEN","PGK","PHP","PKR","PLN","PYG","QAR","RON","RSD","RUB","RWF","SAR","SBD","SCR","SDG","SEK","SGD","SHP","SLL","SOS","SRD","SSP","STN","SVC","SYP","SZL","THB","TJS","TMT","TND","TOP","TRY","TTD","TWD","TZS","UAH","UGX","USD","UYU","UZS","VES","VND","VUV","WST","XAF","XCD","XOF","XPF","YER","ZAR","ZMW","ZWL"],st=new Set(nt);function ge(s){if(typeof s!="string")return!1;const a=s.trim().toUpperCase();return a.length!==3?!1:st.has(a)}const at="Currency must be a 3-letter ISO 4217 code (e.g. USD, EUR, GBP).",it="[INVOICE_FUTURE_DATED]",Se=()=>new Date().toISOString().split("T")[0],we=s=>{const a=new Date;return a.setDate(a.getDate()-s),a.toISOString().split("T")[0]};function rt({status:s}){const a={pending:"bg-amber-100 text-amber-800",processing:"bg-blue-100 text-blue-800",completed:"bg-emerald-100 text-emerald-800",failed:"bg-red-100 text-red-800"};return e.jsx(H,{className:a[s]??"bg-gray-100 text-gray-800",children:s})}const ct={quickbooks:"QuickBooks Online",xero:"Xero",netsuite:"NetSuite"};function V(s){return ct[s]??s}function Ht(){const s=Le(),o=Oe().sponsorName??"HaulProfit",[u,l]=f.useState(!1),n=typeof window<"u"?new URLSearchParams(window.location.search):new URLSearchParams,m=n.get("tab")??"settings",y=n.get("focus"),g=n.get("status")??(y?"failed":""),i=n.get("startDate")??we(30),v=n.get("endDate")??Se(),[c,R]=f.useState(m),[r,w]=f.useState("quickbooks"),[ie,De]=f.useState(""),[re,Pe]=f.useState(i),[ce,Te]=f.useState(v),[X,Ee]=f.useState(we(90)),[Y,$e]=f.useState(Se()),[M,Fe]=f.useState(g),[W,pt]=f.useState(y),[qe,Be]=f.useState({}),Z=te({queryKey:["/api/admin/accounting/settings",r],queryFn:async()=>await(await N("GET",`/api/admin/accounting/settings?provider=${r}`)).json()});f.useEffect(()=>{if(typeof window>"u")return;const t=new URLSearchParams(window.location.search),x=[];for(const C of["xero","netsuite"]){const D=t.get(C);if(!D)continue;x.push(C);const Q=t.get("reason"),me=C==="xero"?"Xero":"NetSuite";D==="connected"?(d.success({title:`${me} connected`,description:`${me} OAuth tokens saved successfully.`}),w(C),s.invalidateQueries({queryKey:["/api/admin/accounting/settings"]})):d.error({title:`Couldn't connect ${me}`,description:"Please try again in a moment.",cause:new Error(Q??"oauth_callback_error"),surface:"admin-accounting.oauth-callback"})}if(x.length>0){x.forEach(Q=>t.delete(Q)),t.delete("reason");const C=t.toString(),D=`${window.location.pathname}${C?`?${C}`:""}`;window.history.replaceState({},"",D)}},[]),f.useEffect(()=>{if(!W)return;const t=window.setTimeout(()=>{const x=document.querySelector(`[data-testid="row-job-${W}"]`);x&&ze(x,{behavior:"smooth",block:"center"})},350);return()=>window.clearTimeout(t)},[W,M,c]),f.useEffect(()=>{if(typeof window>"u")return;const t=new URLSearchParams(window.location.search),x=t.get("retry");if(!x)return;const C=t.get("focus");x==="success"?(d.success({title:"Retry queued",description:C?`Sync job ${C} has been re-enqueued.`:"Sync job has been re-enqueued."}),s.invalidateQueries({queryKey:["/api/admin/accounting/jobs"]}),s.invalidateQueries({queryKey:["/api/admin/accounting/reconciliation"]})):x==="expired"?d.warning({title:"Retry link expired",description:"The one-click retry link is older than the allowed window. Use the Retry button below to re-enqueue this job."}
1):x==="used"?d.warning({title:"Retry link already used",description:"This one-click retry link has already been used. Use the Retry button below if the job still needs another attempt."}):x==="error"?d.error({title:"Couldn't queue that retry",description:"We couldn't re-enqueue the job. Use the Retry button below to try again.",cause:new Error("retry_link_error"),surface:"admin-accounting.retry-link"}):d.warning({title:"Retry link invalid",description:"We couldn't verify that retry link. Use the Retry button below to re-enqueue this job."}),t.delete("retry");const D=t.toString(),Q=`${window.location.pathname}${D?`?${D}`:""}${window.location.hash}`;window.history.replaceState({},"",Q)},[]);const je=te({queryKey:["/api/admin/accounting/jobs",M],queryFn:async()=>{const t=M?`/api/admin/accounting/jobs?status=${M}&limit=200`:"/api/admin/accounting/jobs?limit=200";return await(await N("GET",t)).json()}}),j=te({queryKey:["/api/admin/accounting/reconciliation",re,ce],queryFn:async()=>await(await N("GET",`/api/admin/accounting/reconciliation?startDate=${re}&endDate=${ce}`)).json()}),fe=te({queryKey:["/api/admin/accounting/digest-runs"],queryFn:async()=>await(await N("GET","/api/admin/accounting/digest-runs?limit=25")).json()}),_=P({mutationFn:async()=>await(await N("POST","/api/admin/accounting/digest-runs")).json(),onSuccess:t=>{t.sent?d.success({title:"Digest sent",description:`Delivered to ${t.recipients} recipient${t.recipients===1?"":"s"}.`}):t.reason==="no_recipients"?d.warning({title:"No recipients configured",description:"Set RECONCILIATION_DIGEST_RECIPIENTS or mark at least one user as admin."}):t.reason==="send_error"?d.error({title:"Couldn't send the digest",description:"Please try again in a moment.",cause:new Error(t.error??"send_error"),surface:"admin-accounting.send-digest"}):d.info({title:"Digest skipped",description:t.reason??"Skipped."}),s.invalidateQueries({queryKey:["/api/admin/accounting/digest-runs"]})},onError:t=>d.error({title:"Couldn't send the digest",description:"Please try again in a moment.",cause:t,surface:"admin-accounting.send-digest"})}),ye=P({mutationFn:async t=>(await N("PUT",`/api/admin/accounting/settings?provider=${r}`,t)).json(),onSuccess:()=>{d.success({title:"Saved",description:"Accounting settings updated."}),s.invalidateQueries({queryKey:["/api/admin/accounting/settings"]})},onError:t=>d.error({title:"Couldn't save your changes",description:"Please try again in a moment.",cause:t,surface:"admin-accounting.save-settings"})}),J=P({mutationFn:async t=>(await N("POST",`/api/admin/accounting/jobs/${t}/retry`)).json(),onSuccess:()=>{d.success({title:"Retry queued"}),s.invalidateQueries({queryKey:["/api/admin/accounting/jobs"]})},onError:t=>d.error({title:"Couldn't queue that retry",description:"Please try again in a moment.",cause:t,surface:"admin-accounting.retry-job"})}),z=P({mutationFn:async()=>await(await N("POST","/api/admin/accounting/drain")).json(),onSuccess:t=>{d.success({title:"Sync complete",description:`Processed ${t.processed}, succeeded ${t.succeeded}, failed ${t.failed}`}),s.invalidateQueries({queryKey:["/api/admin/accounting/jobs"]}),s.invalidateQueries({queryKey:["/api/admin/accounting/reconciliation"]})},onError:t=>d.error({title:"Couldn't drain the sync queue",description:"Please try again in a moment.",cause:t,surface:"admin-accounting.drain-queue"})}),A=P({mutationFn:async()=>await(await N("POST","/api/admin/accounting/backfill",{startDate:X,endDate:Y})).json(),onSuccess:t=>{d.success({title:"Backfill queued",description:`${t.enqueued} export jobs enqueued.`}),s.invalidateQueries({queryKey:["/api/admin/accounting/jobs"]})},onError:t=>d.error({title:"Couldn't queue that backfill",description:"Please try again in a moment.",cause:t,surface:"admin-accounting.backfill"})}),oe=P({mutationFn:async()=>await(await N("GET",`/api/admin/accounting/chart-of-accounts?provider=${r}`)).json(),onSuccess:t=>{Be(x=>({...x,[t.provider]:t.accounts})),d.success({title:"Chart of accounts loaded",description:`Pulled ${t.accounts.length} accounts from ${V(t.provider)}.`})},onError:t=>d.error({title:"Couldn't load chart of accounts",description:"Please try again in a moment.",cause:t,surface:"admin-accounting.pull-chart"})}),de=P({mutationFn:async()=>await(await N("POST",`/api/admin/accounting/sync-partners?provider=${r}`)).json(),onSuccess:t=>{d.success({title:"Partners synced",description:`Matched ${t.matched}, created ${t.created} of ${t.total} customers from ${V(r)}.`})},onError:t=>d.error({title:"Couldn't sync partners",description:"Please try again in a moment.",cause:t,surface:"admin-accounting.sync-partners"})}),ee=Z.data?.settings,S=Z.data?.connection,le=ee?.enabled===1,Ue=Z.data?.availableProviders??["quickbooks","xero","netsuite"],ue=r==="quickbooks";return e.jsx(Ze,{title:"Accounting Export",description:"Push partner billing activity (invoices, payments, refunds, Stripe fees) into your books.",children:e.jsxs(Ye,{value:c,onValueChange:R,className:"space-y-4",children:[e.jsxs(We,{children:[e.jsx(ne,{value:"settings","data-testid":"tab-settings",children:"Settings"}),e.jsx(ne,{value:"reconciliation","data-testid":"tab-reconciliation",children:"Reconciliation"}),e.jsx(ne,{value:"jobs","data-testid":"tab-jobs",children:"Sync Log"}),e.jsx(ne,{value:"backfill","data-testid":"tab-backfill",children:"Backfill"})]}),e.jsxs(se,{value:"settings",children:[e.jsxs(T,{className:"mb-4","data-testid":"card-provider",children:[e.jsxs(F,{children:[e.jsx(q,{children:"Provider (current selection)"}),e.jsx(B,{children:"Choose which accounting system you're configuring. Each provider has independent settings, connection, and sync queue."})]}),e.jsx(E,{children:e.jsx("div",{className:"flex flex-wrap gap-2","data-testid":"provider-selector",children:Ue.map(t=>e.jsx(b,{type:"button",variant:t===r?"default":"outline",onClick:()=>w(t),"data-testid":`button-provider-${t}`,children:V(t)},t))})})]}),e.jsxs(T,{children:[e.jsxs(F,{children:[e.jsx(q,{children:V(r)}),e.jsxs(B,{children:["Connection status:"," ",S?.connected?e.jsxs(H,{
1className:"bg-emerald-100 text-emerald-800","data-testid":"badge-provider-connected",children:["Connected",S.companyName?` â ${S.companyName}`:""]}):e.jsx(H,{variant:"outline","data-testid":"badge-provider-disconnected",children:"Not connected"})]})]}),e.jsxs(E,{className:"space-y-4",children:[e.jsxs("div",{className:"flex flex-wrap items-end gap-2",children:[ue&&e.jsx(b,{type:"button",variant:S?.connected?"outline":"default",onClick:()=>{window.location.href="/api/quickbooks/auth"},"data-testid":"button-connect-qbo",children:S?.connected?"Reconnect QuickBooks":"Connect QuickBooks"}),r==="xero"&&e.jsx(b,{type:"button",variant:S?.connected?"outline":"default",onClick:()=>{window.location.href="/api/xero/auth"},"data-testid":"button-connect-xero",children:S?.connected?"Reconnect Xero":"Connect Xero"}),r==="netsuite"&&e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"flex flex-col",children:[e.jsx(I,{htmlFor:"netsuite-account-id",children:"NetSuite account ID"}),e.jsx($,{id:"netsuite-account-id",placeholder:"e.g. 1234567 or 1234567_SB1",value:ie,onChange:t=>De(t.target.value),className:"w-64","data-testid":"input-netsuite-account-id"})]}),e.jsx(b,{type:"button",variant:S?.connected?"outline":"default",disabled:!ie.trim(),onClick:()=>{const t=ie.trim();t&&(window.location.href=`/api/netsuite/auth?accountId=${encodeURIComponent(t)}`)},"data-testid":"button-connect-netsuite",children:S?.connected?"Reconnect NetSuite":"Connect NetSuite"})]}),ee?.connectedUserId&&e.jsx(b,{type:"button",variant:"outline",onClick:()=>de.mutate(),disabled:de.isPending,"data-testid":"button-sync-partners",children:de.isPending?"Syncingâ¦":`Pull customers from ${V(r)}`}),ee?.connectedUserId&&!ue&&e.jsxs(b,{type:"button",variant:"outline",onClick:()=>oe.mutate(),disabled:oe.isPending,"data-testid":"button-pull-chart-of-accounts",children:[oe.isPending?e.jsx(k,{className:"w-4 h-4 me-2 animate-spin"}):e.jsx(et,{className:"w-4 h-4 me-2"}),"Pull chart of accounts"]})]}),ue&&!S?.connected&&e.jsxs(U,{children:[e.jsx(xe,{className:"h-4 w-4"}),e.jsx(G,{children:"Connect QuickBooks first"}),e.jsx(L,{children:"Use the Connect button above to authorize QuickBooks Online with the admin account that owns your company books. After authorizing, return here to finish account mapping."})]}),r==="xero"&&!S?.connected&&e.jsxs(U,{children:[e.jsx(xe,{className:"h-4 w-4"}),e.jsx(G,{children:"Connect Xero first"}),e.jsxs(L,{children:["Click ",e.jsx("strong",{children:"Connect Xero"})," to authorize ",o," against your Xero organization. After authorizing, return here to enter the Xero account codes (Revenue / Fees / Refunds / etc.)."]})]}),r==="netsuite"&&!S?.connected&&e.jsxs(U,{children:[e.jsx(xe,{className:"h-4 w-4"}),e.jsx(G,{children:"Connect NetSuite first"}),e.jsxs(L,{children:["Enter your NetSuite account ID (the number shown in the NetSuite URL â e.g. ",e.jsx("code",{children:"1234567"})," ","or ",e.jsx("code",{children:"1234567_SB1"}),"), then click ",e.jsx("strong",{children:"Connect NetSuite"})," to authorize the SuiteTalk REST integration. After authorizing, return here to enter the NetSuite account internal IDs."]})]}),e.jsx(lt,{provider:r,settings:ee??null,currency:Z.data?.currency??null,enabled:le,onSave:t=>ye.mutate(t),saving:ye.isPending,chartAccounts:qe[r]??null},r)]})]})]}),e.jsxs(se,{value:"reconciliation",children:[e.jsxs(T,{children:[e.jsxs(F,{children:[e.jsx(q,{children:"Daily Reconciliation Summary"}),e.jsx(B,{children:"Compare HaulProfit billing to what was pushed to your books."})]}),e.jsxs(E,{className:"space-y-4",children:[e.jsxs("div",{className:"flex flex-wrap gap-4 items-end",children:[e.jsxs("div",{children:[e.jsx(I,{htmlFor:"recon-start",children:"Start"}),e.jsx($,{id:"recon-start",type:"date",value:re,onChange:t=>Pe(t.target.value),"data-testid":"input-recon-start"})]}),e.jsxs("div",{children:[e.jsx(I,{htmlFor:"recon-end",children:"End"}),e.jsx($,{id:"recon-end",type:"date",value:ce,onChange:t=>Te(t.target.value),"data-testid":"input-recon-end"})]}),e.jsxs(b,{onClick:()=>s.invalidateQueries({queryKey:["/api/admin/accounting/reconciliation"]}),variant:"outline","data-testid":"button-recon-refresh",children:[e.jsx(he,{className:"w-4 h-4 me-2"})," Refresh"]}),e.jsxs(b,{onClick:()=>l(!0),disabled:z.isPending,"data-testid":"button-drain",children:[z.isPending?e.jsx(k,{className:"w-4 h-4 me-2 animate-spin"}):e.jsx(ve,{className:"w-4 h-4 me-2"}),"Run sync now"]}),e.jsx(Je,{open:u,onOpenChange:l,title:"Drain accounting sync queue",summary:"This will immediately process every queued export job (invoices, payments, refunds, fees) against the connected accounting provider. Running this while another sync is in flight may produce duplicate API calls.",confirmationToken:"DRAIN",confirmButtonText:"Drain queue",isPending:z.isPending,testIdPrefix:"confirm-drain",onConfirm:()=>{z.mutate(void 0,{onSettled:()=>l(!1)})}})]}),j.isLoading?e.jsx(k,{className:"w-6 h-6 animate-spin"}):j.data?e.jsxs("div",{className:"grid grid-cols-2 md:grid-cols-4 gap-4",children:[e.jsx(O,{label:"Invoices issued",count:j.data.invoicesIssued.count,total:j.data.invoicesIssued.total,testId:"stat-invoices"}),e.jsx(O,{label:"Payments received",count:j.data.paymentsReceived.count,total:j.data.paymentsReceived.total,testId:"stat-payments"}),e.jsx(O,{label:"Refunds",count:j.data.refunds.count,total:j.data.refunds.total,testId:"stat-refunds"}
1),e.jsx(O,{label:"Stripe fees",count:j.data.stripeFees.count,total:j.data.stripeFees.total,testId:"stat-fees"}),e.jsx(O,{label:"Pending sync jobs",count:j.data.pendingJobs,testId:"stat-pending"}),e.jsx(O,{label:"Sync failures",count:j.data.syncFailures,testId:"stat-failures",emphasizeWarn:j.data.syncFailures>0})]}):null,j.data&&j.data.failedJobs.length>0&&e.jsxs("div",{children:[e.jsx("h4",{className:"font-medium mb-2 mt-6",children:"Recent failures"}),e.jsx(Ne,{jobs:j.data.failedJobs,onRetry:t=>J.mutate(t),retrying:J.isPending})]})]})]}),e.jsxs(T,{className:"mt-4","data-testid":"card-digest-history",children:[e.jsxs(F,{children:[e.jsx(q,{children:"Digest history"}),e.jsx(B,{children:'Recent reconciliation digest emails â when they were sent, to whom, summary metrics, and why they were skipped or failed. The scheduler runs once per day in the 13â15 UTC window; use "Send now" to retry a failure or send a one-off.'})]}),e.jsxs(E,{className:"space-y-4",children:[e.jsxs("div",{className:"flex flex-wrap gap-2",children:[e.jsxs(b,{variant:"outline",onClick:()=>s.invalidateQueries({queryKey:["/api/admin/accounting/digest-runs"]}),"data-testid":"button-digest-refresh",children:[e.jsx(he,{className:"w-4 h-4 me-2"})," Refresh"]}),e.jsxs(b,{onClick:()=>_.mutate(),disabled:_.isPending,"data-testid":"button-digest-send-now",children:[_.isPending?e.jsx(k,{className:"w-4 h-4 me-2 animate-spin"}):e.jsx(tt,{className:"w-4 h-4 me-2 rtl-flip"}),"Send now"]})]}),fe.isLoading?e.jsx(k,{className:"w-6 h-6 animate-spin"}):e.jsx(dt,{runs:fe.data?.items??[],onResend:()=>_.mutate(),resending:_.isPending})]})]})]}),e.jsx(se,{value:"jobs",children:e.jsxs(T,{children:[e.jsxs(F,{children:[e.jsx(q,{children:"Sync Log"}),e.jsx(B,{children:"All accounting export jobs and their status."})]}),e.jsxs(E,{className:"space-y-4",children:[e.jsxs("div",{className:"flex gap-2 items-end",children:[e.jsxs("div",{children:[e.jsx(I,{htmlFor:"status-filter",children:"Status"}),e.jsxs("select",{id:"status-filter",className:"border rounded h-10 px-2",value:M,onChange:t=>Fe(t.target.value),"data-testid":"select-status-filter",children:[e.jsx("option",{value:"",children:"All"}),e.jsx("option",{value:"pending",children:"Pending"}),e.jsx("option",{value:"processing",children:"Processing"}),e.jsx("option",{value:"completed",children:"Completed"}),e.jsx("option",{value:"failed",children:"Failed"})]})]}),e.jsxs(b,{variant:"outline",onClick:()=>s.invalidateQueries({queryKey:["/api/admin/accounting/jobs"]}),"data-testid":"button-jobs-refresh",children:[e.jsx(he,{className:"w-4 h-4 me-2"})," Refresh"]})]}),je.isLoading?e.jsx(k,{className:"w-6 h-6 animate-spin"}):e.jsx(Ne,{jobs:je.data?.items??[],onRetry:t=>J.mutate(t),retrying:J.isPending,showAllStatuses:!0,focusJobId:W})]})]})}),e.jsx(se,{value:"backfill",children:e.jsxs(T,{children:[e.jsxs(F,{children:[e.jsx(q,{children:"Backfill Existing Invoices"}),e.jsx(B,{children:"Enqueue export jobs for invoices issued in a historical date range that were never synced."})]}),e.jsxs(E,{className:"space-y-4",children:[!le&&e.jsx(U,{"data-testid":"alert-backfill-disabled",children:e.jsxs(L,{children:["Backfill is disabled because no accounting provider is connected. Open the ",e.jsx("strong",{children:"Connection"})," tab above to connect Xero or QuickBooks first; the date range and Enqueue button will become available once a provider is linked."]})}),e.jsxs("div",{className:"flex flex-wrap gap-4 items-end",children:[e.jsxs("div",{children:[e.jsx(I,{htmlFor:"bf-start",children:"Start"}),e.jsx($,{id:"bf-start",type:"date",value:X,onChange:t=>Ee(t.target.value),"data-testid":"input-backfill-start"})]}),e.jsxs("div",{children:[e.jsx(I,{htmlFor:"bf-end",children:"End"}),e.jsx($,{id:"bf-end",type:"date",value:Y,onChange:t=>$e(t.target.value),"data-testid":"input-backfill-end"})]}),e.jsxs(b,{onClick:()=>A.mutate(),disabled:A.isPending||!le,"data-testid":"button-backfill",children:[A.isPending?e.jsx(k,{className:"w-4 h-4 me-2 animate-spin"}):e.jsx(ve,{className:"w-4 h-4 me-2"}),"Enqueue backfill"]})]}),A.isPending&&e.jsxs(U,{"data-testid":"alert-backfill-running",children:[e.jsx(k,{className:"h-4 w-4 animate-spin"}),e.jsx(G,{children:"Enqueuing backfillâ¦"}),e.jsxs(L,{children:["Walking invoices issued between ",X," and ",Y," and queuing export jobs for any that were never synced. This may take a moment for large ranges."]})]}),!A.isPending&&A.data&&e.jsxs(U,{"data-testid":"alert-backfill-result",children:[e.jsx(pe,{className:"h-4 w-4"}),e.jsx(G,{children:"Backfill enqueued"}),e.jsxs(L,{children:["Queued ",A.data.enqueued," export job",A.data.enqueued===1?"":"s"," from ",X," to"," ",Y,". Track progress in the Sync Log tab."]})]})]})]})})]})})}function O({label:s,count:a,total:o,testId:u,emphasizeWarn:l}){return e.jsx(T,{className:l?"border-red-300":"",children:e.jsxs(E,{className:"pt-4",children:[e.jsx("div",{className:"text-xs text-muted-foreground",children:s}),e.jsx("div",{className:"text-2xl font-semibold","data-testid":`${u}-count`,children:a}),typeof o=="number"&&e.jsxs("div",{className:"text-sm text-muted-foreground","data-testid":`${u}-total`,children:["$",o.toFixed(2)]})]})})}function ot({status:s}){const o={sent:{label:"Sent",cls:"bg-emerald-100 text-emerald-800"},skipped_no_recipients:{label:"Skipped â no recipients",cls:"bg-amber-100 text-amber-800"},skipped_already_sent:{label:"Skipped â already sent",cls:"bg-gray-100 text-gray-800"},failed:{label:"Failed",cls:"bg-red-100 text-red-800"}}[s];return e.jsx(H,{className:o.cls,children:o.label})}function dt({runs:s,onResend:a,resending:o}){if(s.length===0)return e.jsxs("div",{className:"text-sm text-muted-foreground flex items-center gap-2","data-testid":"text-digest-empty",children:[e.jsx(pe,{className:"w-4 h-4"})," No digest runs recorded yet."]});const u=(n,m)=>{try{const y=new Date(n).toLocaleDateString(),g=new Date(m).toLocaleDateString();return y===g?y:`${y} â ${g}`}catch{return`${n} â ${m}`}},l=n=>{const m=Number(n);return Number.isFinite(m)?Xe(m):n};return e.jsxs(Ie,{children:[e.jsx(ke,{children:e.jsxs(ae,{children:[e.jsx(h,{children:"Sent at"}),e.jsx(h,{children:"Status"}),e.jsx(h,{children:"Trigger"}),e.jsx(h,{children:"Range"}),e.jsx(h,{children:"Recipients"}),e.jsx(h,{children:"Summary"}),e.jsx(h,{children:"Failures"}),e.jsx(h,{children:"Error"}),e.jsx(h,{})]})}),e.jsx(Ae,{children:s.map(n=>e.jsxs(ae,{"data-testid":`row-digest-run-${n.id}`,children:[e.jsx(p,{className:"text-xs whitespace-nowrap",children:new Date(n.createdAt).toLocaleStr
1ing()}),e.jsx(p,{"data-testid":`status-digest-run-${n.id}`,children:e.jsx(ot,{status:n.status})}),e.jsx(p,{className:"text-xs",children:e.jsx(H,{variant:"outline",children:n.triggeredBy})}),e.jsx(p,{className:"text-xs whitespace-nowrap",children:u(n.rangeStart,n.rangeEnd)}),e.jsxs(p,{className:"text-xs max-w-xs",title:n.recipients?.join(", ")??void 0,"data-testid":`text-digest-recipients-${n.id}`,children:[e.jsx("div",{children:n.recipientCount}),n.recipients&&n.recipients.length>0&&e.jsxs("div",{className:"truncate text-muted-foreground",children:[n.recipients.slice(0,2).join(", "),n.recipients.length>2?` +${n.recipients.length-2}`:""]})]}),e.jsxs(p,{className:"text-xs whitespace-nowrap",children:[e.jsxs("div",{children:["Inv ",n.invoicesIssuedCount," ⢠",l(n.invoicesIssuedTotal)]}),e.jsxs("div",{children:["Pay ",n.paymentsReceivedCount," ⢠",l(n.paymentsReceivedTotal)]}),e.jsxs("div",{className:"text-muted-foreground",children:["Ref ",n.refundsCount," ⢠Fees ",n.stripeFeesCount]})]}),e.jsxs(p,{className:"text-xs",children:[e.jsxs("div",{className:n.syncFailures>0?"text-red-700 font-medium":"",children:[n.syncFailures," failed"]}),e.jsxs("div",{className:"text-muted-foreground",children:[n.pendingJobs," pending"]})]}),e.jsx(p,{className:"max-w-xs truncate text-xs text-red-700",title:n.errorMessage??void 0,"data-testid":`text-digest-error-${n.id}`,children:n.errorMessage??""}),e.jsx(p,{children:n.status==="failed"&&e.jsx(b,{size:"sm",variant:"outline",disabled:o,onClick:a,"data-testid":`button-digest-resend-${n.id}`,children:"Send now"})})]},n.id))})]})}function Ne({jobs:s,onRetry:a,retrying:o,showAllStatuses:u,focusJobId:l}){return s.length===0?e.jsxs("div",{className:"text-sm text-muted-foreground flex items-center gap-2",children:[e.jsx(pe,{className:"w-4 h-4"})," No jobs to display."]}):e.jsxs(Ie,{children:[e.jsx(ke,{children:e.jsxs(ae,{children:[e.jsx(h,{children:"Created"}),e.jsx(h,{children:"Kind"}),e.jsx(h,{children:"Status"}),e.jsx(h,{children:"Attempts"}),e.jsx(h,{children:"Error"}),e.jsx(h,{children:"Next retry"}),e.jsx(h,{children:"External Id"}),e.jsx(h,{})]})}),e.jsx(Ae,{children:s.map(n=>{const m=!!(n.lastError&&n.lastError.includes(it)),y=n.nextAttemptAt?new Date(n.nextAttemptAt):null,g=y&&!Number.isNaN(y.getTime());return e.jsxs(ae,{"data-testid":`row-job-${n.id}`,className:l&&n.id===l?"bg-amber-50 outline outline-2 outline-amber-300":void 0,children:[e.jsx(p,{className:"text-xs whitespace-nowrap",children:new Date(n.createdAt).toLocaleString()}),e.jsx(p,{children:e.jsx("code",{className:"text-xs",children:n.kind})}),e.jsx(p,{children:e.jsx(rt,{status:n.status})}),e.jsxs(p,{children:[n.attempts,"/",n.maxAttempts]}),e.jsx(p,{className:"max-w-md truncate text-xs text-red-700",title:n.lastError??void 0,children:n.lastError??""}),e.jsx(p,{className:"text-xs whitespace-nowrap","data-testid":`text-next-retry-${n.id}`,children:n.status==="completed"?e.jsx("span",{className:"text-muted-foreground",children:"â"}):g?e.jsxs("span",{className:m?"text-amber-700 font-medium":void 0,children:[y.toLocaleString(),m&&e.jsx("span",{className:"block text-[10px] text-amber-600","data-testid":`text-next-retry-future-dated-${n.id}`,children:"Blocked: invoice is future-dated"})]}):e.jsx("span",{className:"text-muted-foreground",children:"â"})}),e.jsx(p,{className:"text-xs",children:n.externalId??""}),e.jsx(p,{children:(n.status==="failed"||u&&n.status!=="completed")&&e.jsx(b,{size:"sm",variant:"outline",disabled:o,onClick:()=>a(n.id),"data-testid":`button-retry-${n.id}`,children:"Retry"})})]},n.id)})})]})}function lt({provider:s,settings:a,currency:o,enabled:u,onSave:l,saving:n,chartAccounts:m}){const y=s==="quickbooks",g=c=>s==="xero"?`${c} (Xero account code)`:s==="netsuite"?`${c} (NetSuite internal id)`:`${c}`,[i,v]=f.useState({enabled:u,connectedUserId:a?.connectedUserId??"",revenueAccountId:a?.revenueAccountId??"",feeAccountId:a?.feeAccountId??"",refundAccountId:a?.refundAccountId??"",discountAccountId:a?.discountAccountId??"",salesTaxAccountId:a?.salesTaxAccountId??"",arAccountId:a?.arAccountId??"",currency:o??""});return f.useEffect(()=>
1{v({enabled:u,connectedUserId:a?.connectedUserId??"",revenueAccountId:a?.revenueAccountId??"",feeAccountId:a?.feeAccountId??"",refundAccountId:a?.refundAccountId??"",discountAccountId:a?.discountAccountId??"",salesTaxAccountId:a?.salesTaxAccountId??"",arAccountId:a?.arAccountId??"",currency:o??""})},[a,u,o]),e.jsxs("div",{className:"space-y-4",children:[e.jsxs("div",{className:"flex items-center gap-3",children:[e.jsx(Ke,{checked:i.enabled,onCheckedChange:c=>v({...i,enabled:c}),"data-testid":"switch-enabled"}),e.jsx(I,{children:"Enable accounting export"})]}),y?e.jsx("div",{className:"rounded-md border bg-muted/40 p-3 text-sm","data-testid":"text-connected-user-status",children:i.connectedUserId?e.jsxs("span",{children:["QuickBooks is connected as admin user ",e.jsx("code",{className:"font-mono",children:i.connectedUserId}),". Use the Connect / Reconnect button above to switch accounts."]}):e.jsx("span",{children:'No QuickBooks connection bound yet. Click "Connect QuickBooks" above; on completion this account is bound automatically.'})}):e.jsx(Re,{label:"Connected admin user ID",id:"connectedUserId",value:i.connectedUserId,onChange:c=>v({...i,connectedUserId:c}),required:!0}),e.jsxs("div",{className:"grid grid-cols-1 md:grid-cols-2 gap-4",children:[e.jsx(K,{label:g("Revenue account ID"),id:"revenueAccountId",value:i.revenueAccountId,onChange:c=>v({...i,revenueAccountId:c}),required:!0,chartAccounts:m,preferredCategories:["revenue"]}),e.jsx(K,{label:g("Stripe fees account ID"),id:"feeAccountId",value:i.feeAccountId,onChange:c=>v({...i,feeAccountId:c}),chartAccounts:m,preferredCategories:["expense"]}),e.jsx(K,{label:g("Refunds account ID"),id:"refundAccountId",value:i.refundAccountId,onChange:c=>v({...i,refundAccountId:c}),chartAccounts:m,preferredCategories:["revenue","expense"]}),e.jsx(K,{label:g("Discounts account ID"),id:"discountAccountId",value:i.discountAccountId,onChange:c=>v({...i,discountAccountId:c}),chartAccounts:m,preferredCategories:["revenue","expense"]}),e.jsx(K,{label:g("Sales tax account ID"),id:"salesTaxAccountId",value:i.salesTaxAccountId,onChange:c=>v({...i,salesTaxAccountId:c}),chartAccounts:m,preferredCategories:["tax","liability"]}),e.jsx(K,{label:g("A/R account ID"),id:"arAccountId",value:i.arAccountId,onChange:c=>v({...i,arAccountId:c}),chartAccounts:m,preferredCategories:["ar","bank","asset"]})]}),e.jsx(mt,{value:i.currency,onChange:c=>v({...i,currency:c})}),e.jsx("div",{children:e.jsxs(b,{onClick:()=>{const c=i.currency.trim(),R=c===""?"":c.toUpperCase();R!==""&&!ge(R)||l({enabled:i.enabled,connectedUserId:i.connectedUserId||null,revenueAccountId:i.revenueAccountId||null,feeAccountId:i.feeAccountId||null,refundAccountId:i.refundAccountId||null,discountAccountId:i.discountAccountId||null,salesTaxAccountId:i.salesTaxAccountId||null,arAccountId:i.arAccountId||null,currency:R})},disabled:n||!ut(i.currency),"data-testid":"button-save-settings",children:[n?e.jsx(k,{className:"w-4 h-4 me-2 animate-spin"}):null,"Save settings"]})})]})}function ut(s){const a=s.trim();return a.length===0?!0:ge(a.toUpperCase())}function mt({value:s,onChange:a}){const o=s.trim(),u=o.length>0&&!ge(o.toUpperCase());return e.jsx("div",{className:"grid grid-cols-1 md:grid-cols-2 gap-4",children:e.jsxs("div",{className:"space-y-1",children:[e.jsx(I,{htmlFor:"currency",children:"Connection currency (ISO code)"}),e.jsx($,{id:"currency",value:s,onChange:l=>a(l.target.value.toUpperCase().slice(0,8)),placeholder:"USD",list:"currency-suggestions",maxLength:8,"aria-invalid":u||void 0,"data-testid":"input-currency"}),e.jsxs("datalist",{id:"currency-suggestions",children:[e.jsx("option",{value:"USD"}),e.jsx("option",{value:"CAD"}),e.jsx("option",{value:"EUR"}),e.jsx("option",{value:"GBP"}),e.jsx("option",{value:"AUD"}),e.jsx("option",{value:"NZD"}),e.jsx("option",{value:"MXN"}),e.jsx("option",{value:"JPY"})]}),u?e.jsx("p",{className:"text-xs text-red-600","data-testid":"text-currency-error",role:"alert",children:at}):e.jsx("p",{className:"text-xs text-muted-foreground",children:"Must match the currency of the connected accounting org. Leave blank to skip the currency check (legacy setups)."})]})})}function Re({label:s,id:a,value:o,onChange:u,required:l}){return e.jsxs("div",{children:[e.jsxs(I,{htmlFor:a,children:[s,l&&e.jsx("span",{className:"text-red-600",children:" *"})]}),e.jsx($,{id:a,value:o,onChange:n=>u(n.target.value),"data-testid":`input-${a}`})]})}const xt={revenue:"Revenue",expense:"Expense",asset:"Asset",liability:"Liability",equity:"Equity",bank:"Bank",ar:"Accounts receivable",tax:"Tax",other:"Other"};function ht(s){const a=[s.name];return s.code&&a.push(`â ${s.code}`),s.externalId&&s.externalId!==s.code&&a.push(`(ID: ${s.externalId})`),a.join(" ")}const Ce="__clear__";function K({label:s,id:a,value:o,onChange:u,required:l,chartAccounts:n,preferredCategories:m}){const y=!!n&&n.length>0,g=y&&n.some(r=>r.externalId===o);if(!y)return e.jsx(Re,{label:s,id:a,value:o,onChange:u,required:l});const i=new Map;for(const r of n){const w=r.category??"other";i.has(w)||i.set(w,[]),i.get(w).push(r)}const v=[...m??[],"revenue","expense","ar","bank","asset","tax","liability","equity","other"],c=new Set,R=v.filter(r=>c.has(r)?!1:(c.add(r),i.has(r)));return e.jsxs("div",{children:[e.jsxs(I,{htmlFor:a,children:[s,l&&e.jsx("span",{className:"text-red-600",children:" *"})]}),e.jsxs(Me,{value:o||void 0,onValueChange:r=>u(r===Ce?"":r),children:[e.jsx(_e,{id:a,"data-testid":`select-${a}`,children:e.jsx(Qe,{placeholder:o&&!g?`Saved: ${o} (not in pulled chart)`:"Choose an account"})}),e.jsxs(Ge,{children:[!l&&e.jsx(be,{value:Ce,"data-testid":`option-${a}-clear`,children:e.jsx("span",{className:"text-muted-foreground",children:"â None / clear mapping â"})}),R.map(r=>e.jsxs(Ve,{children:[e.jsx(He,{children:xt[r]}),i.get(r).map(w=>e.jsx(be,{value:w.externalId,"data-testid":`option-${a}-${w.externalId}`,children:ht(w)},w.externalId))]},r))]})]}),o&&!g&&e.jsxs("p",{className:"mt-1 text-xs text-amber-700","data-testid":`text-${a}-not-in-chart`,children:["Saved value ",e.jsx("code",{children:o})," wasn't found in the pulled chart of accounts."]})]})}export{Ht 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.