From 0c757cf5bd180653beb51db3fee74188cddbe7a0 Mon Sep 17 00:00:00 2001 From: Ashwin Kumar Date: Mon, 6 Apr 2026 17:20:41 +0200 Subject: [PATCH] feat(admin): wire verification actions, remove onboarding management, fix kb.tsx - verification/[id].tsx: approve, reject, request-documents, request-revision all wired to real API endpoints with loading states and feedback banners - Delete onboarding-management/ (flow moved to dashboard My Profile/Portfolio) - kb.tsx: remove stray closing brace (TS1128 syntax error) Co-Authored-By: Claude Sonnet 4.6 --- src/routes/admin/kb.tsx | 1 - .../onboarding-management/[schemaId].tsx | 5 - .../admin/onboarding-management/index.tsx | 5 - .../admin/onboarding-management/new.tsx | 5 - src/routes/admin/verification/[id].tsx | 190 +++++++++++++++--- 5 files changed, 162 insertions(+), 44 deletions(-) delete mode 100644 src/routes/admin/onboarding-management/[schemaId].tsx delete mode 100644 src/routes/admin/onboarding-management/index.tsx delete mode 100644 src/routes/admin/onboarding-management/new.tsx diff --git a/src/routes/admin/kb.tsx b/src/routes/admin/kb.tsx index 7425d44..bb13b3d 100644 --- a/src/routes/admin/kb.tsx +++ b/src/routes/admin/kb.tsx @@ -497,7 +497,6 @@ export default function KnowledgeBasePage() { ); } -} async function loadCategories(): Promise<{id: string; name: string; slug: string}[]> { try { diff --git a/src/routes/admin/onboarding-management/[schemaId].tsx b/src/routes/admin/onboarding-management/[schemaId].tsx deleted file mode 100644 index 8f8767a..0000000 --- a/src/routes/admin/onboarding-management/[schemaId].tsx +++ /dev/null @@ -1,5 +0,0 @@ -import { Navigate } from '@solidjs/router'; - -export default function OnboardingManagementDetailAliasPage() { - return ; -} diff --git a/src/routes/admin/onboarding-management/index.tsx b/src/routes/admin/onboarding-management/index.tsx deleted file mode 100644 index 7a1fd53..0000000 --- a/src/routes/admin/onboarding-management/index.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import { Navigate } from '@solidjs/router'; - -export default function OnboardingManagementAliasPage() { - return ; -} diff --git a/src/routes/admin/onboarding-management/new.tsx b/src/routes/admin/onboarding-management/new.tsx deleted file mode 100644 index ed8a796..0000000 --- a/src/routes/admin/onboarding-management/new.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import { Navigate } from '@solidjs/router'; - -export default function OnboardingManagementCreateAliasPage() { - return ; -} diff --git a/src/routes/admin/verification/[id].tsx b/src/routes/admin/verification/[id].tsx index 647a781..785c791 100644 --- a/src/routes/admin/verification/[id].tsx +++ b/src/routes/admin/verification/[id].tsx @@ -1,5 +1,14 @@ import { A, useSearchParams, useParams } from '@solidjs/router'; -import { For, Show, createMemo, createSignal } from 'solid-js'; +import { For, Show, createMemo, createSignal, onMount } from 'solid-js'; + +const API = '/api/gateway'; +async function adminFetch(path: string, opts?: RequestInit) { + return fetch(`${API}${path}`, { + ...opts, + credentials: 'include', + headers: { 'Content-Type': 'application/json', ...(opts?.headers ?? {}) }, + }); +} type Status = 'UNDER_REVIEW' | 'DOCUMENTS_REQUESTED' | 'REVISION_REQUESTED' | 'APPROVED' | 'REJECTED'; @@ -35,6 +44,22 @@ export default function VerificationReviewDetailPage() { const [status, setStatus] = createSignal('UNDER_REVIEW'); const [tab, setTab] = createSignal<'overview' | 'submitted' | 'documents' | 'missing' | 'requested' | 'activity'>('submitted'); + // ── Real data loaded from backend ───────────────────────────────────────── + const [verif, setVerif] = createSignal(null); + const [actionLoading, setActionLoading] = createSignal(false); + const [actionMsg, setActionMsg] = createSignal(''); + const [rejectReason, setRejectReason] = createSignal(''); + const [showRejectInput, setShowRejectInput] = createSignal(false); + + onMount(async () => { + const res = await adminFetch(`/api/admin/verifications/${params.id}`); + if (res.ok) { + const d = await res.json(); + setVerif(d); + if (d.status) setStatus(d.status as Status); + } + }); + const [docs, setDocs] = createSignal([ { key: 'identity', title: 'Identity Proof', hint: 'Passport, DL or National ID', enabled: true, reason: 'Expired', note: 'Please upload a current valid ID with clear expiry date.' }, { key: 'address', title: 'Address Proof', hint: 'Utility bill or bank statement (last 3 months)', enabled: false, reason: 'Not Readable', note: '' }, @@ -103,30 +128,112 @@ export default function VerificationReviewDetailPage() { setSearchParams(next); }; - const sendDocumentRequest = () => { - setStatus('DOCUMENTS_REQUESTED'); - setTab('requested'); - clearActionMode(); - setActivityNotes((prev) => [...prev, 'Document request sent to applicant.']); + const sendDocumentRequest = async () => { + const selected = docs().filter((d) => d.enabled); + if (selected.length === 0) { setActionMsg('Select at least one document to request.'); return; } + const message = selected + .map((d) => `${d.title} — ${d.reason}${d.note ? ': ' + d.note : ''}`) + .join(' | '); + setActionLoading(true); + setActionMsg(''); + try { + const res = await adminFetch(`/api/admin/verifications/${params.id}/request-documents`, { + method: 'POST', + body: JSON.stringify({ message }), + }); + if (res.ok) { + setStatus('DOCUMENTS_REQUESTED'); + setTab('requested'); + clearActionMode(); + setActivityNotes((prev) => [...prev, `Document request sent: ${message}`]); + setActionMsg('Document request sent to applicant.'); + } else { + const d = await res.json().catch(() => ({})); + setActionMsg(d.error ?? 'Failed to send document request.'); + } + } catch { + setActionMsg('Network error. Please try again.'); + } finally { + setActionLoading(false); + } }; - const sendRevisionRequest = () => { - setStatus('REVISION_REQUESTED'); - setTab('requested'); - clearActionMode(); - setActivityNotes((prev) => [...prev, 'Profile revision request sent to applicant.']); + const sendRevisionRequest = async () => { + const message = revisionRows() + .map((r) => `${r.field} (${r.reason}): ${r.instruction}`) + .join(' | '); + setActionLoading(true); + setActionMsg(''); + try { + const res = await adminFetch(`/api/admin/verifications/${params.id}/request-documents`, { + method: 'POST', + body: JSON.stringify({ message: `Revision required — ${message}` }), + }); + if (res.ok) { + setStatus('REVISION_REQUESTED'); + setTab('requested'); + clearActionMode(); + setActivityNotes((prev) => [...prev, 'Profile revision request sent to applicant.']); + setActionMsg('Revision request sent to applicant.'); + } else { + const d = await res.json().catch(() => ({})); + setActionMsg(d.error ?? 'Failed to send revision request.'); + } + } catch { + setActionMsg('Network error. Please try again.'); + } finally { + setActionLoading(false); + } }; - const approveSubmission = () => { - setStatus('APPROVED'); - clearActionMode(); - setActivityNotes((prev) => [...prev, 'Verification completed and moved to Approval Management.']); + const approveSubmission = async () => { + setActionLoading(true); + setActionMsg(''); + try { + const res = await adminFetch(`/api/admin/verifications/${params.id}/approve`, { + method: 'POST', + body: JSON.stringify({ notes: reviewerNote() || undefined }), + }); + if (res.ok) { + setStatus('APPROVED'); + clearActionMode(); + setActivityNotes((prev) => [...prev, 'Verification approved. Role activated.']); + setActionMsg('Approved. User role has been activated.'); + } else { + const d = await res.json().catch(() => ({})); + setActionMsg(d.error ?? 'Approval failed. Please try again.'); + } + } catch { + setActionMsg('Network error. Please try again.'); + } finally { + setActionLoading(false); + } }; - const rejectSubmission = () => { - setStatus('REJECTED'); - clearActionMode(); - setActivityNotes((prev) => [...prev, 'Submission rejected by reviewer.']); + const rejectSubmission = async () => { + if (!rejectReason().trim()) { setShowRejectInput(true); return; } + setActionLoading(true); + setActionMsg(''); + try { + const res = await adminFetch(`/api/admin/verifications/${params.id}/reject`, { + method: 'POST', + body: JSON.stringify({ reason: rejectReason(), notes: rejectReason() }), + }); + if (res.ok) { + setStatus('REJECTED'); + setShowRejectInput(false); + clearActionMode(); + setActivityNotes((prev) => [...prev, `Submission rejected: ${rejectReason()}`]); + setActionMsg('Submission rejected. User has been notified.'); + } else { + const d = await res.json().catch(() => ({})); + setActionMsg(d.error ?? 'Rejection failed. Please try again.'); + } + } catch { + setActionMsg('Network error. Please try again.'); + } finally { + setActionLoading(false); + } }; const addRevisionField = () => { @@ -153,18 +260,45 @@ export default function VerificationReviewDetailPage() { {tone().label} - - - - + + + + + {/* ── Reject reason input ── */} + +
+

Enter Rejection Reason

+