/** * VerificationStatusPage — shows the user their current verification state. * Handles: NOT_SUBMITTED, PENDING, UNDER_REVIEW, DOCUMENTS_REQUESTED, * REVISION_REQUESTED, APPROVED, REJECTED. * Tabs: approval status, documents, activity */ import { For, Show, createSignal, onMount } from 'solid-js'; import { ShieldCheck, FileText, Activity } from 'lucide-solid'; import { CARD, BTN_ORANGE, BTN_GHOST } from '~/components/DashboardShell'; const API = '/api/gateway'; const NAVY = '#0D0D2A'; const ORANGE = '#FF5E13'; type TabKey = 'approval_status' | 'documents' | 'activity'; async function apiFetch(path: string, opts?: RequestInit) { const token = typeof window !== "undefined" ? window.sessionStorage.getItem("nxtgauge_access_token") || "" : ""; const cleanPath = path.startsWith("/api/") ? path.slice(4) : path; return fetch(`${API}${cleanPath}`, { ...opts, credentials: "include", headers: { "Content-Type": "application/json", ...(token ? { Authorization: `Bearer ${token}` } : {}), ...(opts?.headers ?? {}), }, }); } // ── Status config ───────────────────────────────────────────────────────────── const STATUS_CONFIG: Record = { NOT_SUBMITTED: { emoji: '📋', label: 'Not Submitted', color: '#6B7280', bg: '#F9FAFB', border: '#E5E7EB', description: 'Complete your My Profile and My Portfolio, then submit for verification to start using the platform.', }, PENDING: { emoji: '⏳', label: 'Pending Review', color: '#92400E', bg: '#FFFBEB', border: '#FDE68A', description: 'Your profile has been submitted and is in the review queue. We typically respond within 24–48 hours.', }, UNDER_REVIEW: { emoji: '🔍', label: 'Under Review', color: '#1E40AF', bg: '#EEF2FF', border: '#BFDBFE', description: 'Our team is actively reviewing your submission. You will be notified once a decision is made.', }, DOCUMENTS_REQUESTED: { emoji: '📎', label: 'Documents Requested', color: '#C2410C', bg: '#FFF7ED', border: '#FED7AA', description: 'Admin has requested additional or clearer documents. Please review the message below and resubmit.', }, REVISION_REQUESTED: { emoji: '✏️', label: 'Revision Requested', color: '#C2410C', bg: '#FFF7ED', border: '#FED7AA', description: 'Admin has requested changes to your profile information. Please update and resubmit.', }, APPROVED: { emoji: '✅', label: 'Approved', color: '#065F46', bg: '#ECFDF5', border: '#6EE7B7', description: 'Your profile has been verified and approved. You now have full access to the platform.', }, REJECTED: { emoji: '❌', label: 'Rejected', color: '#991B1B', bg: '#FEF2F2', border: '#FECACA', description: 'Your verification was rejected. Please review the reason below, update your profile, and resubmit.', }, }; const FLOW_STEPS = [ { key: 'submit', label: 'Submit Profile' }, { key: 'review', label: 'Under Review' }, { key: 'verify', label: 'Verified' }, { key: 'approved', label: 'Approved' }, ]; function stepIndex(status: string): number { switch (status) { case 'NOT_SUBMITTED': return 0; case 'PENDING': return 1; case 'UNDER_REVIEW': return 2; case 'DOCUMENTS_REQUESTED': case 'REVISION_REQUESTED': return 2; case 'APPROVED': return 4; default: return 1; } } // ── Component ───────────────────────────────────────────────────────────────── interface Props { roleKey: string; onNavigate?: (sidebar: string) => void; onVerificationStatusChange?: (status: string) => void; } export default function VerificationStatusPage(props: Props) { const [activeTab, setActiveTab] = createSignal('approval_status'); const [status, setStatus] = createSignal('NOT_SUBMITTED'); const [docRequest, setDocRequest] = createSignal(null); const [rejectionReason, setRejectionReason] = createSignal(null); const [updatedAt, setUpdatedAt] = createSignal(null); const [loading, setLoading] = createSignal(true); const [resubmitting, setResubmitting] = createSignal(false); const [resubmitMsg, setResubmitMsg] = createSignal(''); const [activityLog, setActivityLog] = createSignal>([]); onMount(async () => { try { const res = await apiFetch(`/api/me/verification-status?roleKey=${props.roleKey}`); if (res.ok) { const d = await res.json(); const nextStatus = String(d.status ?? 'NOT_SUBMITTED'); setStatus(nextStatus); props.onVerificationStatusChange?.(nextStatus); setDocRequest(d.document_request ?? null); setRejectionReason(d.rejection_reason ?? null); setUpdatedAt(d.updated_at ?? null); if (d.activity_log) { setActivityLog(d.activity_log); } } } finally { setLoading(false); } }); const cfg = () => STATUS_CONFIG[status()] ?? STATUS_CONFIG.NOT_SUBMITTED; const currentStep = () => stepIndex(status()); const canResubmit = () => ['DOCUMENTS_REQUESTED', 'REVISION_REQUESTED', 'REJECTED'].includes(status()); const handleResubmit = async () => { setResubmitting(true); setResubmitMsg(''); try { const res = await apiFetch('/api/profile/submit-for-verification', { method: 'POST', body: JSON.stringify({ roleKey: props.roleKey }), }); const d = await res.json().catch(() => ({})); if (res.ok) { setStatus('PENDING'); props.onVerificationStatusChange?.('PENDING'); setDocRequest(null); setRejectionReason(null); setResubmitMsg('Resubmitted successfully! We will review your profile.'); } else if (res.status === 409) { setResubmitMsg(d.error ?? 'A verification is already in progress.'); } else { setResubmitMsg(d.error ?? 'Resubmission failed. Please try again.'); } } catch { setResubmitMsg('Network error. Please try again.'); } finally { setResubmitting(false); } }; const TABS: { key: TabKey; label: string; Icon: any }[] = [ { key: 'approval_status', label: 'Approval Status', Icon: ShieldCheck }, { key: 'documents', label: 'Documents', Icon: FileText }, { key: 'activity', label: 'Activity', Icon: Activity }, ]; return (

