feat(admin): add route aliases and missing detail pages for parity

This commit is contained in:
Ashwin Kumar 2026-03-19 15:05:13 +01:00
parent 16be71a4ce
commit 97f9317797
32 changed files with 893 additions and 3 deletions

View file

@ -12,6 +12,7 @@ const TAB_SETS: Array<{ prefixes: string[]; tabs: Tab[] }> = [
tabs: [
{ href: '/admin/roles', label: 'Internal Roles', exact: true },
{ href: '/admin/roles/create', label: 'Create Role' },
{ href: '/admin/roles/templates', label: 'Role Templates' },
],
},
{
@ -50,6 +51,14 @@ const TAB_SETS: Array<{ prefixes: string[]; tabs: Tab[] }> = [
];
const PAGE_TITLES: Array<{ prefix: string; title: string }> = [
{ prefix: '/admin/workspace', title: 'Dashboard Workspace' },
{ prefix: '/admin/settings', title: 'Settings' },
{ prefix: '/admin/role-modules', title: 'Role Modules' },
{ prefix: '/admin/modules', title: 'Module Management' },
{ prefix: '/admin/responses', title: 'Lead Responses' },
{ prefix: '/admin/applications', title: 'Applications' },
{ prefix: '/admin/financial', title: 'Financial Management' },
{ prefix: '/admin/help', title: 'Support Management' },
{ prefix: '/admin/verification-status', title: 'Verification Status' },
{ prefix: '/admin/verification', title: 'Verification Review' },
{ prefix: '/admin/approval', title: 'Approval Management' },
@ -69,7 +78,10 @@ const PAGE_TITLES: Array<{ prefix: string; title: string }> = [
{ prefix: '/admin/ledger', title: 'Ledger Management' },
{ prefix: '/admin/report', title: 'Report Management' },
{ prefix: '/admin/roles', title: 'Internal Role Management' },
{ prefix: '/admin/external-role-management', title: 'External Role Management' },
{ prefix: '/admin/internal-role-management', title: 'Internal Role Management' },
{ prefix: '/admin/runtime-roles', title: 'External Role Management' },
{ prefix: '/admin/onboarding-management', title: 'Onboarding Management' },
{ prefix: '/admin/onboarding-schemas', title: 'Onboarding Management' },
{ prefix: '/admin', title: 'Dashboard' },
];

View file

@ -0,0 +1,9 @@
import { onMount } from 'solid-js';
import { useNavigate } from '@solidjs/router';
import AdminShell from '~/components/AdminShell';
export default function ApplicationsAliasPage() {
const navigate = useNavigate();
onMount(() => navigate('/admin/jobs', { replace: true }));
return <AdminShell><div class="card"><p class="notice">Redirecting to jobs management...</p></div></AdminShell>;
}

View file

@ -77,6 +77,7 @@ export default function CompanyPage() {
<h1 class="page-title">Company Management</h1>
<p class="page-subtitle">Manage all company accounts on the platform.</p>
</div>
<A class="btn navy" href="/admin/company/create">Create Company</A>
</div>
<Show when={actionError()}>

View file

@ -0,0 +1,101 @@
import { A, useParams } from '@solidjs/router';
import { createMemo, createResource, Show } from 'solid-js';
import AdminShell from '~/components/AdminShell';
const API = '/api/gateway';
type CompanyDetail = {
id: string;
company_name?: string;
companyName?: string;
company_id?: string;
companyId?: string;
industry?: string;
email?: string;
phone?: string;
website_url?: string;
websiteUrl?: string;
city?: string;
state?: string;
address?: string;
status?: string;
is_verified?: boolean;
isVerified?: boolean;
description?: string;
pan?: string;
gst?: string;
tan?: string;
};
async function fetchCompany(id: string): Promise<CompanyDetail | null> {
try {
const res = await fetch(`${API}/api/admin/companies/${id}`);
if (!res.ok) return null;
const data = await res.json();
return data.company || data;
} catch {
return null;
}
}
export default function CompanyDetailPage() {
const params = useParams();
const [company] = createResource(() => params.id, fetchCompany);
const name = createMemo(() => company()?.company_name || company()?.companyName || 'Company');
const cid = createMemo(() => company()?.company_id || company()?.companyId || company()?.id || '—');
const website = createMemo(() => company()?.website_url || company()?.websiteUrl || '');
const isVerified = createMemo(() => Boolean(company()?.is_verified ?? company()?.isVerified));
return (
<AdminShell>
<div class="page-hero-card page-actions">
<div>
<h1 class="page-title">Company Detail</h1>
<p class="page-subtitle">Review company profile, contact details, and verification readiness.</p>
</div>
<div class="page-actions-right">
<A class="btn" href="/admin/company">Back to Companies</A>
<A class="btn navy" href="/admin/approval">Open Approval Management</A>
</div>
</div>
<Show when={company.loading}>
<div class="card"><p class="notice">Loading company...</p></div>
</Show>
<Show when={!company.loading && !company()}>
<div class="card"><p class="notice">Company not found.</p></div>
</Show>
<Show when={company()}>
<div class="grid" style="margin-top:0">
<section class="card">
<h2 style="margin-bottom:8px">Company</h2>
<p class="notice" style="margin:0"><strong>Name:</strong> {name()}</p>
<p class="notice" style="margin:8px 0 0"><strong>Company ID:</strong> {cid()}</p>
<p class="notice" style="margin:8px 0 0"><strong>Industry:</strong> {company()!.industry || '—'}</p>
<p class="notice" style="margin:8px 0 0"><strong>Status:</strong> {company()!.status || '—'}</p>
<p class="notice" style="margin:8px 0 0"><strong>Verification:</strong> {isVerified() ? 'Verified' : 'Not Verified'}</p>
</section>
<section class="card">
<h2 style="margin-bottom:8px">Contact</h2>
<p class="notice" style="margin:0"><strong>Email:</strong> {company()!.email || '—'}</p>
<p class="notice" style="margin:8px 0 0"><strong>Phone:</strong> {company()!.phone || '—'}</p>
<p class="notice" style="margin:8px 0 0"><strong>City/State:</strong> {[company()!.city, company()!.state].filter(Boolean).join(', ') || '—'}</p>
<p class="notice" style="margin:8px 0 0"><strong>Address:</strong> {company()!.address || '—'}</p>
<p class="notice" style="margin:8px 0 0"><strong>Website:</strong> {website() ? <a href={website()} target="_blank" rel="noreferrer">{website()}</a> : '—'}</p>
</section>
</div>
<section class="card" style="margin-top:16px">
<h2 style="margin-bottom:8px">Compliance</h2>
<div class="field-grid-2">
<p class="notice" style="margin:0"><strong>PAN:</strong> {company()!.pan || '—'}</p>
<p class="notice" style="margin:0"><strong>GST:</strong> {company()!.gst || '—'}</p>
<p class="notice" style="margin:0"><strong>TAN:</strong> {company()!.tan || '—'}</p>
</div>
</section>
</Show>
</AdminShell>
);
}

View file

@ -0,0 +1,111 @@
import { A, useNavigate } from '@solidjs/router';
import { createSignal, Show } from 'solid-js';
import AdminShell from '~/components/AdminShell';
const API = '/api/gateway';
export default function CreateCompanyPage() {
const navigate = useNavigate();
const [saving, setSaving] = createSignal(false);
const [error, setError] = createSignal('');
const [form, setForm] = createSignal({
companyName: '',
companyId: '',
address: '',
email: '',
phone: '',
industry: 'TECHNOLOGY',
description: '',
websiteUrl: '',
});
const setField = (k: keyof ReturnType<typeof form>, v: string) => {
setForm((prev) => ({ ...prev, [k]: v }));
};
const submit = async (e: Event) => {
e.preventDefault();
const f = form();
if (!f.companyName.trim() || !f.companyId.trim() || !f.address.trim() || !f.email.trim() || !f.phone.trim()) {
setError('Please fill all required fields.');
return;
}
try {
setSaving(true);
setError('');
const res = await fetch(`${API}/api/admin/companies`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(f),
});
if (!res.ok) {
const payload = await res.json().catch(() => ({}));
throw new Error(payload.message || 'Failed to create company');
}
navigate('/admin/company');
} catch (err: any) {
setError(err.message || 'Failed to create company');
} finally {
setSaving(false);
}
};
return (
<AdminShell>
<div class="page-hero-card page-actions">
<div>
<h1 class="page-title">Create Company</h1>
<p class="page-subtitle">Add a new organization profile to the admin company catalog.</p>
</div>
<A class="btn" href="/admin/company">Back to Companies</A>
</div>
<Show when={error()}>
<div class="error-box">{error()}</div>
</Show>
<form class="card" onSubmit={submit}>
<div class="field-grid-2">
<div class="field">
<label>Company Name *</label>
<input value={form().companyName} onInput={(e) => setField('companyName', e.currentTarget.value)} />
</div>
<div class="field">
<label>Company ID *</label>
<input value={form().companyId} onInput={(e) => setField('companyId', e.currentTarget.value)} />
</div>
<div class="field">
<label>Industry</label>
<input value={form().industry} onInput={(e) => setField('industry', e.currentTarget.value)} />
</div>
<div class="field">
<label>Website</label>
<input value={form().websiteUrl} onInput={(e) => setField('websiteUrl', e.currentTarget.value)} />
</div>
<div class="field">
<label>Email *</label>
<input type="email" value={form().email} onInput={(e) => setField('email', e.currentTarget.value)} />
</div>
<div class="field">
<label>Phone *</label>
<input value={form().phone} onInput={(e) => setField('phone', e.currentTarget.value)} />
</div>
<div class="field" style="grid-column:1/-1">
<label>Address *</label>
<input value={form().address} onInput={(e) => setField('address', e.currentTarget.value)} />
</div>
<div class="field" style="grid-column:1/-1">
<label>Description</label>
<textarea rows={3} value={form().description} onInput={(e) => setField('description', e.currentTarget.value)} />
</div>
</div>
<div class="actions" style="justify-content:flex-end">
<A class="btn" href="/admin/company">Cancel</A>
<button class="btn navy" type="submit" disabled={saving()}>
{saving() ? 'Creating...' : 'Create Company'}
</button>
</div>
</form>
</AdminShell>
);
}

View file

@ -1,5 +1,5 @@
import { A, useNavigate } from '@solidjs/router';
import { createResource, createSignal, Show, For } from 'solid-js';
import { A, useNavigate, useSearchParams } from '@solidjs/router';
import { createResource, createSignal, Show, For, onMount } from 'solid-js';
import AdminShell from '~/components/AdminShell';
const API = '/api/gateway';
@ -60,6 +60,7 @@ function isActive(e: Employee): boolean {
export default function EmployeesPage() {
const navigate = useNavigate();
const [searchParams] = useSearchParams();
const [employees, { refetch }] = createResource(loadEmployees);
const [roles] = createResource(loadRoles);
@ -67,6 +68,12 @@ export default function EmployeesPage() {
// tabs: list | create
const [view, setView] = createSignal<'list' | 'create'>('list');
onMount(() => {
if (searchParams.tab === 'create') {
setView('create');
}
});
// create form fields
const [formName, setFormName] = createSignal('');
const [formEmail, setFormEmail] = createSignal('');

View file

@ -0,0 +1,132 @@
import { A, useNavigate, useParams } from '@solidjs/router';
import { createMemo, createResource, createSignal, Show } from 'solid-js';
import AdminShell from '~/components/AdminShell';
const API = '/api/gateway';
type Role = { id: string; name: string };
type Employee = { id: string; name?: string; full_name?: string; email: string; role_id?: string; role?: { id?: string } };
async function fetchEmployee(id: string): Promise<Employee | null> {
try {
const res = await fetch(`${API}/api/admin/employees/${id}`);
if (!res.ok) return null;
return res.json();
} catch {
return null;
}
}
async function fetchRoles(): Promise<Role[]> {
try {
const res = await fetch(`${API}/api/admin/roles?audience=INTERNAL`);
if (!res.ok) return [];
const data = await res.json();
return Array.isArray(data) ? data : (data.roles || []);
} catch {
return [];
}
}
export default function EditEmployeePage() {
const params = useParams();
const navigate = useNavigate();
const [employee] = createResource(() => params.id, fetchEmployee);
const [roles] = createResource(fetchRoles);
const [name, setName] = createSignal('');
const [email, setEmail] = createSignal('');
const [roleId, setRoleId] = createSignal('');
const [saving, setSaving] = createSignal(false);
const [error, setError] = createSignal('');
createMemo(() => {
const e = employee();
if (!e) return null;
setName(e.name || e.full_name || '');
setEmail(e.email || '');
setRoleId(e.role_id || e.role?.id || '');
return null;
});
const submit = async (e: Event) => {
e.preventDefault();
if (!name().trim() || !email().trim() || !roleId()) {
setError('Name, email, and role are required.');
return;
}
try {
setSaving(true);
setError('');
const res = await fetch(`${API}/api/admin/employees/${params.id}`, {
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name: name().trim(), email: email().trim(), role_id: roleId() }),
});
if (!res.ok) {
const payload = await res.json().catch(() => ({}));
throw new Error(payload.message || 'Failed to update employee');
}
navigate('/admin/employees');
} catch (err: any) {
setError(err.message || 'Failed to update employee');
} finally {
setSaving(false);
}
};
return (
<AdminShell>
<div class="page-hero-card page-actions">
<div>
<h1 class="page-title">Edit Employee</h1>
<p class="page-subtitle">Update internal employee profile and role assignment.</p>
</div>
<A class="btn" href="/admin/employees">Back to Employees</A>
</div>
<Show when={error()}>
<div class="error-box">{error()}</div>
</Show>
<Show when={employee.loading}>
<div class="card"><p class="notice">Loading employee...</p></div>
</Show>
<Show when={!employee.loading && !employee()}>
<div class="card"><p class="notice">Employee not found.</p></div>
</Show>
<Show when={employee()}>
<form class="card" onSubmit={submit}>
<div class="field-grid-2">
<div class="field">
<label>Full Name</label>
<input value={name()} onInput={(e) => setName(e.currentTarget.value)} />
</div>
<div class="field">
<label>Email</label>
<input type="email" value={email()} onInput={(e) => setEmail(e.currentTarget.value)} />
</div>
<div class="field">
<label>Role</label>
<select value={roleId()} onChange={(e) => setRoleId(e.currentTarget.value)}>
<option value="">Select role...</option>
<Show when={!roles.loading}>
{roles()?.map((r) => (
<option value={r.id}>{r.name}</option>
))}
</Show>
</select>
</div>
</div>
<div class="actions" style="justify-content:flex-end">
<A class="btn" href="/admin/employees">Cancel</A>
<button class="btn navy" type="submit" disabled={saving()}>
{saving() ? 'Saving...' : 'Save Changes'}
</button>
</div>
</form>
</Show>
</AdminShell>
);
}

View file

@ -0,0 +1,19 @@
import { onMount } from 'solid-js';
import { useNavigate } from '@solidjs/router';
import AdminShell from '~/components/AdminShell';
export default function CreateEmployeeAliasPage() {
const navigate = useNavigate();
onMount(() => {
navigate('/admin/employees?tab=create', { replace: true });
});
return (
<AdminShell>
<div class="card">
<p class="notice">Redirecting to employee create form...</p>
</div>
</AdminShell>
);
}

View file

@ -0,0 +1,13 @@
import { onMount } from 'solid-js';
import { useNavigate } from '@solidjs/router';
import AdminShell from '~/components/AdminShell';
export default function ExternalRoleManagementAliasPage() {
const navigate = useNavigate();
onMount(() => navigate('/admin/runtime-roles', { replace: true }));
return (
<AdminShell>
<div class="card"><p class="notice">Redirecting to external role management...</p></div>
</AdminShell>
);
}

View file

@ -0,0 +1,9 @@
import { onMount } from 'solid-js';
import { useNavigate } from '@solidjs/router';
import AdminShell from '~/components/AdminShell';
export default function AdjustCreditAliasPage() {
const navigate = useNavigate();
onMount(() => navigate('/admin/credit', { replace: true }));
return <AdminShell><div class="card"><p class="notice">Redirecting to credit management...</p></div></AdminShell>;
}

View file

@ -0,0 +1,9 @@
import { onMount } from 'solid-js';
import { useNavigate } from '@solidjs/router';
import AdminShell from '~/components/AdminShell';
export default function ReconcileAliasPage() {
const navigate = useNavigate();
onMount(() => navigate('/admin/ledger', { replace: true }));
return <AdminShell><div class="card"><p class="notice">Redirecting to ledger management...</p></div></AdminShell>;
}

13
src/routes/admin/help.tsx Normal file
View file

@ -0,0 +1,13 @@
import { onMount } from 'solid-js';
import { useNavigate } from '@solidjs/router';
import AdminShell from '~/components/AdminShell';
export default function HelpAliasPage() {
const navigate = useNavigate();
onMount(() => navigate('/admin/support', { replace: true }));
return (
<AdminShell>
<div class="card"><p class="notice">Redirecting to support management...</p></div>
</AdminShell>
);
}

View file

@ -0,0 +1,25 @@
import { A, useParams } from '@solidjs/router';
import AdminShell from '~/components/AdminShell';
export default function HelpArticlePage() {
const params = useParams();
return (
<AdminShell>
<div class="page-hero-card page-actions">
<div>
<h1 class="page-title">Help Article</h1>
<p class="page-subtitle">Legacy help article route preserved for migration compatibility.</p>
</div>
<A class="btn" href="/admin/support">Back to Support</A>
</div>
<section class="card">
<p class="notice" style="margin:0">
Article ID: <strong>{params.id}</strong>
</p>
<p class="notice" style="margin:8px 0 0">
Detailed knowledge base article rendering is handled in the support/KB modules during this migration phase.
</p>
</section>
</AdminShell>
);
}

View file

@ -0,0 +1,20 @@
import { A } from '@solidjs/router';
import AdminShell from '~/components/AdminShell';
export default function HelpSupportBridgePage() {
return (
<AdminShell>
<div class="page-hero-card">
<h1 class="page-title">Support Bridge</h1>
<p class="page-subtitle" style="margin-top:8px">
This legacy help bridge now routes through the unified Support Management module.
</p>
</div>
<section class="card">
<div class="actions">
<A class="btn navy" href="/admin/support">Open Support Management</A>
</div>
</section>
</AdminShell>
);
}

View file

@ -0,0 +1,13 @@
import { onMount } from 'solid-js';
import { useNavigate } from '@solidjs/router';
import AdminShell from '~/components/AdminShell';
export default function InternalRoleManagementAliasPage() {
const navigate = useNavigate();
onMount(() => navigate('/admin/roles', { replace: true }));
return (
<AdminShell>
<div class="card"><p class="notice">Redirecting to internal role management...</p></div>
</AdminShell>
);
}

View file

@ -0,0 +1,9 @@
import { onMount } from 'solid-js';
import { useNavigate } from '@solidjs/router';
import AdminShell from '~/components/AdminShell';
export default function KbArticlesAliasPage() {
const navigate = useNavigate();
onMount(() => navigate('/admin/kb', { replace: true }));
return <AdminShell><div class="card"><p class="notice">Redirecting to knowledge base...</p></div></AdminShell>;
}

View file

@ -0,0 +1,17 @@
import { onMount } from 'solid-js';
import { A, useNavigate, useParams } from '@solidjs/router';
import AdminShell from '~/components/AdminShell';
export default function KbArticleAliasPage() {
const navigate = useNavigate();
const params = useParams();
onMount(() => navigate('/admin/kb', { replace: true }));
return (
<AdminShell>
<div class="card">
<p class="notice">Redirecting article <strong>{params.id}</strong> to knowledge base module...</p>
<div class="actions"><A class="btn" href="/admin/kb">Open Knowledge Base</A></div>
</div>
</AdminShell>
);
}

View file

@ -0,0 +1,17 @@
import { onMount } from 'solid-js';
import { A, useNavigate, useParams } from '@solidjs/router';
import AdminShell from '~/components/AdminShell';
export default function KbArticleEditAliasPage() {
const navigate = useNavigate();
const params = useParams();
onMount(() => navigate('/admin/kb', { replace: true }));
return (
<AdminShell>
<div class="card">
<p class="notice">Redirecting article edit <strong>{params.id}</strong> to knowledge base module...</p>
<div class="actions"><A class="btn" href="/admin/kb">Open Knowledge Base</A></div>
</div>
</AdminShell>
);
}

View file

@ -0,0 +1,9 @@
import { onMount } from 'solid-js';
import { useNavigate } from '@solidjs/router';
import AdminShell from '~/components/AdminShell';
export default function KbArticleNewAliasPage() {
const navigate = useNavigate();
onMount(() => navigate('/admin/kb', { replace: true }));
return <AdminShell><div class="card"><p class="notice">Redirecting to knowledge base editor...</p></div></AdminShell>;
}

View file

@ -0,0 +1,9 @@
import { onMount } from 'solid-js';
import { useNavigate } from '@solidjs/router';
import AdminShell from '~/components/AdminShell';
export default function KbCategoriesAliasPage() {
const navigate = useNavigate();
onMount(() => navigate('/admin/kb', { replace: true }));
return <AdminShell><div class="card"><p class="notice">Redirecting to knowledge base categories...</p></div></AdminShell>;
}

View file

@ -0,0 +1,82 @@
import { A, useParams } from '@solidjs/router';
import { createMemo, createResource, Show } from 'solid-js';
import AdminShell from '~/components/AdminShell';
const API = '/api/gateway';
type Lead = {
id: string;
requirementId?: string;
requirement_id?: string;
profession?: string;
customerId?: string;
customer_id?: string;
professionalId?: string;
professional_id?: string;
status?: string;
createdAt?: string;
created_at?: string;
updatedAt?: string;
updated_at?: string;
};
async function fetchLead(id: string): Promise<Lead | null> {
try {
const res = await fetch(`${API}/api/leads/${id}`);
if (!res.ok) return null;
const data = await res.json();
return data.lead || data;
} catch {
return null;
}
}
export default function LeadDetailPage() {
const params = useParams();
const [lead] = createResource(() => params.id, fetchLead);
const requirementId = createMemo(() => lead()?.requirementId || lead()?.requirement_id || '');
const customerId = createMemo(() => lead()?.customerId || lead()?.customer_id || '');
const professionalId = createMemo(() => lead()?.professionalId || lead()?.professional_id || '');
const createdAt = createMemo(() => lead()?.createdAt || lead()?.created_at || '');
const updatedAt = createMemo(() => lead()?.updatedAt || lead()?.updated_at || '');
return (
<AdminShell>
<div class="page-hero-card page-actions">
<div>
<h1 class="page-title">Lead Detail</h1>
<p class="page-subtitle">Review one lead and its linked requirement identifiers.</p>
</div>
<A class="btn" href="/admin/leads">Back to Leads</A>
</div>
<Show when={lead.loading}>
<div class="card"><p class="notice">Loading lead...</p></div>
</Show>
<Show when={!lead.loading && !lead()}>
<div class="card"><p class="notice">Lead not found.</p></div>
</Show>
<Show when={lead()}>
<section class="card">
<div class="field-grid-2">
<p class="notice" style="margin:0"><strong>Lead ID:</strong> {lead()!.id}</p>
<p class="notice" style="margin:0"><strong>Status:</strong> {lead()!.status || '—'}</p>
<p class="notice" style="margin:0"><strong>Profession:</strong> {lead()!.profession || '—'}</p>
<p class="notice" style="margin:0"><strong>Requirement ID:</strong> {requirementId() || '—'}</p>
<p class="notice" style="margin:0"><strong>Customer ID:</strong> {customerId() || '—'}</p>
<p class="notice" style="margin:0"><strong>Professional ID:</strong> {professionalId() || 'Unassigned'}</p>
<p class="notice" style="margin:0"><strong>Created:</strong> {createdAt() ? new Date(createdAt()).toLocaleString() : '—'}</p>
<p class="notice" style="margin:0"><strong>Updated:</strong> {updatedAt() ? new Date(updatedAt()).toLocaleString() : '—'}</p>
</div>
<div class="actions">
<Show when={requirementId()}>
<A class="btn" href={`/admin/jobs/${requirementId()}`}>Open Linked Requirement/Job</A>
</Show>
</div>
</section>
</Show>
</AdminShell>
);
}

View file

@ -0,0 +1,9 @@
import { onMount } from 'solid-js';
import { useNavigate } from '@solidjs/router';
import AdminShell from '~/components/AdminShell';
export default function ModulesAliasPage() {
const navigate = useNavigate();
onMount(() => navigate('/admin/internal-dashboard-management', { replace: true }));
return <AdminShell><div class="card"><p class="notice">Redirecting to dashboard module configuration...</p></div></AdminShell>;
}

View file

@ -0,0 +1,13 @@
import { onMount } from 'solid-js';
import { useNavigate } from '@solidjs/router';
import AdminShell from '~/components/AdminShell';
export default function OnboardingManagementAliasPage() {
const navigate = useNavigate();
onMount(() => navigate('/admin/onboarding-schemas', { replace: true }));
return (
<AdminShell>
<div class="card"><p class="notice">Redirecting to onboarding management...</p></div>
</AdminShell>
);
}

View file

@ -108,7 +108,7 @@ export default function PhotographerPage() {
</td>
<td>
<div class="table-actions">
<A class="btn" href={`/admin/users/${item.id}`}>View</A>
<A class="btn" href={`/admin/photographer/${item.id}`}>View</A>
</div>
</td>
</tr>

View file

@ -0,0 +1,77 @@
import { A, useParams } from '@solidjs/router';
import { createMemo, createResource, Show } from 'solid-js';
import AdminShell from '~/components/AdminShell';
const API = '/api/gateway';
type Photographer = {
id: string;
name?: string;
full_name?: string;
email?: string;
phone?: string;
status?: string;
created_at?: string;
createdAt?: string;
city?: string;
state?: string;
experience?: string;
portfolio_url?: string;
};
async function fetchPhotographer(id: string): Promise<Photographer | null> {
try {
const res = await fetch(`${API}/api/admin/users/${id}`);
if (res.ok) return res.json();
const fallback = await fetch(`${API}/api/users/${id}`);
if (!fallback.ok) return null;
return fallback.json();
} catch {
return null;
}
}
export default function PhotographerDetailPage() {
const params = useParams();
const [profile] = createResource(() => params.id, fetchPhotographer);
const name = createMemo(() => profile()?.name || profile()?.full_name || 'Photographer');
const created = createMemo(() => profile()?.createdAt || profile()?.created_at || '');
return (
<AdminShell>
<div class="page-hero-card page-actions">
<div>
<h1 class="page-title">Photographer Detail</h1>
<p class="page-subtitle">View profile snapshot and account metadata for one photographer.</p>
</div>
<div class="page-actions-right">
<A class="btn" href="/admin/photographer">Back to Photographer List</A>
<A class="btn" href={`/admin/users/details/${params.id}`}>Open User Detail</A>
</div>
</div>
<Show when={profile.loading}>
<div class="card"><p class="notice">Loading photographer...</p></div>
</Show>
<Show when={!profile.loading && !profile()}>
<div class="card"><p class="notice">Photographer not found.</p></div>
</Show>
<Show when={profile()}>
<section class="card">
<div class="field-grid-2">
<p class="notice" style="margin:0"><strong>Name:</strong> {name()}</p>
<p class="notice" style="margin:0"><strong>Email:</strong> {profile()!.email || '—'}</p>
<p class="notice" style="margin:0"><strong>Phone:</strong> {profile()!.phone || '—'}</p>
<p class="notice" style="margin:0"><strong>Status:</strong> {profile()!.status || '—'}</p>
<p class="notice" style="margin:0"><strong>City/State:</strong> {[profile()!.city, profile()!.state].filter(Boolean).join(', ') || '—'}</p>
<p class="notice" style="margin:0"><strong>Experience:</strong> {profile()!.experience || '—'}</p>
<p class="notice" style="margin:0"><strong>Created:</strong> {created() ? new Date(created()).toLocaleString() : '—'}</p>
<p class="notice" style="margin:0"><strong>Portfolio:</strong> {profile()!.portfolio_url || '—'}</p>
</div>
</section>
</Show>
</AdminShell>
);
}

View file

@ -0,0 +1,10 @@
import { onMount } from 'solid-js';
import { useNavigate, useParams } from '@solidjs/router';
import AdminShell from '~/components/AdminShell';
export default function ProfileAliasPage() {
const navigate = useNavigate();
const params = useParams();
onMount(() => navigate(`/admin/users/details/${params.id}`, { replace: true }));
return <AdminShell><div class="card"><p class="notice">Redirecting to user profile detail...</p></div></AdminShell>;
}

View file

@ -0,0 +1,9 @@
import { onMount } from 'solid-js';
import { useNavigate } from '@solidjs/router';
import AdminShell from '~/components/AdminShell';
export default function ResponsesAliasPage() {
const navigate = useNavigate();
onMount(() => navigate('/admin/leads', { replace: true }));
return <AdminShell><div class="card"><p class="notice">Redirecting to lead responses management...</p></div></AdminShell>;
}

View file

@ -0,0 +1,9 @@
import { onMount } from 'solid-js';
import { useNavigate } from '@solidjs/router';
import AdminShell from '~/components/AdminShell';
export default function RoleModulesAliasPage() {
const navigate = useNavigate();
onMount(() => navigate('/admin/role-ui-configs', { replace: true }));
return <AdminShell><div class="card"><p class="notice">Redirecting to role module configuration...</p></div></AdminShell>;
}

View file

@ -0,0 +1,92 @@
import { A } from '@solidjs/router';
import { createMemo, createResource, For, Show } from 'solid-js';
import AdminShell from '~/components/AdminShell';
const API = '/api/gateway';
type RoleTemplate = {
id: string;
name: string;
description?: string;
code?: string;
audience?: string;
};
async function fetchTemplates(): Promise<RoleTemplate[]> {
try {
const res = await fetch(`${API}/api/admin/roles?audience=INTERNAL`);
if (!res.ok) return [];
const data = await res.json();
const rows = Array.isArray(data) ? data : (data.roles || []);
return rows.map((r: any) => ({
id: r.id,
name: r.name,
description: r.description || '',
code: r.code || r.key || '',
audience: r.audience || 'INTERNAL',
}));
} catch {
return [];
}
}
export default function RoleTemplatesPage() {
const [templates] = createResource(fetchTemplates);
const count = createMemo(() => (templates() || []).length);
return (
<AdminShell>
<div class="page-hero-card page-actions">
<div>
<h1 class="page-title">Role Templates</h1>
<p class="page-subtitle">Starter role presets for faster internal role creation and cloning.</p>
</div>
<A class="btn navy" href="/admin/roles/create">Create Internal Role</A>
</div>
<section class="card" style="padding:0;overflow:hidden">
<div style="display:flex;align-items:center;justify-content:space-between;padding:14px 18px;border-bottom:1px solid #e2e8f0">
<h2 style="margin:0;font-size:16px">Available Templates</h2>
<span style="font-size:12px;color:#64748b">{count()} template{count() !== 1 ? 's' : ''}</span>
</div>
<div class="table-wrap">
<table class="list-table">
<thead>
<tr>
<th>Name</th>
<th>Description</th>
<th>Code</th>
<th class="align-right">Actions</th>
</tr>
</thead>
<tbody>
<Show when={templates.loading}>
<tr><td colspan="4" style="text-align:center;padding:32px;color:#64748b">Loading templates...</td></tr>
</Show>
<Show when={!templates.loading && count() === 0}>
<tr><td colspan="4" style="text-align:center;padding:32px;color:#94a3b8">No templates available yet.</td></tr>
</Show>
<Show when={!templates.loading && count() > 0}>
<For each={templates()}>
{(item) => (
<tr>
<td style="font-weight:600;color:#0f172a">{item.name}</td>
<td style="color:#475569">{item.description || '—'}</td>
<td style="color:#64748b;font-family:ui-monospace,SFMono-Regular,Menlo,monospace">{item.code || '—'}</td>
<td>
<div class="table-actions">
<A class="btn" href={`/admin/roles/${item.id}`}>View</A>
<A class="btn" href="/admin/roles/create">Use As Base</A>
</div>
</td>
</tr>
)}
</For>
</Show>
</tbody>
</table>
</div>
</section>
</AdminShell>
);
}

View file

@ -0,0 +1,9 @@
import { onMount } from 'solid-js';
import { useNavigate } from '@solidjs/router';
import AdminShell from '~/components/AdminShell';
export default function SettingsAliasPage() {
const navigate = useNavigate();
onMount(() => navigate('/admin/internal-dashboard-management', { replace: true }));
return <AdminShell><div class="card"><p class="notice">Redirecting to settings/configuration...</p></div></AdminShell>;
}

View file

@ -0,0 +1,9 @@
import { onMount } from 'solid-js';
import { useNavigate } from '@solidjs/router';
import AdminShell from '~/components/AdminShell';
export default function WorkspaceAliasPage() {
const navigate = useNavigate();
onMount(() => navigate('/admin', { replace: true }));
return <AdminShell><div class="card"><p class="notice">Redirecting to dashboard workspace...</p></div></AdminShell>;
}

View file

@ -0,0 +1,16 @@
import { onMount } from 'solid-js';
import { useNavigate, useParams } from '@solidjs/router';
import AdminShell from '~/components/AdminShell';
export default function WorkspaceMenuAliasPage() {
const navigate = useNavigate();
const params = useParams();
onMount(() => navigate('/admin', { replace: true }));
return (
<AdminShell>
<div class="card">
<p class="notice">Redirecting workspace menu <strong>{params.menuId}</strong> to dashboard...</p>
</div>
</AdminShell>
);
}