PageSourceSearch

https://knuckl.com/assets/CredentialOversight-CCUXd1qt.js

js knuckl.com collected 2026-09-24 14:47:46 UTC 6,812 bytes, 1 lines download raw bytes

1import{u as q,a as _,c as z,j as e}from"./vendor-query-BxlSJxfF.js";import{m as T,B as I,S as P,T as V,s as c,e as C}from"./index-DO0aGFYL.js";import{B as j}from"./badge-4jZnSn2H.js";import{T as $,a as F,b as A,c as n,d as K,e as o}from"./table-BvQttBPW.js";import{T as L,a as O,b as k,c as g}from"./tabs-BC396HY3.js";import{A as Q}from"./AdminPage-Bk0BX7la.js";import"./AdminCard-BSBYnc2T.js";import{A as p}from"./AdminMetric-DUcp41Nj.js";import{p as W}from"./parseISO-B5tfR1s8.js";import{d as H}from"./differenceInDays-DtcEJM-I.js";import{C as U}from"./circle-check-BzeG-ygw.js";import{C as G}from"./clock-TDYRjmmL.js";import"./vendor-react-DdNhUK9-.js";import"./vendor-supabase-Csx7YFID.js";import"./index-B9yCq0Ag.js";import"./index-Ce1IEqmK.js";import"./index-Bidgsva9.js";function pe(){const M=q(),[y,v]=_.useState(null),{data:u,isLoading:R}=z({queryKey:["admin-credentials"],queryFn:async()=>{const{data:r,error:a}=await c.from("worker_profiles").select("id, user_id, credentials, updated_at").not("credentials","is",null);if(a)throw a;const s=(r??[]).filter(d=>{const l=d.credentials;return Array.isArray(l)&&l.length>0}),i=Array.from(new Set(s.map(d=>
1d.user_id))).filter(Boolean);let t=new Map;if(i.length){const{data:d}=await c.from("profiles").select("id, full_name, phone").in("id",i);for(const l of d??[])t.set(l.id,{full_name:l.full_name,phone:l.phone})}return s.map(d=>({id:d.id,user_id:d.user_id,credentials:d.credentials??[],updated_at:d.updated_at,profile:t.get(d.user_id)??{full_name:null,phone:null}}))}}),m=_.useMemo(()=>{var a,s;if(!u)return[];const r=[];for(const i of u){const t=Array.isArray(i.credentials)?i.credentials:[];for(const d of t)!d||!d.skill||r.push({worker_profile_id:i.id,user_id:i.user_id,worker_name:((a=i.profile)==null?void 0:a.full_name)??"—",phone:((s=i.profile)==null?void 0:s.phone)??null,updated_at:i.updated_at,credential:d})}return r.sort((i,t)=>i.worker_name.localeCompare(t.worker_name))},[u]),f=_.useMemo(()=>{const r=new Set,a=new Set,s=new Set,i=new Set;for(const t of m)r.add(t.worker_profile_id),t.credential.verified&&s.add(t.worker_profile_id),t.credential.expired&&i.add(t.worker_profile_id),!t.credential.verified&&!t.credential.expired&&a.add(t.worker_profile_id);return{total:r.size,selfReported:a.size,verified:s.size,expired:i.size}},[m]),b=m.filter(r=>!r.credential.verified&&!r.credential.expired),w=m.filter(r=>r.credential.expired),B=async r=>{var s;const a=`${r.worker_profile_id}::${r.credential.skill}`;v(a);try{const{data:i,error:t}=await c.from("worker_profiles").select("credentials").eq("id",r.worker_profile_id).single();if(t)throw t;const l=(Array.isArray(i==null?void 0:i.credentials)?i.credentials:[]).map(h=>h.skill===r.credential.skill?{...h,verified:!0,self_reported:!1,verified_at:new Date().toISOString()}:h),{error:S}=await c.from("worker_profiles").update({credentials:l}).eq("id",r.worker_profile_id);if(S)throw S;const{data:x}=await c.auth.getUser();await c.from("admin_events").insert({admin_id:((s=x==null?void 0:x.user)==null?void 0:s.id)??null,target_user_id:r.user_id,action:"credential_manually_verified",details:{worker_profile_id:r.worker_profile_id,skill:r.credential.skill,license_number:r.credential.license_number,verified_at:new Date().toISOString()}}),C.success(`Marked ${r.credential.skill} verified`),await M.invalidateQueries({queryKey:["admin-credentials"]})}catch{C.error("Failed to mark verified")}finally{v(null)}},E=r=>{if(!r)return e.jsx("span",{className:"text-muted-foreground",children:"—"});try{const a=W(r),s=H(a,new Date),i=T(a,"MMM d, yyyy");return s<0?e.jsxs("span",{className:"text-destructive font-medium",children:[i," (expired)"]}):s<=30?e.jsxs("span",{className:"text-amber-500 font-medium",children:[i," (",s,"d)"]}):e.jsx("span",{children:i})}catch{return e.jsx("span",{children:r})}},D=r=>r.verified?e.jsxs(j,{className:"bg-emerald-500/15 text-emerald-600 border-emerald-500/30",children:[e.jsx(U,{className:"h-3 w-3 mr-1"})," Verified"]}):r.expired?e.jsxs(j,{variant:"outline",className:"border-amber-500/40 text-amber-600",children:[e.jsx(V,{className:"h-3 w-3 mr-1"})," Expired"]}):e.jsxs(j,{variant:"secondary",children:[e.jsx(G,{className:"h-3 w-3 mr-1"}
1)," Self-reported"]}),N=(r,a)=>R?e.jsx("p",{className:"text-sm text-muted-foreground p-4",children:"Loading…"}):r.length?e.jsx("div",{className:"rounded-lg border border-border overflow-x-auto",children:e.jsxs($,{children:[e.jsx(F,{children:e.jsxs(A,{children:[e.jsx(n,{children:"Worker"}),e.jsx(n,{children:"Skill"}),e.jsx(n,{children:"License #"}),e.jsx(n,{children:"Type"}),e.jsx(n,{children:"Expiration"}),e.jsx(n,{children:"Added"}),e.jsx(n,{children:"Status"}),a&&e.jsx(n,{className:"text-right",children:"Action"})]})}),e.jsx(K,{children:r.map(s=>{const i=`${s.worker_profile_id}::${s.credential.skill}`,t=s.credential.added_at??s.updated_at;return e.jsxs(A,{children:[e.jsx(o,{className:"font-medium",children:s.worker_name}),e.jsx(o,{children:s.credential.skill}),e.jsx(o,{className:"font-mono text-xs",children:s.credential.license_number||"—"}),e.jsx(o,{className:"text-sm text-muted-foreground",children:s.credential.license_type||"—"}),e.jsx(o,{children:E(s.credential.expiration_date)}),e.jsx(o,{className:"text-xs text-muted-foreground",children:t?T(new Date(t),"MMM d, yyyy"):"—"}),e.jsx(o,{children:D(s.credential)}),a&&e.jsx(o,{className:"text-right",children:e.jsxs(I,{size:"sm",variant:"outline",disabled:y===i,onClick:()=>B(s),children:[e.jsx(P,{className:"h-3.5 w-3.5 mr-1"}),y===i?"Marking…":"Mark verified"]})})]},i)})})]})}):e.jsx("p",{className:"text-sm text-muted-foreground p-4",children:"No credentials in this view."});return e.jsxs(Q,{title:"Credentials",subtitle:"Worker-reported licenses & cards. Review, verify, and monitor expirations.",children:[e.jsxs("div",{className:"grid grid-cols-2 md:grid-cols-4 gap-3",children:[e.jsx(p,{label:"Self-reported",value:f.selfReported}),e.jsx(p,{label:"Verified",value:f.verified,tone:"good"}),e.jsx(p,{label:"Expired",value:f.expired,tone:"warn"}),e.jsx(p,{label:"Total workers",value:f.total})]}),e.jsxs(L,{defaultValue:"self_reported",className:"w-full",children:[e.jsxs(O,{children:[e.jsxs(k,{value:"self_reported",children:["Self-reported (",b.length,")"]}),e.jsxs(k,{value:"expired",children:["Expired (",w.length,")"]}),e.jsx(k,{value:"dbpr",children:"DBPR failures"})]}),e.jsx(g,{value:"self_reported",className:"mt-4",children:N(b,!0)}),e.jsx(g,{value:"expired",className:"mt-4",children:N(w,!1)}),e.jsx(g,{value:"dbpr",className:"mt-4",children:e.jsx("div",{className:"rounded-lg border border-dashed border-border p-8 text-center",children:e.jsx("p",{className:"text-sm text-muted-foreground",children:"DBPR automatic verification will appear here once the Florida DBPR integration is live."})})})]})]})}export{pe 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.