Verification Portal

Track verification progress, documents, and updates.

{/* Tabs */}
{(tab) => ( )}
Loading verification status…
{/* ── Approval Status Tab ─────────────────────────────────────── */} {/* Main status card */}
{cfg().emoji}

Verification Status

{cfg().label}

{cfg().description}

Last updated: {new Date(updatedAt()!).toLocaleString('en-IN')}

{/* Doc request / rejection reason */}

Document Request from Admin

{docRequest()}

Rejection Reason

{rejectionReason()}

{/* Progress timeline */}

Verification Progress

{(step, idx) => { const done = currentStep() > idx(); const active = currentStep() === idx() + 1; return ( <>
{done ? '✓' : idx() + 1}

{step.label}

); }}
{/* Actions */}

{resubmitMsg()}

🎉

You're Verified!

Your profile is approved. Start exploring opportunities on Nxtgauge.

{/* ── Documents Tab ───────────────────────────────────────────── */}

Submitted Documents

No documents submitted yet. Complete your profile to submit documents for verification.

Document Request

{docRequest()}

Rejection Reason

{rejectionReason()}

Identity Proof

{status() === 'APPROVED' ? '✓ Verified' : status() === 'REJECTED' ? '✕ Rejected' : '◌ Pending review'}

Address Proof

{status() === 'APPROVED' ? '✓ Verified' : status() === 'REJECTED' ? '✕ Rejected' : '◌ Pending review'}

Professional Certifications

{status() === 'APPROVED' ? '✓ Verified' : status() === 'REJECTED' ? '✕ Rejected' : '◌ Pending review'}

{/* ── Activity Tab ────────────────────────────────────────────── */}

Verification Activity

Profile created

{updatedAt() ? new Date(updatedAt()!).toLocaleString('en-IN') : 'Just now'}

Profile submitted for verification

{updatedAt() ? new Date(updatedAt()!).toLocaleString('en-IN') : 'Recently'}

Profile approved

{updatedAt() ? new Date(updatedAt()!).toLocaleString('en-IN') : 'Recently'}

0}>
{(item) => (

{item.action}

{item.date}

{item.details}

)}
); }