1import{d as x,j as e}from"./vendor-react-DI1Sy88J.js";import{b as G,e as K,c as B}from"./vendor-router-DW82M7iN.js";import{b as y,d as k,c as M,T as W,y as X,z as O,x as U,v as J,w as Y,E as R}from"./app-public-tools-BQrJS2St.js";import{ar as Z,e as p,B as ee,f as j,j as v,k as f,g as N,S as se,Q as ae,T as te,N as ie,O as le}from"./app-marketing-ByOgiMj4.js";import{f as re,at as ne,b1 as q,cB as de,V as I,g as $,X as z}from"./vendor-misc-B1nzCziw.js";import"./vendor-radix-ocehe2sJ.js";import"./vendor-date-DPo7-IdA.js";const ce={"moved in":"Active","move in":"Active",active:"Active",current:"Active",resident:"Active","in house":"Active","moved out":"Discharged","move out":"Discharged",discharged:"Discharged",inactive:"Discharged",terminated:"Discharged",left:"Discharged",completed:"Discharged",graduated:"Discharged",applied:"Applied",applicant:"Applied",pending:"Pending",waiting:"Pending",waitlist:"Pending",intake:"Intake",others:"Discharged",other:"Discharged"};function oe(o,n){const d="".toLowerCase().trim();if(d==="others"||d==="other")return"Discharged";const i=String(o||"").toLowerCase().trim();return i&&ce[i]||"Pending"}function me(o){const n=String(o||"").trim(),d=/^(\d{4})-(\d{2})-(\d{2})$/.exec(n),i=/^(\d{1,2})[/-](\d{1,2})[/-](\d{4})$/.exec(n),l=Number(d?.[1]??i?.[3]),h=Number(d?.[2]??i?.[1]),m=Number(d?.[3]??i?.[2]);if(!Number.isInteger(l)||!Number.isInteger(h)||!Number.isInteger(m))return null;const t=new Date(Date.UTC(l,h-1,m));return t.getUTCFullYear()!==l||t.getUTCMonth()!==h-1||t.getUTCDate()!==m?null:`${String(l).padStart(4,"0")}-${String(h).padStart(2,"0")}-${String(m).padStart(2,"0")}`}const P=[{value:"skip",label:"-- Skip this column --"},{value:"firstName",label:"First Name *"},{value:"lastName",label:"Last Name *"},{value:"email",label:"Email"},{value:"phone",label:"Phone"},{value:"birthdate",label:"Birthdate"},{value:"gender",label:"Gender"},{value:"houseId",label:"House (by name)"},{value:"status",label:"Status"},{value:"admissionDate",label:"Admission Date"},{value:"sobrietyDate",label:"Sobriety Date"},{value:"balance",label:"Balance"},{value:"accountBalance",label:"Account Balance"},{value:"substanceOfChoice",label:"Substance of Choice"},{value:"maritalStatus",label:"Marital Status"},{value:"race",label:"Race"},{value:"employed",label:"Employed"},{value:"allergies",label:"Allergies"},{value:"referralSource",label:"Referral Source"},{value:"pronouns",label:"Pronouns"},{value:"clinicalDiagnosis",label:"Clinical Diagnosis (yes/no)"},{value:"clinicalDiagnosisDetails",label:"Clinical Diagnosis Details"},{value:"medicalProblems",label:"Medical Problems (yes/no)"},{value:"medicalProblemsDetails",label:"Medical Problems Details"},{value:"hasVehicle",label:"Has Vehicle (yes/no)"},{value:"previousSoberLiving",label:"Previous Sober Living (yes/no)"},{value:"previousSoberLivingName",label:"Previous Sober Living Name"},{value:"dischargeDate",label:"Discharge Date"},{value:"dischargeReason",label:"Discharge Reason"}];function ue(o){const n=o.toLowerCase().replace(/[^a-z]/g,"");return{firstname:"firstName",first:"firstName",fname:"firstName",lastname:"lastName",last:"lastName",lname:"lastName",email:"email",emailaddress:"email",phone:"phone",phonenumber:"phone",cell:"phone",mobile:"phone",birthdate:"birthdate",dob:"birthdate",dateofbirth:"birthdate",birthday:"birthdate",gender:"gender",sex:"gender",house:"houseId",housename:"houseId",location:"houseId",status:"status",admission:"admissionDate",admissiondate:"admissionDate",admitdate:"admissionDate",movedin:"admissionDate",sobriety:"sobrietyDate",sobrietydate:"sobrietyDate",cleandate:"sobrietyDate",balance:"balance",amountowed:"balance",owed:"balance",substance:"substanceOfChoice",drug:"substanceOfChoice",substanceofchoice:"substanceOfChoice",marital:"maritalStatus",maritalstatus:"maritalStatus",race:"race",ethnicity:"race",employed:"employed",employment:"employed",allergies:"allergies",allergy:"allergies",referral:"referralSource",referralsource:"referralSource",dischargedate:"dischargeDate",dischargedat:"dischargeDate",moveoutdate:"dischargeDate",movedoutdate:"dischargeDate",exitdate:"dischargeDate",dischargereason:"dischargeReason",moveoutreason:"dischargeReason"}[n]||"skip"}function fe(){const[,o]=G(),{toast:n}=Z(),d=x.useRef(null),[i,l]=x.useState("upload"),[h,m]=x.useState([]),[t,T]=x.useState([]),[u,D]=x.useState({}),[c,A]=x.useState(null),{data:w}=K({queryKey:["houses"],queryFn:R.houses.list}),F=B({mutationFn:s=>R.import.parseCSV(s),onSuccess:s=>{m(s.headers),T(s.rows);const a={};
1s.headers.forEach(r=>{a[r]=ue(r)}),D(a),l("mapping"),n({title:`Parsed ${s.totalRows} rows`,description:"Map your columns below."})},onError:s=>{n({title:"Upload failed",description:s.message,variant:"destructive"})}}),S=B({mutationFn:()=>R.import.execute(t,u),onSuccess:s=>{A(s),l("results")},onError:s=>{n({title:"Import failed",description:s.message,variant:"destructive"})}}),H=s=>{const a=s.target.files?.[0];if(a){if(!a.name.endsWith(".csv")){n({title:"Invalid file",description:"Please select a CSV file.",variant:"destructive"});return}F.mutate(a)}},_=["firstName","lastName"],C=Object.values(u).filter(s=>s!=="skip"),L=Object.entries(u).find(([,s])=>s==="status")?.[0],E=Object.entries(u).find(([,s])=>s==="dischargeDate")?.[0],V=L?t.flatMap((s,a)=>oe(s[L])==="Discharged"?[a+1]:[]):[],g=V.filter(s=>!E||!me(t[s-1]?.[E])),b=_.filter(s=>!C.includes(s));V.length>0&&!C.includes("dischargeDate")&&b.push("dischargeDate");const Q=b.map(s=>P.find(a=>a.value===s)?.label?.replace(" *","")||s);return e.jsxs("div",{className:"space-y-6",children:[e.jsxs("div",{className:"flex items-center gap-4",children:[e.jsx(p,{variant:"ghost",size:"icon",onClick:()=>o("/admin/residents"),"data-testid":"button-back",children:e.jsx(re,{className:"w-5 h-5"})}),e.jsxs("div",{children:[e.jsx("h1",{className:"text-2xl font-bold","data-testid":"text-page-title",children:"Import Clients"}),e.jsx("p",{className:"text-muted-foreground",children:"Upload a CSV file to bulk-import residents"})]})]}),e.jsx("div",{className:"flex gap-2 mb-6",children:["upload","mapping","preview","results"].map((s,a)=>e.jsxs(ee,{variant:i===s?"default":"outline",className:"capitalize","data-testid":`badge-step-${s}`,children:[a+1,". ",s==="upload"?"Upload":s==="mapping"?"Map Columns":s==="preview"?"Preview":"Results"]},s))}),i==="upload"&&e.jsxs(j,{"data-testid":"card-upload",children:[e.jsx(v,{children:e.jsxs(f,{className:"flex items-center gap-2",children:[e.jsx(ne,{className:"w-5 h-5"}),"Upload CSV File"]})}),e.jsxs(N,{children:[e.jsxs("div",{className:"border-2 border-dashed border-muted-foreground/25 rounded-lg p-12 text-center cursor-pointer hover:border-primary/50 transition-colors",onClick:()=>d.current?.click(),"data-testid":"dropzone-upload",children:[F.isPending?e.jsxs("div",{className:"flex flex-col items-center gap-3",children:[e.jsx(q,{className:"w-12 h-12 text-primary animate-spin"}),e.jsx("p",{className:"text-lg font-medium",children:"Parsing file..."})]}):e.jsxs("div",{className:"flex flex-col items-center gap-3",children:[e.jsx(de,{className:"w-12 h-12 text-muted-foreground"}),e.jsx("p",{className:"text-lg font-medium",children:"Click to select a CSV file"}),e.jsx("p",{className:"text-sm text-muted-foreground",children:"Maximum file size: 5MB"})]}),e.jsx("input",{ref:d,type:"file",accept:".csv",className:"hidden",onChange:H,"data-testid":"input-file"})]}),e.jsxs("div",{className:"mt-6 space-y-2",children:[e.jsx("p",{className:"text-sm font-medium",children:"Tips for your CSV file:"}),e.jsxs("ul",{className:"text-sm text-muted-foreground space-y-1 list-disc pl-5",children:[e.jsx("li",{children:"First row should be column headers"}),e.jsx("li",{children:"Required columns: First Name, Last Name (Email and Phone are recommended but optional)"}),e.jsx("li",{children:'House names will be matched automatically (e.g., "Briarwood", "Cedar")'}),e.jsx("li",{children:"Dates should be in a standard format (MM/DD/YYYY or YYYY-MM-DD)"}),e.jsx("li",{children:"Yes/No fields accept: yes, no, true, false, 1, 0"})]})]})]})]}),i==="mapping"&&e.jsxs(j,{"data-testid":"card-mapping",children:[e.jsx(v,{children:e.jsxs(f,{children:["Map Columns (",t.length," rows found)"]})}),e.jsxs(N,{children:[b.length>0&&e.jsxs(y,{variant:"destructive",className:"mb-4",children:[e.jsx(I,{className:"h-4 w-4"}),e.jsx(k,{children:"Missing required fields"}),e.jsxs(M,{children:["Please map these required columns: ",Q.join(", ")]})]}),g.length>0&&C.includes("dischargeDate")&&e.jsxs(y,{variant:"destructive",className:"mb-4",children:[e.jsx(I,{className:"h-4 w-4"}),e.jsx(k,{children:"Missing discharge dates"}),e.jsxs(M,{children:["Discharge Date is required for discharged residents and must be a real date. Check CSV row",g.length===1?"":"s"," ",g.join(", "),"."]})]}),e.jsx("div",{className:"space-y-3",children:h.map(s=>e.jsxs("div",{className:"flex items-center gap-4","data-testid":`mapping-row-${s}`,children:[e.jsxs("div",{className:"w-1/3",children:[e.jsx("p",{className:"text-sm font-medium",children:s}),e.jsxs("p",{className:"text-xs text-muted-foreground truncate",children:['e.g. "',t[0]?.[s]||"",'"']})]}),e.jsx($,{className:"w-4 h-4 text-muted-foreground flex-shrink-0"}),e.jsx("div",{className:"w-1/2",children:e.jsxs(se,{value:u[s]||"skip",onValueChange:a=>D(r=>({...r,[s]:a})),children:[e.jsx(ae,{"data-testid":`select-mapping-${s}`,children:e.jsx(te,{})}),e.jsx(ie,{children:P.map(a=>e.jsx(le,{value:a.value,children:a.label},a.value))})]})})]},s))}),w&&w.length>0&&e.jsxs("div",{className:"mt-4 p-3 bg-muted/50 rounded-lg",children:[e.jsx("p",{className:"text-sm font-medium mb-1",children:"Available houses for matching:"}),e.jsx("p",{className:"text-xs text-muted-foreground",children:w.map(s=>s.name).join(", ")})]}),e.jsxs("div",{className:"flex gap-3 mt-6",children:[e.jsx(p,{variant:"outline",onClick:()=>l("upload"),"data-testid":"button-back-upload",children:"Back"}),e.jsxs(p,{onClick:()=>l("preview"),disabled:b.length>0||g.length>0,"data-testid":"button-next-preview",children:["Preview Import ",e.jsx($,{className:"w-4 h-4 ml-2"})]})]})]})]}),i==="preview"&&e.jsxs(j,{"data-testid":"card-preview",children:[e.jsx(v,{children:e.jsxs(f,{children:["Preview (",t.length," residents to import)"]})}),e.jsxs(N,{children:[e.jsx("div",{className:"rounded-md border overflow-auto max-h-[400px]",children:e.jsxs(W,{children:[e.jsx(X,{children:e.jsxs(O,{children:[e.jsx(U,{children:"#"}),Object.entries(u).filter(([,s])=>s!=="skip").map(([s,a])=>e.jsx(U,{children:P.find(r=>r.value===a)?.label||a},s))]})}),e.jsx(J,{children:t.slice(0,10).map((s,a)=>e.jsxs(O,{children:[e.jsx(Y,{className:"text-muted-foreground",children:a+1}),Object.entries(u).filter(([,r])=>r!=="skip").map(([r])=>e.jsx(Y,{className:"max-w-[200px] truncate",children:s[r]||"-"},r))]},a))})]})}),t.length>10&&e.jsxs("p",{className:"text-sm text-muted-foreground mt-2",children:["Showing first 10 of ",t.length," rows"]}),e.jsxs("div",{className:"flex gap-3 mt-6",children:[e.jsx(p,{variant:"outline",onClick:()=>l("mapping"),"data-testid":"button-back-mapping",children:"Back to Mapping"}),e.jsx(p,{onClick:()=>S.mutate(),disabled:S.isPending,"data-testid":"button-execute-import",children:S.isPending?e.jsxs(e.Fragment,{children:[e.jsx(q,{className:"w-4 h-4 mr-2 animate-spin"})," Importing..."]}):e.jsxs(e.Fragment,{children:[e.jsx(z,{className:"w-4 h-4 mr-2"})," Import ",t.length," Residents"]})})]})]})]}),i==="results"&&c&&e.jsxs(j,{"data-testid":"card-results",children:[e.jsx(v,{children:e.jsxs(f,{className:"flex items-center gap-2",children:[e.jsx(z,{className:"w-5 h-5 text-green-500"}),"Import Complete"]})}),e.jsxs(N,{children:[e.jsxs("div",{className:"grid grid-cols-2 gap-4 mb-6 sm:grid-cols-4",children:[e.jsxs("div",{className:"p-4 bg-green-50 dark:bg-green-900/20 rounded-lg text-center",children:[e.jsx("p",{className:"text-3xl font-bold text-green-600","data-testid":"text-imported-count",children:c.imported}),e.jsx("p",{className:"text-sm text-green-600",children:"Imported"})]}),e.jsxs("div",{className:"p-4 bg-blue-50 dark:bg-blue-900/20 rounded-lg text-center",children:[e.jsx("p",{className:"text-3xl font-bold text-blue-600","data-testid":"text-duplicates-count",children:c.duplicates||0}),e.jsx("p",{className:"text-sm text-blue-600",children:"Exact Duplicates Skipped"})]}),e.jsxs("div",{className:"p-4 bg-orange-50 dark:bg-orange-900/20 rounded-lg text-center",children:[e.jsx("p",{className:"text-3xl font-bold text-orange-600","data-testid":"text-potential-duplicates-count",children:c.potentialDuplicates||0}),e.jsx("p",{className:"text-sm text-orange-600",children:"Name Matches â Review"})]}),e.jsxs("div",{className:"p-4 bg-amber-50 dark:bg-amber-900/20 rounded-lg text-center",children:[e.jsx("p",{className:"text-3xl font-bold text-amber-600","data-testid":"text-skipped-count",children:c.skipped}),e.jsx("p",{className:"text-sm text-amber-600",children:"Total Skipped"})]})]}),c.potentialDuplicateNames&&c.potentialDuplicateNames.length>0&&e.jsxs(y,{className:"mb-4 border-orange-200 bg-orange-50 dark:bg-orange-900/20",children:[e.jsx(I,{className:"h-4 w-4 text-orange-600"}),e.jsx(k,{className:"text-orange-700",children:"Name matches skipped â please review"}),e.jsxs(M,{className:"text-orange-600",children:["These names already exist in y
1our residents list and were not imported (no email or phone to confirm they're different people): ",e.jsx("strong",{children:c.potentialDuplicateNames.join(", ")}),". If they are different people, add an email or phone number to their CSV row and re-import."]})]}),c.errors.length>0&&e.jsxs("div",{className:"mb-6",children:[e.jsx("p",{className:"text-sm font-medium mb-2",children:"Errors:"}),e.jsx("div",{className:"max-h-[200px] overflow-auto bg-muted rounded-lg p-3 space-y-1",children:c.errors.map((s,a)=>e.jsx("p",{className:"text-sm text-destructive",children:s},a))})]}),e.jsxs("div",{className:"flex gap-3",children:[e.jsx(p,{onClick:()=>o("/admin/residents"),"data-testid":"button-view-residents",children:"View Residents"}),e.jsx(p,{variant:"outline",onClick:()=>{l("upload"),m([]),T([]),D({}),A(null)},"data-testid":"button-import-more",children:"Import More"})]})]})]})]})}export{fe 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.