1import{j as a}from"./vendor-markdown-Daxr8BJC.js";import{D as ie,a as re,b as le,c as ce,d as de}from"./dialog-Z6uIZR47.js";import{f as ue,b as H,Y as he}from"./index-DomUViA2.js";import{S as me}from"./scroll-area-veLcfJ6w.js";import{V as pe,a as fe,b as ge,G as be,c as _e,S as z,U as we,F as O}from"./referenceData-BSZBOYMG.js";import{L as Se}from"./lightbulb-BwzGPmNG.js";import{D as xe}from"./download-Bu4HsJ2t.js";const ve=/^(?=.{1,253}$)([a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,63}$/,B=50,W=e=>e?ve.test(e.trim()):!1,$e=e=>{const t=(e??"").trim();if(!t||t.length>2048||/\s/.test(t))return!1;if(/^https?:\/\//i.test(t))try{const n=new URL(t);return n.protocol!=="http:"&&n.protocol!=="https:"?!1:W(n.hostname)}catch{return!1}return/^[a-z][a-z0-9+.-]*:/i.test(t)?!1:W(t)},Ne=e=>{if(!e)return{valid:[],invalid:[]};const t=new Set,n=[],s=[];return String(e).split(",").map(r=>r.trim()).filter(Boolean).forEach(r=>{if(!$e(r)){s.push(r);return}const u=r.toLowerCase();t.has(u)||(t.add(u),n.push(r))}),{valid:n,invalid:s}},J={jan:"1",feb:"2",mar:"3",apr:"4",may:"5",jun:"6",jul:"7",aug:"8",sep:"9",oct:"10",nov:"11",dec:"12",january:"1",february:"2",march:"3",april:"4",june:"6",july:"7",august:"8",september:"9",october:"10",november:"11",december:"12"},ye=["elementary","middle school","high school","k-12","k-5","6-8","9-12","higher education","pre-k","elementary (k-5)","middle school (6-8)","high school (9-12)"],V=e=>e.toLowerCase()==="k"||/^\d{1,2}$/.test(e)&&parseInt(e,10)>=1&&parseInt(e,10)<=12,je=e=>{if(e==null||e==="")return"";const t=String(e).trim();if(ye.includes(t.toLowerCase()))return t;const n=t.match(/^([Kk]|\d{1,2})\s*[-â]\s*([Kk]|\d{1,2})$/);if(n&&V(n[1])&&V(n[2]))return t;const s=/^([Kk]|\d{1,2})\s*[-â]\s*([A-Za-z]+)$/,r=/^([A-Za-z]+)\s*[-â]\s*([Kk]|\d{1,2})$/;let u,i;const h=t.match(s);if(h){const l=J[h[2].toLowerCase()];l&&V(h[1])&&(u=h[1],i=l)}if(!u){const l=t.match(r);if(l){const _=J[l[1].toLowerCase()];_&&V(l[2])&&(u=_,i=l[2])}}if(u&&i){const l=`${u}-${i}`;return console.warn(`â ï¸ Auto-corrected Excel date corruption in grade_range: "${t}" â "${l}"`),l}return/^\d+\.\d+$/.test(t)||/^\d{5,}$/.test(t)&&parseInt(t,10)>=3e4&&parseInt(t,10)<=8e4?(console.warn(`â ï¸ Detected Excel serial number in grade_range field: ${t} - setting to empty`),""):t},Oe=e=>{if(e==null||e==="")return"";const t=String(e).trim();if(/^\d+\.\d+$/.test(t))return console.warn(`â ï¸ Detected Excel serial number in grade field: ${t} - setting to empty`),"";if(/^\d{5,}$/.test(t)){const n=parseInt(t,10);if(n>=3e4&&n<=8e4)return console.warn(`â ï¸ Detected potential Excel serial date in grade field: ${t} - setting to empty`),""}return t},De=we,Le=["specific","gradeband"],Y=[...pe],Re=Y.map(e=>e.toLowerCase()),Ee={name:"Software Name",category:"Category",user_type:"Who Can Use This Software",total_cost:"Total Cost",students_licensed:"Number of Student Licenses",teachers_licensed:"Number of Teacher Licenses",purchase_date:"Purchase Date",expiration_date:"Expiration Date",url:"Software Website URL",school_selection_type:"School Selection Method",selected_schools:"Selected Schools",applicable_grade_bands:"Grade Bands",grade:"Grade",grade_range:"Grade Range",funding_source:"Funding Source",other_domains:"Additional Domains",sub_population:"Sub-Population",subject:"Subject",school_name:"School Name"},F=e=>Ee[e]||e.replace(/_/g," ").replace(/\b\w/g,t=>t.toUpperCase()),o=(e,t,n,s)=>({field:e,message:t,friendlyField:F(e),friendlyMessage:n,hint:s}),Ae=e=>{if(!e||String(e).trim()==="")return!1;const t=String(e).trim();try{const n=/^https?:\/\//i.test(t)?t:`https://${t}`,r=new URL(n).hostname;return!r||r.length<3?!1:/^[a-z0-9.-]+\.[a-z]{2,}$/i.test(r)}catch{return!1}},X=e=>{if(e==null||e==="")return null;if(typeof e=="number"&&Number.isFinite(e)){if(e<1||e>6e4)return null;const r=Math.round((e-25569)*86400*1e3),u=new Date(r);return isNaN(u.getTime())?null:u.toISOString().slice(0,10)}const t=String(e).trim();if(!t)return null;if(/^\d+(\.\d+)?$/.test(t)){const r=Number(t);if(r>1e3&&r<6e4){const u=Math.round((r-25569)*86400*1e3),i=new Date(u);if(!isNaN(i.getTime()))return i.toISOString().slice(0,10)}}let n=t.match(/^(\d{4})-(\d{1,2})-(\d{1,2})$/);if(n){const[r,u,i,h]=n,l=new Date(Number(u),Number(i)-1,Number(h));return Q(l,+u,+i,+h)?q(l):null}if(n=t.match(/^(\d{1,2})[\/\-](\d{1,2})[\/\-](\d{2}|\d{4})$/),n){let[,r,u,i]=n;const h=i.length===2?2e3+Number(i):Number(i),l=new Date(h,Number(r)-1,Number(u));return Q(l,h,+r,+u)?q(l):null}const s=new Date(t);return isNaN(s.getTime())?null:q(s)},Q=(e,t,n,s)=>e.getFullYear()===t&&e.getMonth()===n-1&&e.getDate()===s,q=e=>{const t=e.getFullYear(),n=String(e.getMonth()+1).padStart(2,"0"),s=String(e.getDate()).padStart(2,"0");return`${t}-${n}-${s}`},Ce=e=>{if(e==null||e==="")return{ok:!1,reason:"empty"};const t=String(e).trim();if(!/^-?\d+(\.\d+)?$/.test(t))return{ok:!1,reason:"not_number"};const n=Number(t);return Number.isFinite(n)?n<0?{ok:!1,reason:"negative"}:{ok:!0,num:n}:{ok:!1,reason:"not_number"}},ee=e=>{if(e==null||e==="")return{ok:!1,reason:"empty"};const t=String(e).trim();if(!/^-?\d+$/.test(t))return{ok:!1,reason:"not_integer"};const n=Number(t);return Number.isFinite(n)?n<0?{ok:!1,reason:"negative"}:{ok:!0,num:n}:{ok:!1,reason:"not_integer"}},M=e=>String(e).split(/[,;]/).map(t=>
1t.trim()).filter(Boolean),ke=(e,t,n={})=>{const s=[],r={...e},u=n.validSubjects??fe,i=n.validSubPopulations??ge,h=n.validGradeBands??be,l=n.validGradeRanges??_e;(!e.name||String(e.name).trim()==="")&&s.push(o("name","Software name is required","Please enter a name for this software.","Every software entry needs a name to identify it."));const _=e.category??e.Category??"",v=String(_).trim();v?z.find(c=>c.toLowerCase()===v.toLowerCase())||s.push(o("category",`Invalid category '${v}'.`,`"${v}" is not a valid category.`,`Allowed categories: ${z.join(", ")}.`)):s.push(o("category","Category is required","Please enter a category for this software.",`Use one of the allowed categories: ${z.join(", ")}.`));const f=String(e.userType||e.user_type||"").toLowerCase().trim();f?De.includes(f)||s.push(o("user_type",`Invalid user_type '${f}'.`,`"${f}" is not recognized. Please enter "student", "teacher", or "both".`,"This tells us who will be using the software.")):s.push(o("user_type","user_type is required","Please specify who can use this software.",'Enter "student", "teacher", or "both".'));const $=e.totalCost??e.total_cost,j=Ce($);j.ok||(j.reason==="empty"?s.push(o("total_cost","total_cost is required","Please enter a cost amount. You can enter 0 if the software is free.","Enter the total amount paid for this software license.")):j.reason==="not_number"?s.push(o("total_cost",`Invalid total_cost '${$}'. Must be numeric only.`,`"${$}" is not a valid number. Total cost must be numeric only.`,"Remove any currency symbols, commas, or letters (e.g., use 1500 instead of $1,500).")):j.reason==="negative"&&s.push(o("total_cost",`total_cost cannot be negative ('${$}').`,"Total cost cannot be a negative number.","Enter 0 or a positive amount.")));const g=e.studentsLicensed??e.students_licensed,w=ee(g);w.ok||(w.reason==="empty"?s.push(o("students_licensed","students_licensed is required","Please enter the number of student licenses. Enter 0 if no students will use this.","This is the maximum number of students who can use this software.")):w.reason==="not_integer"?s.push(o("students_licensed",`Invalid students_licensed '${g}'. Must be a whole number.`,`"${g}" is not a valid whole number.`,"Enter a whole number without decimals or special characters (e.g., 100).")):w.reason==="negative"&&s.push(o("students_licensed",`students_licensed cannot be negative ('${g}').`,"Number of student licenses cannot be negative.","Enter 0 or a positive whole number.")));const S=e.teachersLicensed??e.teachers_licensed;let C;if(S!=null&&String(S).trim()!==""){const d=ee(S);d.ok?C=d.num:d.reason==="not_integer"?s.push(o("teachers_licensed",`Invalid teachers_licensed '${S}'.`,`"${S}" is not a valid whole number.`,"Enter a whole number (e.g., 25).")):d.reason==="negative"&&s.push(o("teachers_licensed","teachers_licensed cannot be negative.","Number of teacher licenses cannot be negative.","Enter 0 or a positive whole number."))}w.ok&&(w.num??0)===0&&(C??0)===0&&s.push(o("students_licensed","Either students_licensed or teachers_licensed must be greater than 0.","At least one of student or teacher licenses must be greater than 0.","Set students_licensed or teachers_licensed to a positive number."));const te=e.fundingSource??e.funding_source??e["Funding Source"]??"",k=String(te).trim();k?O.find(c=>c.toLowerCase()===k.toLowerCase())||s.push(o("funding_source",`Invalid funding_source '${k}'.`,`"${k}" is not a valid funding source.`,`Allowed funding sources: ${O.join(", ")}.`)):s.push(o("funding_source","Funding source is required","Please enter a funding source for this software.",`Use one of the allowed funding sources: ${O.join(", ")}.`));const D=e.purchaseDate??e.purchase_date,L=e.expirationDate??e.expiration_date;let N=null,y=null;D!=null&&String(D).trim()!==""&&(N=X(D),N?(r.purchase_date=N,r.purchaseDate=N):s.push(o("purchase_date",`Invalid purchase_date '${D}'.`,`"${D}" is not a recognized date format.`,'Use a format like "01/15/2024", "2024-01-15", or "January 15, 2024".'))),L!=null&&String(L).trim()!==""&&(y=X(L),y?(r.expiration_date=y,r.expirationDate=y):s.push(o("expiration_date",`Invalid expiration_date '${L}'.`,`"${L}" is not a recognized date format.`,'Use a format like "01/15/2025", "2025-01-15", or "January 15, 2025".'))),N&&y&&y<=N&&s.push(o("expiration_date",`expiration_date (${y}) must be after purchase_date (${N}).`,"Expiration date must be after the purchase date.","Adjust either the purchase date or expiration date so the license has a positive duration."));const se=e.otherDomains??e.other_domains??e["Other Domains"]??e["Additional Domains"]??"",K=String(se).trim();if(K){const{valid:d,invalid:c}=Ne(K);if(c.length>0){const m=c.slice(0,3).map(b=>`"${b}"`).join(", "),p=c.length>3?` (+${c.length-3} more)`:"";s.push(o("other_domains",`Invalid domain(s): ${c.join(", ")}`,`${m}${p} ${c.length===1?"isn't":"aren't"} valid domain(s).`,'Use comma-separated values like "example.com, https://app.example.com".'))}else d.length>B&&s.push(o("other_domains",`Too many domains (${d.length}); max ${B}`,`You provided ${d.length} domains, but the maximum is ${B}.`,"Reduce the list to the most important domains for this software."))}const E=e.url||e["Software URL"]||"";!E||String(E).trim()===""?s.push(o("url","URL is required","Please enter the softw
1are's website address.","Example: https://www.example.com or www.example.com")):Ae(String(E))||s.push(o("url",`Invalid URL '${E}'.`,`"${E}" doesn't look like a valid website address.`,'Use a full domain like "https://example.com" or "example.com" with a valid TLD.'));const R=String(e.schoolSelectionType||e.school_selection_type||"").toLowerCase().trim();R?Le.includes(R)||s.push(o("school_selection_type",`Invalid school_selection_type '${R}'.`,`"${R}" is not recognized. Please enter "gradeband" or "specific".`,'"gradeband" = apply to schools by grade level. "specific" = choose individual schools.')):s.push(o("school_selection_type","school_selection_type is required","Please specify how schools should be assigned for this software.",'Use "specific" to list individual schools, or "gradeband" to apply by grade level.'));const I=e.schoolName??e.school_name??e["School Name"];if(I!=null&&String(I).trim()!==""){const d=n.validSchoolNames??[],c=String(I).trim();d.length===0?s.push(o("school_name","Cannot validate school_name: school list unavailable.","School list is unavailable, so school_name cannot be validated.","Reload the page so the school list loads, then re-upload.")):new Set(d.map(p=>p.toLowerCase())).has(c.toLowerCase())||s.push(o("school_name",`Unknown school_name '${c}'.`,`"${c}" is not a school in your district.`,"Use the exact school name shown in the school dropdown of the Add Software form."))}if(R==="specific"){const d=e.selectedSchools||e.selected_schools||"",c=String(d).trim(),m=n.validSchoolNames??[],p=m.slice(0,8).join(", "),b=m.slice(0,2).join(", "),x=m.length>0?`Use the exact school names shown in the school dropdown. Available schools include: ${p}${m.length>8?"â¦":""}. Separate multiple schools with commas${b?` (e.g., "${b}")`:""}.`:"Use the exact school names shown in the school dropdown. Separate multiple schools with commas.";if(!c)s.push(o("selected_schools",'selected_schools is required when school_selection_type is "specific"',"Please list which schools should have access to this software.",x));else if(m.length===0)s.push(o("selected_schools","Cannot validate selected_schools: school list unavailable.","School list is unavailable, so selected_schools cannot be validated.","Reload the page so the school list loads, then re-upload."));else{const ae=new Set(m.map(A=>A.toLowerCase())),G=M(c).filter(A=>!ae.has(A.toLowerCase()));if(G.length>0){const A=G.map(oe=>`"${oe}"`).join(", ");s.push(o("selected_schools",`School(s) not found in district: ${G.join(", ")}`,`${A} ${G.length===1?"doesn't":"don't"} match any school in your district.`,x))}}}if(R==="gradeband"){const d=e.applicableGradeBands||e.applicable_grade_bands||"",c=String(d).trim();if(!c)s.push(o("applicable_grade_bands",'applicable_grade_bands is required when school_selection_type is "gradeband"',"Please specify which grade levels this software is for.",`Allowed values: ${h.join(", ")}.`));else{const m=new Set(h.map(x=>x.toLowerCase())),b=M(c).filter(x=>!m.has(x.toLowerCase()));b.length>0&&s.push(o("applicable_grade_bands",`Invalid grade band(s): ${b.join(", ")}`,`${b.map(x=>`"${x}"`).join(", ")} ${b.length===1?"is":"are"} not valid grade band(s).`,`Allowed grade bands: ${h.join(", ")}.`))}}const Z=String(e.grade||e.Grade||"").trim();if(Z){const c=M(Z).filter(m=>!Re.includes(m.toLowerCase()));c.length>0&&s.push(o("grade",`Invalid grade value(s): ${c.join(", ")}.`,`"${c.join(", ")}" ${c.length===1?"is":"are"} not valid grade(s).`,`Allowed grades: ${Y.join(", ")}. Separate multiple with commas (e.g., "K,1,2").`))}const ne=e.grade_range||e.Grade_Range||e["grade range"]||e["Grade Range"]||"",P=String(ne).trim();if(P){const d=je(P);if(d!==""){const m=new Set(l.map(b=>b.toLowerCase())).has(d.toLowerCase()),p=/^([Kk]|\d{1,2})\s*[-â]\s*([Kk]|\d{1,2})$/.test(d);!m&&!p&&s.push(o("grade_range",`Invalid grade_range '${P}'.`,`"${P}" is not a valid grade range.`,`Allowed: ${l.join(", ")}, or a range like "K-5", "6-8", "9-12".`))}}const T=e.sub_population??e.subPopulation??e["Sub-Population"]??e["Sub Population"];if(T!=null&&String(T).trim()!==""){const d=String(T).trim();new Set(i.map(m=>m.toLowerCase())).has(d.toLowerCase())||s.push(o("sub_population",`Invalid sub_population '${d}'.`,`"${d}" is not a valid sub-population.`,`Allowed sub-populations: ${i.join(", ")}.`))}const U=e.subject??e.Subject??e.program_population??e.programPopulation??e["Program Population"];if(U!=null&&String(U).trim()!==""){const d=M(String(U)),c=new Set(u.map(p=>p.toLowerCase())),m=d.filter(p=>!c.has(p.toLowerCase()));m.length>0&&s.push(o("subject",`Invalid subject(s): ${m.join(", ")}.`,`${m.map(p=>`"${p}"`).join(", ")} ${m.length===1?"is":"are"}
1 not valid subject(s).`,`Allowed subjects: ${u.join(", ")}. Separate multiple with commas.`))}return{valid:s.length===0,errors:s,normalized:r}},Ie=(e,t={})=>{const n=[],s=[];return e.forEach((r,u)=>{const i=u+2,{valid:h,errors:l,normalized:_}=ke(r,i,t);h?n.push(_):s.push({row:i,data:r,errors:l})}),{validRecords:n,invalidRecords:s,summary:{total:e.length,valid:n.length,invalid:s.length}}},Be=Object.freeze(Object.defineProperty({__proto__:null,VALID_GRADES:Y,getFriendlyFieldName:F,validateBulkSoftwareData:Ie},Symbol.toStringTag,{value:"Module"}));function qe({open:e,onClose:t,invalidRecords:n,validCount:s}){const r=()=>{try{const i=n.map($=>{const j=$.errors.map(g=>{const w=g.friendlyField||F(g.field),S=g.friendlyMessage||g.message,C=g.hint?` (Tip: ${g.hint})`:"";return`${w}: ${S}${C}`}).join(` 2 `);return`Row ${$.row}: 3 ${j}`}).join(` 4 5`),h=`Validation Error Report 6Generated: ${new Date().toLocaleString()} 7${"=".repeat(50)} 8 9`,l=`Summary: ${n.length} row(s) with errors 10 11`,_=new Blob([h+l+i],{type:"text/plain;charset=utf-8"}),v=URL.createObjectURL(_),f=document.createElement("a");f.href=v,f.download=`validation-errors-${new Date().toISOString().split("T")[0]}.txt`,f.rel="noopener",document.body.appendChild(f),f.click(),setTimeout(()=>{f.parentNode&&f.parentNode.removeChild(f),URL.revokeObjectURL(v)},0)}catch(i){console.error("Failed to download validation report",i),he.error("Could not download report. Please try again.")}},u=n.reduce((i,h)=>i+h.errors.length,0);return a.jsx(ie,{open:e,onOpenChange:t,children:a.jsxs(re,{className:"max-w-3xl max-h-[85vh] flex flex-col",children:[a.jsx(le,{className:"shrink-0",children:a.jsxs("div",{className:"flex items-start gap-3",children:[a.jsx(ue,{className:"h-5 w-5 text-destructive mt-0.5 flex-shrink-0"}),a.jsxs("div",{className:"flex-1",children:[a.jsx(ce,{className:"text-lg",children:"Some Rows Need Your Attention"}),a.jsxs(de,{className:"mt-2",children:["We found issues in ",n.length," row(s) that need to be fixed before uploading.",s>0&&a.jsxs("span",{className:"block mt-1 text-green-600 dark:text-green-400",children:["â ",s," row(s) were uploaded successfully!"]})]})]})]})}),a.jsx("div",{className:"flex-1 min-h-0",children:a.jsx(me,{className:"h-full max-h-full rounded-md border p-4",children:a.jsxs("div",{className:"space-y-4",children:[n.slice(0,50).map(i=>{var h;return a.jsxs("div",{className:"rounded-lg border border-destructive/20 bg-destructive/5 p-4",children:[a.jsxs("div",{className:"font-semibold text-sm mb-3 flex items-center gap-2",children:[a.jsxs("span",{className:"bg-destructive/10 text-destructive px-2 py-0.5 rounded",children:["Row ",i.row]}),a.jsxs("span",{className:"text-muted-foreground font-normal",children:[i.errors.length," issue",i.errors.length>1?"s":""," to fix"]}),((h=i.data)==null?void 0:h.name)&&a.jsxs("span",{className:"text-muted-foreground font-normal text-xs",children:["â ",i.data.name]})]}),a.jsx("ul",{className:"space-y-3",children:i.errors.map((l,_)=>a.jsx("li",{className:"text-sm",children:a.jsxs("div",{className:"flex gap-2 items-start",children:[a.jsx("div",{className:"w-1.5 h-1.5 rounded-full bg-destructive mt-2 flex-shrink-0"}),a.jsxs("div",{className:"flex-1",children:[a.jsxs("span",{className:"font-medium text-foreground",children:[l.friendlyField||F(l.field),":"]})," ",a.jsx("span",{className:"text-muted-foreground",children:l.friendlyMessage||l.message}),l.hint&&a.jsxs("div",{className:"mt-1.5 flex items-start gap-1.5 text-xs text-muted-foreground bg-muted/50 rounded px-2 py-1.5",children:[a.jsx(Se,{className:"h-3.5 w-3.5 text-amber-500 flex-shrink-0 mt-0.5"}),a.jsx("span",{children:l.hint})]})]})]})},_))})]},i.row)}),n.length>50&&a.jsxs("div",{className:"text-center text-sm text-muted-foreground py-4 border-t",children:["Showing first 50 of ",n.length," rows with errors. Download the full report to see all issues."]})]})})}),a.jsxs("div",{className:"shrink-0 flex items-center justify-between gap-3 pt-4 border-t",children:[a.jsxs("div",{className:"text-sm text-muted-foreground",children:[a.jsx("span",{className:"font-semibold text-foreground",children:u})," issue",u>1?"s":""," in"," ",a.jsx("span",{className:"font-semibold text-foreground",children:n.length})," row",n.length>
111?"s":""]}),a.jsxs("div",{className:"flex gap-2",children:[a.jsxs(H,{variant:"outline",onClick:r,className:"gap-2",children:[a.jsx(xe,{className:"h-4 w-4"}),"Download Report"]}),a.jsx(H,{onClick:t,children:"Close"})]})]})]})})}export{B as M,qe as V,Oe as a,Y as b,Be as c,Ne as p,je as s};
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.