import { A } from '@solidjs/router'; import { For, Show, createMemo, createSignal, onMount } from 'solid-js'; type VerificationStatus = 'PENDING' | 'UNDER_REVIEW' | 'DOCUMENTS_REQUESTED' | 'REVISION_REQUESTED' | 'APPROVED' | 'REJECTED'; type VerificationPriority = 'HIGH' | 'MEDIUM' | 'LOW'; type VerificationRow = { id: string; applicantName: string; requestType: | 'Profile Approval' | 'Portfolio Approval' | 'Company Approval' | 'Job Seeker Approval' | 'Service Seeker Profile Approval' | 'Service Seeker Requirement' | 'Job Approval'; roleLabel: string; submittedOn: string; status: VerificationStatus; priority: VerificationPriority; userType: 'PROFESSIONAL' | 'COMPANY' | 'JOBSEEKER' | 'CUSTOMER'; area: string; userId: string; roleKey: string; payload?: any; }; type SubmittedDocument = { id: string; title: string; type: 'IMAGE' | 'PDF'; url: string; status: 'SUBMITTED' | 'MISSING' | 'INVALID'; }; type PortfolioAsset = { id: string; title: string; url: string; }; type ApprovalQueueItem = { id: string; requestType: VerificationRow['requestType']; applicantName: string; roleLabel: string; userType: VerificationRow['userType']; roleKey: string; area: string; submittedOn: string; documents: SubmittedDocument[]; submittedFields: Array<{ label: string; value: string }>; }; const ROLE_PROFILE_FIELDS: Record = { CUSTOMER: ['First Name', 'Last Name', 'Email Address', 'Mobile Number', 'Area', 'Place', 'PIN Code', 'Service Category'], COMPANY: ['Company Name', 'Company Email', 'Company Phone', 'City', 'Area', 'Place', 'PIN Code', 'Website URL'], JOB_SEEKER: ['First Name', 'Last Name', 'Email Address', 'Mobile Number', 'Current Role', 'Total Experience', 'City', 'Area', 'Place'], PROFESSIONAL: ['First Name', 'Last Name', 'Email Address', 'Mobile Number', 'Gender', 'Address Line 1', 'Address Line 2 (Optional)', 'City', 'Area', 'State', 'PIN Code'], }; const ROLE_DOCUMENTS: Record = { CUSTOMER: ['Identity Proof', 'Address Proof'], COMPANY: ['GST Certificate', 'PAN Card', 'Incorporation Certificate'], JOB_SEEKER: ['Identity Proof', 'Address Proof', 'Education Proof'], PHOTOGRAPHER: ['Identity Proof', 'Address Proof', 'Portfolio Ownership Proof'], MAKEUP_ARTIST: ['Identity Proof', 'Address Proof', 'Professional Certifications'], DEVELOPER: ['Identity Proof', 'Address Proof', 'Tax Document'], VIDEO_EDITOR: ['Identity Proof', 'Address Proof', 'Tax Document'], UGC_CONTENT_CREATOR: ['Identity Proof', 'Address Proof', 'Tax Document'], GRAPHIC_DESIGNER: ['Identity Proof', 'Address Proof', 'Tax Document'], SOCIAL_MEDIA_MANAGER: ['Identity Proof', 'Address Proof', 'Tax Document'], FITNESS_TRAINER: ['Identity Proof', 'Address Proof', 'Certification Proof'], TUTOR: ['Identity Proof', 'Address Proof', 'Educational Proof'], CATERING_SERVICES: ['Identity Proof', 'Address Proof', 'Food License'], PROFESSIONAL: ['Identity Proof', 'Address Proof', 'Tax Document'], }; const REQUIREMENT_ROLE_FIELDS: Record = { PHOTOGRAPHER: ['Event Type', 'Shoot Type', 'Event Date & Time', 'Event Duration (Hours)', 'Venue / Location', 'Number of People', 'Delivery Deadline'], MAKEUP_ARTIST: ['Event Type', 'Makeup Category', 'Event Date & Time', 'Artists Required', 'Venue / Location', 'Skin Tone Preference', 'Ready By Time'], TUTOR: ['Subject', 'Class / Grade', 'Mode (Online / Offline)', 'Sessions Per Week', 'Preferred Start Date', 'Student Location', 'Exam Goal'], DEVELOPER: ['Project Type', 'Platform', 'Preferred Stack', 'Project Duration', 'Launch Deadline', 'Team Size Needed', 'Support Duration'], VIDEO_EDITOR: ['Video Category', 'Final Duration', 'Footage Volume', 'Delivery Date', 'Editing Style', 'Platform', 'Revision Rounds'], UGC_CONTENT_CREATOR: ['Campaign Goal', 'Platform', 'Deliverables Needed', 'Brand Category', 'Delivery Deadline', 'Target Audience', 'Usage Rights Duration'], FITNESS_TRAINER: ['Primary Goal', 'Current Activity Level', 'Preferred Mode', 'Training Days Per Week', 'Preferred Timings', 'Health Conditions', 'Goal Timeline'], CATERING_SERVICES: ['Event Type', 'Cuisine Preference', 'Guest Count', 'Service Date', 'Venue / Location', 'Meal Slot', 'Serving Style'], GRAPHIC_DESIGNER: ['Project Type', 'Brand Industry', 'Deliverables Needed', 'Deadline', 'Target Audience', 'Reference Links', 'Output Formats'], SOCIAL_MEDIA_MANAGER: ['Primary Goal', 'Platforms', 'Posting Frequency', 'Campaign Duration', 'Start Date', 'Brand Category', 'Monthly Budget'], }; const JOB_POSTING_FIELDS = [ 'Job Title', 'Department', 'Job Category', 'Employment Type', 'Seniority', 'Openings', 'Role & Requirements', 'Compensation', 'Location', 'Description', ]; const APPROVAL_QUEUE_STORAGE_KEY = 'nxtgauge_admin_approval_queue'; const API = ''; const toTitle = (value: string) => String(value || '').replace(/_/g, ' ').replace(/\b\w/g, (c) => c.toUpperCase()); const statusUi = (status: VerificationStatus) => { if (status === 'APPROVED') return { bg: '#ECFDF3', border: '#BBF7D0', text: '#166534', label: 'Approved' }; if (status === 'UNDER_REVIEW') return { bg: '#EEF2FF', border: '#C7D2FE', text: '#3730A3', label: 'Under Review' }; if (status === 'DOCUMENTS_REQUESTED') return { bg: '#FFF7ED', border: '#FED7AA', text: '#C2410C', label: 'Document Requested' }; if (status === 'REVISION_REQUESTED') return { bg: '#FFF7ED', border: '#FED7AA', text: '#C2410C', label: 'Revision Requested' }; if (status === 'REJECTED') return { bg: '#FEF2F2', border: '#FECACA', text: '#B91C1C', label: 'Rejected' }; return { bg: '#FFFBEB', border: '#FDE68A', text: '#92400E', label: 'Pending' }; }; const priorityUi = (priority: VerificationPriority) => { if (priority === 'HIGH') return { color: '#DC2626', label: 'High' }; if (priority === 'MEDIUM') return { color: '#D97706', label: 'Medium' }; return { color: '#64748B', label: 'Low' }; }; const parseDate = (value: string) => { const ts = Date.parse(String(value || '')); return Number.isNaN(ts) ? 0 : ts; }; const normalizeRoleSpecKey = (value: string) => { const key = String(value || '').toUpperCase(); if (key.includes('COMPANY')) return 'COMPANY'; if (key.includes('CUSTOMER') || key.includes('SERVICE_SEEKER')) return 'CUSTOMER'; if (key.includes('JOB_SEEKER') || key.includes('JOBSEEKER')) return 'JOB_SEEKER'; if (key.includes('PHOTOGRAPHER') || key.includes('PHOTO')) return 'PHOTOGRAPHER'; if (key.includes('MAKEUP')) return 'MAKEUP_ARTIST'; if (key.includes('DEVELOPER')) return 'DEVELOPER'; if (key.includes('VIDEO')) return 'VIDEO_EDITOR'; if (key.includes('UGC') || (key.includes('CONTENT') && key.includes('CREATOR'))) return 'UGC_CONTENT_CREATOR'; if (key.includes('GRAPHIC')) return 'GRAPHIC_DESIGNER'; if (key.includes('SOCIAL')) return 'SOCIAL_MEDIA_MANAGER'; if (key.includes('FITNESS')) return 'FITNESS_TRAINER'; if (key.includes('TUTOR')) return 'TUTOR'; if (key.includes('CATER')) return 'CATERING_SERVICES'; return 'PROFESSIONAL'; }; export default function VerificationManagementPage() { const [rows, setRows] = createSignal([]); const [search, setSearch] = createSignal(''); const [statusFilter, setStatusFilter] = createSignal<'ALL' | VerificationStatus>('ALL'); const [sortBy, setSortBy] = createSignal<'latest' | 'oldest' | 'priority'>('latest'); const [sortOpen, setSortOpen] = createSignal(false); const [filterOpen, setFilterOpen] = createSignal(false); const [error, setError] = createSignal(''); const [categoryTab, setCategoryTab] = createSignal<'all' | 'profile' | 'portfolio' | 'company' | 'job_seeker' | 'service_profile' | 'service_requirement' | 'job'>('all'); const [listTab, setListTab] = createSignal<'all' | 'view'>('all'); const [selectedRow, setSelectedRow] = createSignal(null); const [viewer, setViewer] = createSignal<{ open: boolean; title: string; type: 'IMAGE' | 'PDF'; url: string }>({ open: false, title: '', type: 'IMAGE', url: '', }); const [docSelection, setDocSelection] = createSignal>({}); const [requestNote, setRequestNote] = createSignal(''); const [actionMessage, setActionMessage] = createSignal(''); const load = async () => { try { setError(''); const res = await fetch(`${API}/api/admin/verifications?page=1&limit=200`, { headers: { Accept: 'application/json' }, credentials: 'include', }); if (!res.ok) throw new Error(`Failed to load verification queue (${res.status})`); const data = await res.json().catch(() => ({} as any)); const items = Array.isArray(data?.items) ? data.items : []; const mergedRows: VerificationRow[] = items.map((v: any) => { const payload = v.payload || {}; const userType = (v.type === 'job_approval' ? 'COMPANY' : (v.type === 'requirement_approval' ? 'CUSTOMER' : 'PROFESSIONAL')) as VerificationRow['userType']; return { id: v.id, applicantName: v.user_name || 'Applicant', requestType: (v.type === 'job_approval' ? 'Job Approval' : (v.type === 'requirement_approval' ? 'Service Seeker Requirement' : 'Profile Approval')) as VerificationRow['requestType'], roleLabel: toTitle(v.role_key || 'User'), submittedOn: v.created_at, status: v.status as VerificationStatus, priority: 'MEDIUM', userType, area: payload.city || payload.area || 'Unknown', userId: v.user_id, roleKey: v.role_key, payload, }; }); setRows(mergedRows); } catch (e: any) { setRows([]); setError(e?.message ? e.message : 'Could not load verification queue.'); } }; onMount(() => { void load(); }); const tabCounts = createMemo(() => { const all = rows(); return { all: all.length, profile: all.filter((r) => r.requestType === 'Profile Approval').length, portfolio: all.filter((r) => r.requestType === 'Portfolio Approval').length, company: all.filter((r) => r.requestType === 'Company Approval').length, jobSeeker: all.filter((r) => r.requestType === 'Job Seeker Approval').length, serviceProfile: all.filter((r) => r.requestType === 'Service Seeker Profile Approval').length, serviceRequirement: all.filter((r) => r.requestType === 'Service Seeker Requirement').length, job: all.filter((r) => r.requestType === 'Job Approval').length, }; }); const filteredRows = createMemo(() => { const query = search().trim().toLowerCase(); const activeTab = categoryTab(); const status = statusFilter(); const sort = sortBy(); const scoped = rows().filter((row) => { if (activeTab === 'profile' && row.requestType !== 'Profile Approval') return false; if (activeTab === 'portfolio' && row.requestType !== 'Portfolio Approval') return false; if (activeTab === 'company' && row.requestType !== 'Company Approval') return false; if (activeTab === 'job_seeker' && row.requestType !== 'Job Seeker Approval') return false; if (activeTab === 'service_profile' && row.requestType !== 'Service Seeker Profile Approval') return false; if (activeTab === 'service_requirement' && row.requestType !== 'Service Seeker Requirement') return false; if (activeTab === 'job' && row.requestType !== 'Job Approval') return false; if (status !== 'ALL' && row.status !== status) return false; if (!query) return true; return [row.id, row.applicantName, row.requestType, row.roleLabel, row.area] .some((value) => String(value || '').toLowerCase().includes(query)); }); const next = [...scoped]; if (sort === 'priority') { const rank = (p: VerificationPriority) => (p === 'HIGH' ? 3 : p === 'MEDIUM' ? 2 : 1); next.sort((a, b) => rank(b.priority) - rank(a.priority)); return next; } next.sort((a, b) => (sort === 'oldest' ? 1 : -1) * (parseDate(a.submittedOn) - parseDate(b.submittedOn))); return next; }); const displayRows = createMemo(() => { return filteredRows(); }); const metrics = createMemo(() => { const all = rows(); const today = new Date().toISOString().slice(0, 10); const submittedToday = all.filter((r) => String(r.submittedOn || '').slice(0, 10) === today); return { totalPending: all.filter((r) => r.status === 'PENDING' || r.status === 'UNDER_REVIEW').length, approvedToday: submittedToday.filter((r) => r.status === 'APPROVED').length, rejectedToday: submittedToday.filter((r) => r.status === 'REJECTED').length, needsRevision: all.filter((r) => r.status === 'DOCUMENTS_REQUESTED' || r.status === 'REVISION_REQUESTED').length, }; }); const exportCsv = () => { const headers = ['Submission ID', 'Type', 'Applicant Name', 'Role', 'Submitted On', 'Status', 'Priority', 'Area']; const lines = filteredRows().map((row) => [ row.id, row.requestType, row.applicantName, row.roleLabel, String(row.submittedOn || '').slice(0, 10), row.status, row.priority, row.area, ]); const csv = [headers, ...lines] .map((line) => line.map((cell) => `"${String(cell).replace(/"/g, '""')}"`).join(',')) .join('\n'); const blob = new Blob([csv], { type: 'text/csv;charset=utf-8;' }); const url = URL.createObjectURL(blob); const link = document.createElement('a'); link.href = url; link.download = `verification-queue-${new Date().toISOString().slice(0, 10)}.csv`; document.body.appendChild(link); link.click(); document.body.removeChild(link); URL.revokeObjectURL(url); }; const selectedDocuments = createMemo(() => { const row = selectedRow(); if (!row) return []; if (row.requestType === 'Job Approval') { return [ { id: 'job_desc', title: 'Job Description File', type: 'PDF', url: '/nxtgauge-logo.png', status: 'SUBMITTED' }, { id: 'company_proof', title: 'Company Verification Snapshot', type: 'PDF', url: '/nxtgauge-icon.png', status: 'SUBMITTED' }, ]; } if (row.requestType === 'Service Seeker Requirement') { return [ { id: 'requirement_brief', title: 'Requirement Brief', type: 'PDF', url: '/nxtgauge-logo.png', status: 'SUBMITTED' }, { id: 'reference', title: 'Reference Attachment', type: 'IMAGE', url: '/nxtgauge-icon.png', status: 'SUBMITTED' }, ]; } const roleSpecKey = normalizeRoleSpecKey(row.roleKey || row.userType); const fromPayload = Array.isArray(row.payload?.documents) ? row.payload.documents : []; if (fromPayload.length) { return fromPayload.slice(0, 8).map((doc: any, idx: number) => ({ id: String(doc.id || `doc-${idx + 1}`), title: String(doc.title || doc.name || `Document ${idx + 1}`), type: String(doc.type || '').toUpperCase().includes('PDF') ? 'PDF' : 'IMAGE', url: String(doc.url || '/nxtgauge-logo.png'), status: String(doc.status || '').toUpperCase() === 'MISSING' ? 'MISSING' : String(doc.status || '').toUpperCase() === 'INVALID' ? 'INVALID' : 'SUBMITTED', })); } const docs = ROLE_DOCUMENTS[roleSpecKey] || ROLE_DOCUMENTS.PROFESSIONAL; return docs.map((title, idx) => ({ id: `${roleSpecKey.toLowerCase()}-doc-${idx + 1}`, title, type: title.toLowerCase().includes('proof') ? 'IMAGE' : 'PDF', url: idx % 2 === 0 ? '/nxtgauge-logo.png' : '/nxtgauge-icon.png', status: idx === docs.length - 1 ? 'MISSING' : 'SUBMITTED', })); }); const selectedPortfolio = createMemo(() => { const row = selectedRow(); if (!row || !(row.userType === 'PROFESSIONAL' || row.requestType === 'Portfolio Approval')) return []; const fromPayload = Array.isArray(row.payload?.portfolio_images) ? row.payload.portfolio_images : Array.isArray(row.payload?.images) ? row.payload.images : Array.isArray(row.payload?.gallery) ? row.payload.gallery : []; if (fromPayload.length) { return fromPayload.slice(0, 6).map((asset: any, idx: number) => ({ id: String(asset.id || `pf-${idx + 1}`), title: String(asset.title || asset.name || `Portfolio ${idx + 1}`), url: String(asset.url || '/nxtgauge-logo.png'), })); } return Array.from({ length: 6 }).map((_, idx) => ({ id: `pf-${idx + 1}`, title: `Portfolio ${idx + 1}`, url: idx % 2 === 0 ? '/nxtgauge-logo.png' : '/nxtgauge-icon.png', })); }); const selectedFieldValues = createMemo>(() => { const row = selectedRow(); if (!row) return []; const roleSpecKey = normalizeRoleSpecKey(row.roleKey || row.userType); const payload = row.payload || {}; const fullName = String( payload.full_name || payload.fullName || [payload.first_name || payload.firstName, payload.last_name || payload.lastName].filter(Boolean).join(' ') || row.applicantName || '', ).trim(); const email = String(payload.email || payload.email_address || payload.emailAddress || 'applicant@nxtgauge.com'); const phone = String(payload.phone || payload.mobile || payload.mobile_number || payload.mobileNumber || '+91 90000 00000'); const area = String(payload.area || row.area || 'T. Nagar'); const place = String(payload.place || payload.locality || 'Chennai'); const city = String(payload.city || 'Chennai'); const state = String(payload.state || 'Tamil Nadu'); const pin = String(payload.pin_code || payload.pinCode || '600001'); const gender = String(payload.gender || 'Not specified'); const byLabel: Record = { 'First Name': fullName.split(' ')[0] || fullName, 'Last Name': fullName.split(' ').slice(1).join(' ') || '—', 'Full Name': fullName || '—', 'Email Address': email, 'Mobile Number': phone, Area: area, Place: place, City: city, State: state, 'PIN Code': pin, Gender: gender, 'Address Line 1': String(payload.address_line1 || payload.addressLine1 || payload.address || 'No. 12, Main Road'), 'Address Line 2 (Optional)': String(payload.address_line2 || payload.addressLine2 || '—'), 'Service Category': String(payload.service_category || payload.serviceCategory || row.roleLabel || 'General'), 'Company Name': String(payload.company_name || payload.companyName || row.applicantName || '—'), 'Company Email': email, 'Company Phone': phone, 'Website URL': String(payload.website || payload.website_url || payload.websiteUrl || '—'), 'Contact Person Name': String(payload.contact_person_name || payload.contactPersonName || fullName || '—'), 'Current Role': String(payload.current_role || payload.currentRole || row.roleLabel || '—'), 'Total Experience': String(payload.total_experience || payload.totalExperience || payload.experience || '—'), }; if (row.requestType === 'Job Approval') { return JOB_POSTING_FIELDS.map((label) => ({ label, value: ({ 'Job Title': String(payload.title || payload.job_title || '—'), Department: String(payload.department || payload.company_department || '—'), 'Job Category': String(payload.category || payload.job_category || '—'), 'Employment Type': String(payload.employment_type || payload.type || '—'), Seniority: String(payload.seniority || payload.level || '—'), Openings: String(payload.openings || payload.positions || '—'), 'Role & Requirements': String(payload.requirements || payload.skills || '—'), Compensation: String(payload.salary_range || payload.compensation || '—'), Location: String(payload.location || payload.city || 'Chennai'), Description: String(payload.description || payload.summary || '—'), } as Record)[label] || '—', })); } if (row.requestType === 'Service Seeker Requirement') { const reqRoleKey = normalizeRoleSpecKey(String(payload.role_key || payload.profession || payload.category || row.roleKey)); const dynamicReqFields = REQUIREMENT_ROLE_FIELDS[reqRoleKey] || REQUIREMENT_ROLE_FIELDS.PHOTOGRAPHER; const baseReqFields = ['Requirement Title', 'Priority', 'Requirement Description', 'Expected Start Date', 'Service City', 'Contact Number']; const fields = [...baseReqFields, ...dynamicReqFields]; return fields.map((label) => ({ label, value: ({ 'Requirement Title': String(payload.title || payload.requirement_title || '—'), Priority: String(payload.priority || '—'), 'Requirement Description': String(payload.description || payload.details || '—'), 'Expected Start Date': String(payload.start_date || payload.expected_start_date || '—'), 'Service City': String(payload.city || 'Chennai'), 'Contact Number': String(payload.phone || payload.contact_number || '+91 90000 00000'), } as Record)[label] || String( payload[label] || payload[label.toLowerCase().replace(/[^a-z0-9]+/g, '_')] || '—', ), })); } if (row.requestType === 'Portfolio Approval') { const portfolioFields = [ 'About', 'Services & Pricing', 'Portfolio Photos', 'Experience & Tools', 'Specialties', 'Languages', 'Service Areas', ]; return portfolioFields.map((label) => ({ label, value: ({ About: String(payload.about || payload.bio || '—'), 'Services & Pricing': Array.isArray(payload.services) ? `${payload.services.length} service(s) added` : String(payload.pricing || '—'), 'Portfolio Photos': Array.isArray(payload.images || payload.portfolio_images || payload.gallery) ? `${(payload.images || payload.portfolio_images || payload.gallery).length} image(s)` : '0 image(s)', 'Experience & Tools': String(payload.experience || payload.tools || '—'), Specialties: Array.isArray(payload.specialties) ? payload.specialties.join(', ') : String(payload.specialties || '—'), Languages: Array.isArray(payload.languages) ? payload.languages.join(', ') : String(payload.languages || '—'), 'Service Areas': Array.isArray(payload.service_areas || payload.travel_areas) ? (payload.service_areas || payload.travel_areas).join(', ') : String(payload.service_areas || '—'), } as Record)[label] || '—', })); } const fields = ROLE_PROFILE_FIELDS[roleSpecKey] || ROLE_PROFILE_FIELDS.PROFESSIONAL; return fields.map((label) => ({ label, value: byLabel[label] || '—' })); }); const pushToApprovalQueue = (row: VerificationRow) => { if (typeof window === 'undefined') return; const item: ApprovalQueueItem = { id: row.id, requestType: row.requestType, applicantName: row.applicantName, roleLabel: row.roleLabel, userType: row.userType, roleKey: row.roleKey, area: row.area, submittedOn: row.submittedOn, documents: selectedDocuments(), submittedFields: selectedFieldValues(), }; const raw = window.localStorage.getItem(APPROVAL_QUEUE_STORAGE_KEY); const parsed = raw ? JSON.parse(raw) : []; const current = Array.isArray(parsed) ? parsed as ApprovalQueueItem[] : []; const filtered = current.filter((entry) => entry.id !== item.id); window.localStorage.setItem(APPROVAL_QUEUE_STORAGE_KEY, JSON.stringify([item, ...filtered])); }; const applySelectedStatus = async (nextStatus: VerificationStatus) => { const current = selectedRow(); if (!current) return; const isApprove = nextStatus === 'APPROVED'; const isReject = nextStatus === 'REJECTED'; if (!isApprove && !isReject) { // local update only for intermediate states if needed, but usually we skip backend call here setRows((prev) => prev.map((item) => (item.id === current.id ? { ...item, status: nextStatus } : item))); setSelectedRow({ ...current, status: nextStatus }); return; } try { const accessToken = typeof sessionStorage !== 'undefined' ? sessionStorage.getItem('nxtgauge_admin_access_token') || '' : ''; const common = { method: 'POST', headers: { 'Content-Type': 'application/json', Accept: 'application/json', ...(accessToken ? { Authorization: `Bearer ${accessToken}` } : {}), }, credentials: 'include' as const, body: isReject ? JSON.stringify({ reason: requestNote() }) : undefined, }; const endpoint = `/api/admin/verifications/${current.id}/${isApprove ? 'approve' : 'reject'}`; const res = await fetch(`${API}${endpoint}`, common); if (!res.ok) { const txt = await res.text(); throw new Error(`Failed to update status (${res.status}): ${txt}`); } setRows((prev) => prev.map((item) => (item.id === current.id ? { ...item, status: nextStatus } : item))); setSelectedRow({ ...current, status: nextStatus }); if (isApprove) { pushToApprovalQueue({ ...current, status: nextStatus }); setActionMessage('Successfully verified and sent to Approval Management.'); } else { setActionMessage('Successfully rejected submission.'); } } catch (e: any) { setError(e.message || 'Failed to update backend status'); } }; const requestSelectedDocuments = () => { const selectedIds = Object.entries(docSelection()).filter(([, checked]) => checked).map(([id]) => id); if (selectedIds.length === 0) { setActionMessage('Select at least one document before sending request.'); return; } applySelectedStatus('DOCUMENTS_REQUESTED'); setActionMessage(`Document request sent for ${selectedIds.length} item(s).`); }; const requestProfileChanges = () => { applySelectedStatus('REVISION_REQUESTED'); setActionMessage('Revision request sent to applicant.'); }; return ( <>

Verification Management

Review and verify all platform submissions before they move to approval management

{error()}
{([ { key: 'all', label: 'All Verifications' }, { key: 'view', label: 'View Verification' }, ] as const).map((tab) => ( ))}
{([ { key: 'all', label: `All Verifications (${tabCounts().all})` }, { key: 'profile', label: `Profile Approvals (${tabCounts().profile})` }, { key: 'portfolio', label: `Portfolio Approvals (${tabCounts().portfolio})` }, { key: 'company', label: `Company Approvals (${tabCounts().company})` }, { key: 'job_seeker', label: `Job Seeker Approvals (${tabCounts().jobSeeker})` }, { key: 'service_profile', label: `Service Seeker Profile (${tabCounts().serviceProfile})` }, { key: 'service_requirement', label: `Service Seeker Requirements (${tabCounts().serviceRequirement})` }, { key: 'job', label: `Job Approvals (${tabCounts().job})` }, ] as const).map((tab) => ( ))}
setSearch(e.currentTarget.value)} placeholder="Search by ID, name or type..." style="height:34px;flex:1;border-radius:8px;border:1px solid #E5E7EB;background:white;padding:0 12px;font-size:13px;color:#111827;outline:none" />
{([ { key: 'latest', label: 'Latest Submitted' }, { key: 'oldest', label: 'Oldest Submitted' }, { key: 'priority', label: 'Priority (High-Low)' }, ] as const).map((item) => ( ))}
{([ { key: 'ALL', label: 'All Status' }, { key: 'PENDING', label: 'Pending' }, { key: 'UNDER_REVIEW', label: 'Under Review' }, { key: 'DOCUMENTS_REQUESTED', label: 'Documents Requested' }, { key: 'REVISION_REQUESTED', label: 'Revision Requested' }, { key: 'APPROVED', label: 'Approved' }, { key: 'REJECTED', label: 'Rejected' }, ] as const).map((item) => ( ))}
{['Submission ID', 'Type', 'Applicant Name', 'Role', 'Submitted On', 'Status', 'Priority', 'Actions'].map((header) => ( ))} 0} fallback={ } > {(row) => { const s = statusUi(row.status); const p = priorityUi(row.priority); return ( ); }}
{header}

No verification requests found

Try changing filters or search.

#{row.id} {row.requestType}
{row.applicantName} {row.area || 'Chennai'}
{row.roleLabel} {String(row.submittedOn || '').slice(0, 10) || '—'} {s.label} {p.label}
0}>

Showing 1–{displayRows().length} of {displayRows().length} verifications

No verification selected

Click View on a row from All Verifications.

} >

#{selectedRow()!.id}

{selectedRow()!.requestType}

{statusUi(selectedRow()!.status).label}

Applicant Name

{selectedRow()!.applicantName}

Role

{selectedRow()!.roleLabel}

Area

{selectedRow()!.area || 'Chennai'}

Submitted Form Details

{selectedFieldValues().length} fields
{(entry) => (

{entry.label}

{entry.value}

)}

Submitted Documents

{selectedDocuments().length} files
{['Document', 'State', 'View', 'Request'].map((header) => ( ))} {(doc) => ( )}
{header}
{doc.title} {toTitle(doc.status)}

Verification Requirements Checklist

System Audit
{(doc) => (
}>
{doc.title}
{doc.status === 'SUBMITTED' ? 'RECEIVED' : 'MISSING'}
)}

Profile Completeness

{(field) => (
{field.label}: {field.value && field.value !== '—' ? 'Filled' : 'Empty'}
)}

Reviewer Actions