1import{a as oe,j as e}from"./vendor-query-D1Mshrk-.js";import{r as c}from"./vendor-react-BfQMi0FG.js";import{P as ce}from"./papaparse.min-C4qe41JU.js";import{k as de,L as me,B as O,r as xe,U as he,l as pe,E as ue,F as je,H as fe,t as g}from"./index-BrXTQpUj.js";import{C as y,a as I,b as S,c as H,d as R}from"./card-BPl9i0oB.js";import{C as ge}from"./client-combobox-CsjI-P9P.js";import{T as q,a as J,b as C,c as h,d as Q,e as m}from"./table-DJGYHiNt.js";import{u as Ne}from"./use-clients-BGII2zas.js";import{F as Ce}from"./file-up-BvbWktze.js";import{C as ve}from"./circle-x-C49fLmFl.js";import"./vendor-ui-87y9m6y2.js";import"./chevrons-up-down-BrtwLfHu.js";import"./check-DjZQULyt.js";function we(){return oe({mutationFn:async x=>(await de.post("/superadmin/customers/import",x)).data.data})}const be=/^[A-Z]{1,2}\d[A-Z\d]?\d[A-Z]{2}$/;function ye(x){const l=x.replace(/\s+/g,"").toUpperCase();return be.test(l)?null:"Invalid postcode"}function Y(x){const l=x.replace(/[\s\-()]/g,"");if(l.startsWith("+44")){const i=l.slice(3).replace(/\D/g,"");return i.length!==10?`${i.length} digits (need 10 after +44)`:i.startsWith("7")?null:"Must start with +447"}const j=l.replace(/\D/g,"");return j.length!==11?`${j.length} digits (need 11)`:j.startsWith("07")?null:"Must start with 07"}const Ie={email:"email",firstname:"firstName",secondname:"secondName",lastname:"secondName",phone:"phone",addressline1:"addressLine1",addressline2:"addressLine2",city:"city",postcode:"postCode",socialidgoogle:"socialIdGoogle",socialidapple:"socialIdApple",createdon:"createdOn"};function Se(x){const l={};for(const[j,i]of Object.entries(x)){const E=j.trim().toLowerCase().replace(/[\s_-]/g,""),a=Ie[E];if(a){const p=i?.trim();l[a]=p&&p.toLowerCase()!=="null"?p:null}}return l}function Ue(){const{data:x,isLoading:l}=Ne({limit:100}),j=we(),[i,E]=c.useState(""),[a,p]=c.useState([]),[M,D]=c.useState(""),[f,v]=c.useState(null),[w,k]=c.useState(!1),[U,L]=c.useState(!1),[N,P]=c.useState(!1),[F,V]=c.useState(!1),[T,A]=c.useState(null),_=c.useRef(null),B=c.useRef(null),W=a.filter(s=>s.email?.trim()),Z=a.filter(s=>!s.email?.trim()),ee=(()=>{const s=new Map,r=new Set;for(const t of a){const n=t.email?.trim().toLowerCase();if(!n)continue;const d=(s.get(n)||0)+1;s.set(n,d),d>1&&r.add(n)}return r})(),z=s=>!!(!s.email?.trim()||s.phone?.trim()&&Y(s.phone.trim())),b=a.filter(z).length,G=a.length-b,X=s=>{if(!s.name.toLowerCase().endsWith(".csv")){g.error("Please upload a CSV file");return}v(null),P(!1),D(s.name),ce.parse(s,{header:!0,skipEmptyLines:!0,complete:r=>{const t=r.data.map(Se);p(t),g.success(`Parsed ${t.length} rows from ${s.name}`)},error:()=>{g.error("Failed to parse CSV file"),p([])}})},se=s=>{const r=s.target.files?.[0];r&&(X(r),s.target.value="")},te=s=>{s.preventDefault(),L(!1);const r=s.dataTransfer.files[0];r&&X(r)},re=s=>{s.preventDefault(),L(!0)},ae=s=>{s.preventDefault(),L(!1)},ne=async()=>{if(!i){g.error("Please select a client");return}if(W.length===0){g.error("No records with email to import");return}const s=200,r=a,t=r.length;let n=0,d=0;const u=[];V(!0),A({done:0,total:t});try{for(let o=0;o<r.length;o+=s){const ie=r.slice(o,o+s),$=await j.mutateAsync({clientId:i,customers:ie});n+=$.imported,d+=$.skipped,u.push(...$.errors.map(K=>({...K,row:K.row+o}))),A({done:Math.min(o+s,t),total:t})}v({imported:n,skipped:d,errors:u}),p([]),D(""),u.length>0&&k(!0),setTimeout(()=>{B.current?.scrollIntoView({behavior:"smooth",block:"start"})},100)}catch(o){v({imported:n,skipped:d,errors:u}),g.error(o.response?.data?.error?.message||`Import interrupted after ${n} imported. Re-run to continue â already-imported customers are skipped.`)}finally{V(!1),A(null)}},le=()=>{p([]),D(""),v(null),k(!1),P(!1)};return e.jsxs("div",{className:"space-y-6",children:[e.jsxs("div",{children:[e.jsx("h1",{className:"text-2xl font-bold",children:"Customer Import"}),e.jsx("p",{className:"text-muted-foreground",children:"Import customers from the legacy system via CSV"})]}),e.jsxs(y,{children:[e.jsxs(I,{children:[e.jsx(S,{className:"text-base",children:"Select Client"}),e.jsx(H,{children:"Choose the client to import customers into"})]}),e.jsx(R,{children:e.jsxs("div",{className:"max-w-sm",children:[e.jsx(me,{htmlFor:"client-select",children:"Client"}),e.jsx("div",{className:"mt-1.5",children:e.jsx(ge,{id:"client-select",value:i,onChange:E,loading:l,clients:(x?.clients??[]).filter(s=>s.status==="ACTIVE")})})]})})]}),e.jsxs(y,{children:[e.jsxs(I,{children:[e.jsx(S,{className:"text-base",children:"Upload CSV"}),e.jsx(H,{children:"Expected columns: Email, FirstName, SecondName, Phone, AddressLine1, AddressLine2, City, PostCo
1de, SocialIdGoogle, SocialIdApple, CreatedOn"})]}),e.jsxs(R,{children:[e.jsx("input",{ref:_,type:"file",accept:".csv",className:"hidden",onChange:se}),e.jsxs("div",{onDrop:te,onDragOver:re,onDragLeave:ae,onClick:()=>_.current?.click(),className:`flex cursor-pointer flex-col items-center gap-3 rounded-lg border-2 border-dashed p-8 transition-colors ${U?"border-primary bg-primary/5":"border-muted-foreground/25 hover:border-muted-foreground/50"}`,children:[e.jsx(Ce,{className:`h-8 w-8 ${U?"text-primary":"text-muted-foreground"}`}),M?e.jsx("p",{className:"text-sm font-medium",children:M}):e.jsxs(e.Fragment,{children:[e.jsx("p",{className:"text-sm font-medium",children:"Drag & drop a CSV file here, or click to browse"}),e.jsx("p",{className:"text-xs text-muted-foreground",children:"Only .csv files are accepted"})]})]}),a.length>0&&e.jsx("div",{className:"mt-3 flex justify-end",children:e.jsx(O,{variant:"ghost",size:"sm",onClick:le,children:"Clear"})})]})]}),a.length>0&&!f&&e.jsxs(y,{children:[e.jsxs(I,{children:[e.jsx(S,{className:"text-base",children:"Preview"}
1),e.jsxs(H,{children:[a.length," total rows ·"," ",e.jsxs("span",{className:"text-green-600",children:[W.length," with email"]}),Z.length>0&&e.jsxs(e.Fragment,{children:[" ","·"," ",e.jsxs("span",{className:"text-yellow-600",children:[Z.length," without email (will be skipped)"]})]}),b>0&&e.jsxs(e.Fragment,{children:[" ","·"," ",e.jsxs("button",{onClick:()=>P(!N),className:"text-orange-500 hover:underline cursor-pointer",children:[b," will be rejected",N&&" (showing)"]})]})]})]}),e.jsxs(R,{children:[e.jsx("div",{className:"overflow-auto rounded border",children:e.jsxs(q,{children:[e.jsx(J,{children:e.jsxs(C,{children:[e.jsx(h,{className:"w-10",children:"#"}),e.jsx(h,{children:"Email"}),e.jsx(h,{children:"First Name"}),e.jsx(h,{children:"Last Name"}),e.jsx(h,{children:"Phone"}),e.jsx(h,{children:"PostCode"})]})}),e.jsx(Q,{children:(()=>{const s=N?a.map((t,n)=>({row:t,originalIndex:n})).filter(({row:t})=>z(t)):a.slice(0,10).map((t,n)=>({row:t,originalIndex:n})),r=N?b:a.length;return e.jsxs(e.Fragment,{children:[s.map(({row:t,originalIndex:n})=>{const d=t.phone?.trim()?Y(t.phone.trim()):null,u=t.postCode?.trim()?ye(t.postCode.trim()):null,o=t.email?.trim()&&ee.has(t.email.trim().toLowerCase());return e.jsxs(C,{className:t.email?.trim()?"":"opacity-50",children:[e.jsx(m,{className:"text-muted-foreground",children:n+1}),e.jsxs(m,{className:"font-mono text-xs",children:[e.jsx("span",{className:o?"text-yellow-600":"",children:t.email||"â"}),o&&e.jsx("span",{className:"ml-1 text-[10px] text-yellow-600",children:"(duplicate)"})]}),e.jsx(m,{children:t.firstName||"â"}),e.jsx(m,{children:t.secondName||"â"}),e.jsxs(m,{children:[e.jsx("span",{className:d?"text-orange-500":"",children:t.phone||"â"}),d&&e.jsxs("span",{className:"ml-1 text-[10px] text-orange-500",children:["(",d,")"]})]}),e.jsxs(m,{children:[e.jsx("span",{className:u?"text-orange-500":"",children:t.postCode||"â"}),u&&e.jsxs("span",{className:"ml-1 text-[10px] text-orange-500",children:["(",u,")"]})]})]},n)}),!N&&a.length>10&&e.jsx(C,{children:e.jsxs(m,{colSpan:6,className:"text-center text-sm text-muted-foreground",children:["... and ",r-10," more rows"]})})]})})()})]})}),e.jsxs("div",{className:"mt-4 flex items-center justify-end gap-3",children:[!i&&e.jsx("p",{className:"text-sm text-muted-foreground",children:"Select a client above to enable import"}),e.jsxs(O,{onClick:ne,disabled:!i||G<=0||F,children:[F?e.jsx(xe,{className:"mr-2 h-4 w-4 animate-spin"}):e.jsx(he,{className:"mr-2 h-4 w-4"}),F&&T?`Importing ${T.done}/${T.total}...`:`Import ${G} Customers`]})]})]})]}),f&&e.jsxs(y,{ref:B,children:[e.jsx(I,{children:e.jsx(S,{className:"text-base",children:"Import Results"})}),e.jsxs(R,{children:[e.jsxs("div",{className:"grid grid-cols-3 gap-4",children:[e.jsxs("div",{className:"flex items-center gap-3 rounded-lg border p-4",children:[e.jsx(pe,{className:"h-5 w-5 text-green-600"}),e.jsxs("div",{children:[e.jsx("p",{className:"text-2xl font-bold",children:f.imported}),e.jsx("p",{className:"text-xs text-muted-foreground",children:"Imported"})]})]}),e.jsxs("div",{className:"flex items-center gap-3 rounded-lg border p-4",children:[e.jsx(ue,{className:"h-5 w-5 text-yellow-600"}),e.jsxs("div",{children:[e.jsx("p",{className:"text-2xl font-bold",children:f.skipped}),e.jsx("p",{className:"text-xs text-muted-foreground",children:"Skipped (duplicates)"})]})]}),e.jsxs("div",{className:"flex items-center gap-3 rounded-lg border p-4",children:[e.jsx(ve,{className:"h-5 w-5 text-red-600"}),e.jsxs("div",{children:[e.jsx("p",{className:"text-2xl font-bold",children:f.errors.length}),e.jsx("p",{className:"text-xs text-muted-foreground",children:"Rejected (bad data)"})]})]})]}),f.errors.length>0&&e.jsxs("div",{className:"mt-4",children:[e.jsxs(O,{variant:"ghost",size:"sm",className:"gap-1 text-destructive",onClick:()=>k(!w),children:[w?e.jsx(je,{className:"h-4 w-4"}):e.jsx(fe,{className:"h-4 w-4"}),w?"Hide":"Show"," rejected rows"]}),w&&e.jsx("div",{className:"mt-2 max-h-60 overflow-auto rounded border",children:e.jsxs(q,{children:[e.jsx(J,{children:e.jsxs(C,{children:[e.jsx(h,{className:"w-16",children:"Row"}),e.jsx(h,{children:"Email"}),e.jsx(h,{children:"Reason"})]})}),e.jsx(Q,{children:f.errors.map((s,r)=>e.jsxs(C,{children:[e.jsx(m,{children:s.row}),e.jsx(m,{className:"font-mono text-xs",children:s.email||"â"}),e.jsx(m,{className:"text-xs text-destructive",children:s.reason})]},r))})]})})]})]})]})]})}export{Ue as CustomerImportPage};
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.