From 6c6667b8bea5ff7cd93865272216c144cb07c8b2 Mon Sep 17 00:00:00 2001 From: Ashwin Kumar Date: Thu, 19 Mar 2026 14:21:49 +0100 Subject: [PATCH] feat(admin): add jobs and verification parity routes --- src/routes/admin/jobs/[id].tsx | 118 ++++++++++++++++++ src/routes/admin/verification-status.tsx | 101 +++++++++++++++ src/routes/admin/verification-status/[id].tsx | 84 +++++++++++++ src/routes/admin/verification.tsx | 24 ++++ src/routes/admin/verification/[id].tsx | 80 ++++++++++++ 5 files changed, 407 insertions(+) create mode 100644 src/routes/admin/jobs/[id].tsx create mode 100644 src/routes/admin/verification-status.tsx create mode 100644 src/routes/admin/verification-status/[id].tsx create mode 100644 src/routes/admin/verification.tsx create mode 100644 src/routes/admin/verification/[id].tsx diff --git a/src/routes/admin/jobs/[id].tsx b/src/routes/admin/jobs/[id].tsx new file mode 100644 index 0000000..291384e --- /dev/null +++ b/src/routes/admin/jobs/[id].tsx @@ -0,0 +1,118 @@ +import { A, useParams } from '@solidjs/router'; +import { createMemo, createResource, Show } from 'solid-js'; +import AdminShell from '~/components/AdminShell'; + +const API = '/api/gateway'; + +type Job = { + id: string; + title?: string; + description?: string; + requiredSkills?: string[]; + required_skills?: string[]; + experienceLevel?: string; + experience_level?: string; + status?: string; + clientName?: string; + client_name?: string; + companyName?: string; + company_name?: string; + location?: string; + hourlyRateMin?: number; + hourlyRateMax?: number; + hourly_rate_min?: number; + hourly_rate_max?: number; + availability?: string; + durationDays?: number; + duration_days?: number; +}; + +async function fetchJob(id: string): Promise { + try { + const res = await fetch(`${API}/api/jobs/${id}`); + if (!res.ok) return null; + const data = await res.json(); + return data.job || data; + } catch { + return null; + } +} + +export default function JobDetailPage() { + const params = useParams(); + const [job] = createResource(() => params.id, fetchJob); + + const skills = createMemo(() => job()?.requiredSkills || job()?.required_skills || []); + const client = createMemo(() => job()?.clientName || job()?.client_name || job()?.companyName || job()?.company_name || '—'); + const exp = createMemo(() => job()?.experienceLevel || job()?.experience_level || '—'); + const rateMin = createMemo(() => job()?.hourlyRateMin ?? job()?.hourly_rate_min); + const rateMax = createMemo(() => job()?.hourlyRateMax ?? job()?.hourly_rate_max); + const duration = createMemo(() => job()?.durationDays ?? job()?.duration_days); + + return ( + +
+
+

Job Management

+

Review one live backend job in the same detail-first style as other admin modules.

+
+ Back to Jobs +
+ + +

Loading job...

+
+ + +

Job not found.

+
+ + +
+
+
+

Title

+

{job()!.title || '—'}

+
+
+

Status

+

{job()!.status || '—'}

+
+
+

Client

+

{client()}

+
+
+

Experience

+

{exp()}

+
+
+

Rate

+

{rateMin() != null ? `₹${rateMin()} - ₹${rateMax() ?? rateMin()}` : '—'}

+
+
+

Location

+

{job()!.location || '—'}

+
+
+

Availability

+

{job()!.availability || '—'}

+
+
+

Duration

+

{duration() != null ? `${duration()} days` : '—'}

+
+
+

Required Skills

+

{skills().length > 0 ? skills().join(', ') : '—'}

+
+
+
+

Description

+

{job()!.description || '—'}

+
+
+
+
+ ); +} diff --git a/src/routes/admin/verification-status.tsx b/src/routes/admin/verification-status.tsx new file mode 100644 index 0000000..0358a19 --- /dev/null +++ b/src/routes/admin/verification-status.tsx @@ -0,0 +1,101 @@ +import { A } from '@solidjs/router'; +import { createMemo, createResource, For, Show } from 'solid-js'; +import AdminShell from '~/components/AdminShell'; + +const API = '/api/gateway'; + +type ApprovalRow = { + id: string; + requestType?: string; + type?: string; + requestStatus?: string; + status?: string; + requester?: { name?: string; email?: string }; + requesterName?: string; + requesterEmail?: string; + createdAt?: string; + created_at?: string; +}; + +async function fetchApprovals(): Promise { + try { + const res = await fetch(`${API}/api/admin/approvals`); + if (!res.ok) return []; + const data = await res.json(); + return Array.isArray(data) ? data : (data.approvals || []); + } catch { + return []; + } +} + +export default function VerificationStatusPage() { + const [rows] = createResource(fetchApprovals); + + const normalized = createMemo(() => (rows() || []).map((r) => ({ + ...r, + status: (r.requestStatus || r.status || 'UNKNOWN').toUpperCase(), + type: (r.requestType || r.type || 'PROFILE').toUpperCase(), + requesterName: r.requester?.name || r.requesterName || 'Unknown', + requesterEmail: r.requester?.email || r.requesterEmail || '—', + createdAt: r.createdAt || r.created_at || '', + }))); + + return ( + +
+
+

Verification Status

+

Track request status states and open a specific record for follow-up.

+
+ Open Approval Center +
+ +
+
+ + + + + + + + + + + + + + + + + + + 0}> + + {(item) => ( + + + + + + + + + )} + + + +
IDTypeRequesterStatusSubmittedActions
Loading verification statuses...
No verification status records found.
{item.id.slice(0, 8)}...{item.type} +
{item.requesterName}
+
{item.requesterEmail}
+
{item.status}{item.createdAt ? new Date(item.createdAt).toLocaleString() : '—'} +
+ + 👁 +
+
+
+
+
+ ); +} diff --git a/src/routes/admin/verification-status/[id].tsx b/src/routes/admin/verification-status/[id].tsx new file mode 100644 index 0000000..072dd22 --- /dev/null +++ b/src/routes/admin/verification-status/[id].tsx @@ -0,0 +1,84 @@ +import { A, useParams } from '@solidjs/router'; +import { createMemo, createResource, Show } from 'solid-js'; +import AdminShell from '~/components/AdminShell'; + +const API = '/api/gateway'; + +type ApprovalDetail = { + id: string; + requestType?: string; + type?: string; + requestStatus?: string; + status?: string; + requester?: { name?: string; email?: string }; + requesterName?: string; + requesterEmail?: string; + createdAt?: string; + created_at?: string; + requestReason?: string; +}; + +async function fetchApproval(id: string): Promise { + try { + const res = await fetch(`${API}/api/admin/approvals/${id}`); + if (!res.ok) return null; + return res.json(); + } catch { + return null; + } +} + +export default function VerificationStatusDetailPage() { + const params = useParams(); + const [detail] = createResource(() => params.id, fetchApproval); + + const status = createMemo(() => (detail()?.requestStatus || detail()?.status || 'UNKNOWN').toUpperCase()); + const type = createMemo(() => (detail()?.requestType || detail()?.type || 'PROFILE').toUpperCase()); + const requester = createMemo(() => detail()?.requester?.name || detail()?.requesterName || 'Unknown'); + const email = createMemo(() => detail()?.requester?.email || detail()?.requesterEmail || '—'); + const submitted = createMemo(() => detail()?.createdAt || detail()?.created_at || ''); + + return ( + +
+
+

Verification Status Detail

+

Open one verification status request and jump into approval review when needed.

+
+ +
+ + +

Loading verification status...

+
+ + +

Verification status record not found.

+
+ + +
+
+

Request

+

ID: {detail()!.id}

+

Type: {type()}

+

Status: {status()}

+

Submitted: {submitted() ? new Date(submitted()).toLocaleString() : '—'}

+
+
+

Requester

+

Name: {requester()}

+

Email: {email()}

+
+
+
+

Request Reason Payload

+
{detail()!.requestReason || 'No requestReason payload found.'}
+
+
+
+ ); +} diff --git a/src/routes/admin/verification.tsx b/src/routes/admin/verification.tsx new file mode 100644 index 0000000..28fa32b --- /dev/null +++ b/src/routes/admin/verification.tsx @@ -0,0 +1,24 @@ +import { A } from '@solidjs/router'; +import AdminShell from '~/components/AdminShell'; + +export default function VerificationManagementPage() { + return ( + +
+

Approval Management

+

+ Admin review now lives under Approval Management. Verification remains a user-facing status concept. +

+
+ +
+

+ Use Approval Management to review companies, customers, candidates, and professional submissions. +

+ +
+
+ ); +} diff --git a/src/routes/admin/verification/[id].tsx b/src/routes/admin/verification/[id].tsx new file mode 100644 index 0000000..25c5d73 --- /dev/null +++ b/src/routes/admin/verification/[id].tsx @@ -0,0 +1,80 @@ +import { A, useParams } from '@solidjs/router'; +import { createMemo, createResource, Show } from 'solid-js'; +import AdminShell from '~/components/AdminShell'; + +const API = '/api/gateway'; + +type Approval = { + id: string; + requestStatus?: string; + status?: string; + requestType?: string; + type?: string; + requester?: { name?: string; email?: string }; + requesterName?: string; + requesterEmail?: string; + requestReason?: string; +}; + +async function fetchApproval(id: string): Promise { + try { + const res = await fetch(`${API}/api/admin/approvals/${id}`); + if (!res.ok) return null; + return res.json(); + } catch { + return null; + } +} + +export default function VerificationDetailPage() { + const params = useParams(); + const [approval] = createResource(() => params.id, fetchApproval); + + const status = createMemo(() => (approval()?.requestStatus || approval()?.status || 'PENDING').toUpperCase()); + const type = createMemo(() => (approval()?.requestType || approval()?.type || 'PROFILE').toUpperCase()); + const requester = createMemo(() => approval()?.requester?.name || approval()?.requesterName || 'Unknown'); + const email = createMemo(() => approval()?.requester?.email || approval()?.requesterEmail || '—'); + + return ( + +
+
+

Verification Review

+

Review submission context, documents, and verification decision state.

+
+ +
+ + +

Loading verification detail...

+
+ +

Verification request not found.

+
+ +
+
+

Summary

+

Approval ID: {approval()!.id}

+

Type: {type()}

+

Status: {status()}

+

Requester: {requester()}

+

Email: {email()}

+
+
+

Remark Snapshot

+

+ This route mirrors the Next.js verification detail entry point and delegates action workflow to Approval Management. +

+ +
+
+
+
+ ); +}