ui: match stitch design system — navy accents, tighter rounding, sidebar groups

- AdminSidebar: group labels (Management/Organisation/Service Providers/Operations),
  navy active state (bg-[#e8edf5] + [#0a1d37] left bar + navy icon), width 268px,
  13.5px font, 40px item height, lucide-solid icons only (no img fallback), min-w-0
  truncation fix, tighter py spacing
- AdminShell: header height 64px (was 86px), compact search bar 40px/rounded-lg,
  navy avatar square (was orange gradient), tab indicator navy (was orange), sidebar
  width 268px, main bg #f3f4f8
- Dashboard: rounded-xl→rounded-lg cards, rounded-2xl→rounded-xl sections, navy
  accent bars on KPI cards, tabular-nums on values, navy pipeline progress bars
- app.css: table thead now navy (#0a1d37) with white text matching reference screenshot,
  focus rings navy, admin-segment/admin-link-tabs active state navy, reduced border
  radii on segmented controls and list items

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Ashwin Kumar 2026-03-24 02:49:09 +01:00
parent 9b1ffdacf6
commit ab15466368
4 changed files with 336 additions and 112 deletions

View file

@ -87,3 +87,212 @@ body {
.auth-link-btn:hover { color: #ea580c; text-decoration: underline; }
.auth-inline-msg { margin-top: 12px; }
.hint { margin: 6px 0 0; color: #64748b; font-size: 12px; }
/* ===== Admin Shell + Shared UI ===== */
.admin-shell-root {
background:
radial-gradient(circle at 92% 8%, rgba(253, 98, 22, 0.08), transparent 30%),
radial-gradient(circle at 12% 100%, rgba(10, 29, 55, 0.08), transparent 36%),
#f8f9fd;
}
.admin-main section.rounded-xl.border.border-gray-200.bg-white.shadow-sm {
border-color: #e2e8f0 !important;
box-shadow: 0 12px 28px -20px rgba(10, 29, 55, 0.34) !important;
border-radius: 14px !important;
}
.admin-main table.w-full {
border-collapse: separate;
border-spacing: 0;
}
.admin-main table.w-full thead th {
background: #0a1d37;
color: rgba(255,255,255,0.85);
font-size: 11px;
text-transform: uppercase;
letter-spacing: 0.08em;
font-weight: 700;
padding: 11px 14px;
border-bottom: none;
text-align: left;
white-space: nowrap;
}
.admin-main table.w-full tbody td {
color: #0f172a;
font-size: 13px;
padding: 12px 14px;
border-bottom: 1px solid #eef2f7;
vertical-align: middle;
}
.admin-main table.w-full tbody tr:last-child td {
border-bottom: 0;
}
.admin-main table.w-full tbody tr:hover td {
background: #fcfdff;
}
.admin-main input[type='text'],
.admin-main input[type='search'],
.admin-main input[type='number'],
.admin-main input[type='email'],
.admin-main input[type='url'],
.admin-main input[type='password'],
.admin-main select,
.admin-main textarea {
border: 1px solid #cbd5e1;
border-radius: 7px;
background: #fff;
color: #0f172a;
transition: border-color 160ms, box-shadow 160ms;
}
.admin-main input:focus,
.admin-main select:focus,
.admin-main textarea:focus {
border-color: rgba(10, 29, 55, 0.45) !important;
box-shadow: 0 0 0 3px rgba(10, 29, 55, 0.08);
outline: none;
}
.notice {
margin: 0;
color: #64748b;
font-size: 13px;
line-height: 1.5;
padding: 14px 16px;
}
.list-header {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
margin-bottom: 14px;
}
.list-header h2 {
margin: 0;
font-size: 18px;
font-weight: 800;
color: #0a1d37;
}
.list-item {
border: 1px solid #e2e8f0;
border-radius: 8px;
background: #fff;
padding: 14px;
}
.list-item h3 {
margin: 0 0 10px;
font-size: 14px;
font-weight: 800;
color: #0a1d37;
}
.admin-segmented {
display: inline-flex;
align-items: center;
gap: 6px;
margin: 0 0 16px;
padding: 4px;
border-radius: 8px;
border: 1px solid #e2e8f0;
background: #fff;
}
.admin-segment {
border: 0;
background: transparent;
color: #475569;
font-size: 13px;
font-weight: 700;
line-height: 1;
padding: 7px 12px;
border-radius: 6px;
cursor: pointer;
transition: all 140ms ease;
}
.admin-segment:hover {
background: #f8fafc;
color: #0f172a;
}
.admin-segment.active {
background: rgba(10, 29, 55, 0.10);
color: #0a1d37;
}
.admin-segment:disabled {
opacity: 0.45;
cursor: not-allowed;
}
.admin-pagination {
display: flex;
align-items: center;
justify-content: flex-end;
gap: 12px;
padding: 12px 14px;
border-top: 1px solid #e2e8f0;
color: #64748b;
font-size: 12px;
font-weight: 700;
}
.admin-link-tabs {
display: inline-flex;
align-items: center;
gap: 6px;
margin: 0 0 14px;
padding: 3px;
border: 1px solid #e2e8f0;
border-radius: 8px;
background: #fff;
}
.admin-link-tabs a {
border-radius: 6px;
padding: 7px 12px;
text-decoration: none;
font-size: 13px;
font-weight: 700;
color: #64748b;
}
.admin-link-tabs a[aria-current='page'] {
background: rgba(10, 29, 55, 0.10);
color: #0a1d37;
}
.preview-tabs {
display: flex;
flex-wrap: wrap;
gap: 8px;
}
.preview-tabs button {
border: 1px solid #e2e8f0;
border-radius: 999px;
background: #fff;
color: #475569;
font-size: 12px;
font-weight: 700;
padding: 6px 11px;
cursor: pointer;
}
@media (max-width: 1023px) {
.admin-main table.w-full thead th,
.admin-main table.w-full tbody td {
font-size: 12px;
padding: 10px 11px;
}
}

View file

@ -173,8 +173,8 @@ export default function AdminShell(props: { children: JSX.Element }) {
return (
<div class="min-h-screen bg-[#f0f1f6]">
<header class="fixed inset-x-0 top-0 z-50 border-b border-[#d8dbe3] bg-[#f6f7fb]">
<div class="flex h-[86px] items-center justify-between px-8">
<header class="fixed inset-x-0 top-0 z-50 border-b border-[#d8dbe3] bg-white">
<div class="flex h-[64px] items-center justify-between px-6">
<div class="flex min-w-0 items-center gap-6">
<button
type="button"
@ -192,7 +192,7 @@ export default function AdminShell(props: { children: JSX.Element }) {
</div>
<div class="flex items-center gap-4">
<div class="hidden h-[54px] w-[760px] items-center gap-3 rounded-2xl border border-[#daddE8] bg-[#edeef4] px-5 text-[15px] text-[#6a7285] lg:flex">
<div class="hidden h-[40px] w-[560px] items-center gap-3 rounded-lg border border-[#daddE8] bg-[#f3f4f8] px-4 text-[13px] text-[#6a7285] lg:flex">
<IconSearch />
<span>Search system operations...</span>
</div>
@ -208,12 +208,12 @@ export default function AdminShell(props: { children: JSX.Element }) {
<IconCog />
</button>
<div class="hidden h-10 w-px bg-[#d9dde7] lg:block" />
<div class="hidden items-center gap-3 lg:flex">
<div class="hidden items-center gap-2.5 lg:flex">
<div class="text-right leading-tight">
<p class="text-[17px] font-semibold text-[#111827]">{adminName()}</p>
<p class="text-[14px] text-[#6b7280]">Administrator</p>
<p class="text-[14px] font-semibold text-[#0a1d37]">{adminName()}</p>
<p class="text-[11px] text-[#6b7280]">Administrator</p>
</div>
<div class="flex h-11 w-11 items-center justify-center overflow-hidden rounded-xl border border-[#d9dce7] bg-gradient-to-br from-[#fef3eb] to-[#ffd9c4] text-[15px] font-bold text-[#fd6216]">
<div class="flex h-9 w-9 items-center justify-center overflow-hidden rounded-lg border border-[#d9dce7] bg-[#0a1d37] text-[13px] font-bold text-white">
{adminInitials()}
</div>
</div>
@ -222,17 +222,17 @@ export default function AdminShell(props: { children: JSX.Element }) {
</header>
{checkedSession() ? (
<div class="fixed inset-0 top-[86px] flex">
<div class="fixed inset-0 top-[64px] flex">
<div
class={`absolute inset-0 z-20 bg-[#0a1d37]/35 transition-opacity lg:hidden ${sidebarOpen() ? 'pointer-events-auto opacity-100' : 'pointer-events-none opacity-0'}`}
onClick={() => setSidebarOpen(false)}
/>
<div class={`absolute inset-y-0 left-0 z-30 w-[310px] -translate-x-full transition-transform duration-200 lg:static lg:translate-x-0 ${sidebarOpen() ? 'translate-x-0' : ''}`}>
<div class={`absolute inset-y-0 left-0 z-30 w-[268px] -translate-x-full transition-transform duration-200 lg:static lg:translate-x-0 ${sidebarOpen() ? 'translate-x-0' : ''}`}>
<AdminSidebar onNavigate={() => setSidebarOpen(false)} onLogout={onLogout} />
</div>
<main class="scrollbar min-w-0 flex-1 overflow-y-auto bg-[#f0f1f6] px-8 pb-8 pt-9">
<main class="scrollbar min-w-0 flex-1 overflow-y-auto bg-[#f3f4f8] px-7 pb-8 pt-7">
<ShowTabs
tabs={tabs()}
isTabActive={isTabActive}
@ -244,9 +244,9 @@ export default function AdminShell(props: { children: JSX.Element }) {
</main>
</div>
) : (
<div class="fixed inset-0 top-[86px] flex">
<div class="hidden w-[310px] border-r border-[#d7d8df] bg-[#f6f6f8] lg:block" />
<main class="flex flex-1 items-center justify-center bg-[#f0f1f6]">
<div class="fixed inset-0 top-[64px] flex">
<div class="hidden w-[268px] border-r border-[#d7d8df] bg-white lg:block" />
<main class="flex flex-1 items-center justify-center bg-[#f3f4f8]">
<p class="text-sm text-gray-500">Checking session...</p>
</main>
</div>
@ -265,15 +265,15 @@ function ShowTabs(props: {
if (props.tabs.length === 0) return null;
return (
<div ref={props.setTabsTrackEl} class="relative mb-7 flex items-center gap-8 border-b border-[#d8dbe3] px-1">
<div ref={props.setTabsTrackEl} class="relative mb-6 flex items-center gap-1 border-b border-[#d8dbe3]">
<For each={props.tabs}>
{(tab) => (
<A
href={tab.href}
ref={(el) => props.setTabRefs((prev) => ({ ...prev, [tab.href]: el }))}
aria-current={props.isTabActive(tab) ? 'page' : undefined}
class={`relative rounded-t-xl px-6 pb-4 pt-4 text-[16px] font-semibold transition-colors ${
props.isTabActive(tab) ? 'bg-white text-[#111827]' : 'text-[#636b7f] hover:text-[#111827]'
class={`relative px-4 pb-3 pt-3 text-[13.5px] font-semibold transition-colors ${
props.isTabActive(tab) ? 'text-[#0a1d37]' : 'text-[#636b7f] hover:text-[#0a1d37]'
}`}
>
{tab.label}
@ -281,7 +281,7 @@ function ShowTabs(props: {
)}
</For>
<div
class={`absolute bottom-0 h-[3px] bg-[#fd6216] transition-all duration-300 ease-out ${props.tabIndicator().ready ? 'opacity-100' : 'opacity-0'}`}
class={`absolute bottom-0 h-[2px] bg-[#0a1d37] transition-all duration-300 ease-out ${props.tabIndicator().ready ? 'opacity-100' : 'opacity-0'}`}
style={{ left: `${props.tabIndicator().left}px`, width: `${props.tabIndicator().width}px` }}
/>
</div>

View file

@ -23,52 +23,45 @@ type Item = {
icon?: any;
aliasPrefix?: string;
separatorBefore?: boolean;
group?: string;
};
const items: Item[] = [
{ href: '/admin', label: 'Dashboard', iconPath: '/sidebar-icons/dashboard.svg', icon: LayoutGrid },
{ href: '/admin/roles', label: 'Internal Role Management', iconPath: '/sidebar-icons/role.svg', icon: FolderCog },
{ href: '/admin/runtime-roles', label: 'External Role Management', iconPath: '/sidebar-icons/users.svg', icon: Users },
{ href: '/admin/onboarding-management', label: 'External Onboarding', iconPath: '/sidebar-icons/users.svg', icon: Users, aliasPrefix: '/admin/onboarding-schemas' },
{ href: '/admin/internal-dashboard-management', label: 'Internal Dashboards', iconPath: '/sidebar-icons/dashboard.svg', icon: LayoutGrid },
{ href: '/admin/external-dashboard-management', label: 'External Dashboards', iconPath: '/sidebar-icons/dashboard.svg', icon: LayoutGrid, aliasPrefix: '/admin/role-ui-configs' },
{ href: '/admin/approval', label: 'Approval Management', iconPath: '/sidebar-icons/approval.svg', icon: ClipboardList },
{ href: '/admin/department', label: 'Department Management', iconPath: '/sidebar-icons/department.svg', icon: Briefcase, separatorBefore: true },
{ href: '/admin/designation', label: 'Designation Management', iconPath: '/sidebar-icons/designation.svg', icon: Briefcase },
{ href: '/admin/employees', label: 'Employee Management', iconPath: '/sidebar-icons/users.svg', icon: UserCircle2 },
{ href: '/admin/users', label: 'Users Management', iconPath: '/sidebar-icons/users.svg', icon: Users },
{ href: '/admin/company', label: 'Company Management', iconPath: '/sidebar-icons/company.svg', icon: Briefcase },
{ href: '/admin/candidate', label: 'Candidate Management', iconPath: '/sidebar-icons/candidate.svg', icon: UserCircle2 },
{ href: '/admin/customer', label: 'Customer Management', iconPath: '/sidebar-icons/support.svg', icon: UserCircle2 },
{ href: '/admin/photographer', label: 'Photographer Management', iconPath: '/sidebar-icons/photographer.svg', icon: Sparkles, separatorBefore: true },
{ href: '/admin/makeup-artist', label: 'Makeup Artist Management', iconPath: '/sidebar-icons/makeup-artist.svg', icon: Sparkles },
{ href: '/admin/tutors', label: 'Tutors Management', iconPath: '/sidebar-icons/tutor.svg', icon: Sparkles },
{ href: '/admin/developers', label: 'Developers Management', iconPath: '/sidebar-icons/developers.svg', icon: Sparkles },
{ href: '/admin/jobs', label: 'Jobs Management', iconPath: '/sidebar-icons/jobs.svg', icon: Briefcase },
{ href: '/admin/leads', label: 'Leads Management', iconPath: '/sidebar-icons/leads.svg', icon: HandHelping },
{ href: '/admin/pricing', label: 'Pricing Management', iconPath: '/sidebar-icons/pricing.svg', icon: WalletCards },
{ href: '/admin/credit', label: 'Credit Management', iconPath: '/sidebar-icons/credits.svg', icon: WalletCards },
{ href: '/admin/coupon', label: 'Coupon Management', iconPath: '/sidebar-icons/coupon.svg', icon: Percent },
{ href: '/admin/discount', label: 'Discount Management', iconPath: '/sidebar-icons/discount.svg', icon: Percent },
{ href: '/admin/tax', label: 'Tax Management', iconPath: '/sidebar-icons/tax.svg', icon: Receipt },
{ href: '/admin/order', label: 'Order Management', iconPath: '/sidebar-icons/order.svg', icon: FileText },
{ href: '/admin/invoice', label: 'Invoice Management', iconPath: '/sidebar-icons/invoice.svg', icon: FileText },
{ href: '/admin/review', label: 'Review Management', iconPath: '/sidebar-icons/reviews.svg', icon: FileText },
{ href: '/admin/support', label: 'Support Management', iconPath: '/sidebar-icons/support.svg', icon: UserCircle2 },
{ href: '/admin/report', label: 'Report Management', iconPath: '/sidebar-icons/report.svg', icon: Bell },
{ href: '/admin/ledger', label: 'Ledger Management', iconPath: '/sidebar-icons/ledger.svg', icon: Receipt },
{ href: '/admin', label: 'Dashboard', icon: LayoutGrid },
{ href: '/admin/roles', label: 'Internal Role Management', icon: FolderCog, group: 'Management' },
{ href: '/admin/runtime-roles', label: 'External Role Management', icon: Users },
{ href: '/admin/onboarding-management', label: 'External Onboarding', icon: Users, aliasPrefix: '/admin/onboarding-schemas' },
{ href: '/admin/internal-dashboard-management', label: 'Internal Dashboards', icon: LayoutGrid },
{ href: '/admin/external-dashboard-management', label: 'External Dashboards', icon: LayoutGrid, aliasPrefix: '/admin/role-ui-configs' },
{ href: '/admin/approval', label: 'Approval Management', icon: ClipboardList },
{ href: '/admin/department', label: 'Department Management', icon: Briefcase, group: 'Organisation' },
{ href: '/admin/designation', label: 'Designation Management', icon: Briefcase },
{ href: '/admin/employees', label: 'Employee Management', icon: UserCircle2 },
{ href: '/admin/users', label: 'Users Management', icon: Users },
{ href: '/admin/company', label: 'Company Management', icon: Briefcase },
{ href: '/admin/candidate', label: 'Candidate Management', icon: UserCircle2 },
{ href: '/admin/customer', label: 'Customer Management', icon: UserCircle2 },
{ href: '/admin/photographer', label: 'Photographer Management', icon: Sparkles, group: 'Service Providers' },
{ href: '/admin/makeup-artist', label: 'Makeup Artist Management', icon: Sparkles },
{ href: '/admin/tutors', label: 'Tutors Management', icon: Sparkles },
{ href: '/admin/developers', label: 'Developers Management', icon: Sparkles },
{ href: '/admin/jobs', label: 'Jobs Management', icon: Briefcase, group: 'Operations' },
{ href: '/admin/leads', label: 'Leads Management', icon: HandHelping },
{ href: '/admin/pricing', label: 'Pricing Management', icon: WalletCards, group: 'Finance' },
{ href: '/admin/credit', label: 'Credit Management', icon: WalletCards },
{ href: '/admin/coupon', label: 'Coupon Management', icon: Percent },
{ href: '/admin/discount', label: 'Discount Management', icon: Percent },
{ href: '/admin/tax', label: 'Tax Management', icon: Receipt },
{ href: '/admin/order', label: 'Order Management', icon: FileText },
{ href: '/admin/invoice', label: 'Invoice Management', icon: FileText },
{ href: '/admin/review', label: 'Review Management', icon: FileText, group: 'Support' },
{ href: '/admin/support', label: 'Support Management', icon: UserCircle2 },
{ href: '/admin/report', label: 'Report Management', icon: Bell },
{ href: '/admin/ledger', label: 'Ledger Management', icon: Receipt },
{ href: '/admin/kb', label: 'Knowledge Base', icon: FileText },
{ href: '/admin/notifications', label: 'Notifications', icon: Bell },
];
function renderIcon(item: Item, isActive: boolean) {
if (item.iconPath) {
return <img src={item.iconPath} alt="" class={`h-[18px] w-[18px] object-contain ${isActive ? 'opacity-95' : 'opacity-70'}`} />;
}
const Icon = item.icon || FileText;
return <Icon size={18} class={isActive ? 'text-[#0f172a]' : 'text-slate-500'} />;
}
export default function AdminSidebar(props: { onNavigate?: () => void; onLogout?: () => void }) {
const location = useLocation();
@ -79,26 +72,42 @@ export default function AdminSidebar(props: { onNavigate?: () => void; onLogout?
};
return (
<aside class="flex h-full w-[310px] flex-col border-r border-[#d7d8df] bg-[#f6f6f8]">
<nav class="scrollbar flex-1 overflow-y-auto px-6 py-5">
<aside class="flex h-full w-[268px] flex-col border-r border-[#d7d8df] bg-white">
<nav class="scrollbar min-h-0 flex-1 overflow-y-auto px-3 py-4">
<For each={items}>
{(item) => {
const isActive = active(item);
{(item, index) => {
const isActive = () => active(item);
const showGroup = () => item.group && (index() === 0 || items[index() - 1]?.group !== item.group);
const Icon = item.icon || FileText;
return (
<>
<Show when={item.separatorBefore}>
<div class="my-4 border-t border-[#dfdfe5]" />
<Show when={showGroup()}>
<div class={`${index() > 0 ? 'mt-4' : ''} mb-1 px-3 pb-1`}>
<p class="text-[10px] font-bold uppercase tracking-widest text-slate-400">{item.group}</p>
</div>
</Show>
<A
href={item.href}
onClick={() => props.onNavigate?.()}
class={`group relative mb-1.5 flex min-h-[48px] items-center gap-3 rounded-xl px-4 text-[17px] font-medium leading-tight transition ${
isActive ? 'bg-[#ffece3] text-[#fd6216]' : 'text-[#2f3647] hover:bg-white hover:text-[#111827]'
title={item.label}
class={`group relative mb-0.5 flex min-h-[40px] w-full items-center gap-2.5 overflow-hidden rounded-lg px-3 text-[13.5px] font-semibold leading-tight transition-all duration-150 ${
isActive()
? 'bg-[#e8edf5] text-[#0a1d37]'
: 'text-[#44495a] hover:bg-slate-50 hover:text-[#0a1d37]'
}`}
>
<span class={`absolute bottom-0 left-0 top-0 w-[4px] rounded-r-md bg-[#fd6216] transition-opacity ${isActive ? 'opacity-100' : 'opacity-0'}`} />
{renderIcon(item, isActive)}
<span class="truncate">{item.label}</span>
{/* Left active indicator */}
<span
class={`absolute bottom-1.5 left-0 top-1.5 w-[3px] rounded-r-full bg-[#0a1d37] transition-opacity duration-150 ${
isActive() ? 'opacity-100' : 'opacity-0'
}`}
/>
<Icon
size={16}
class={`shrink-0 transition-colors ${isActive() ? 'text-[#0a1d37]' : 'text-slate-400 group-hover:text-slate-600'}`}
/>
<span class="min-w-0 flex-1 truncate">{item.label}</span>
</A>
</>
);
@ -106,13 +115,13 @@ export default function AdminSidebar(props: { onNavigate?: () => void; onLogout?
</For>
</nav>
<div class="border-t border-[#dfdfe5] px-6 py-5">
<div class="border-t border-[#e5e7ef] px-3 py-3">
<button
type="button"
onClick={() => props.onLogout?.()}
class="flex h-[50px] w-full items-center gap-3 rounded-xl px-4 text-left text-[17px] font-semibold text-[#c51d1d] transition hover:bg-[#fff1f1]"
class="flex h-[40px] w-full items-center gap-2.5 rounded-lg px-3 text-left text-[13.5px] font-semibold text-[#c51d1d] transition hover:bg-red-50"
>
<svg class="h-[18px] w-[18px]" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<svg class="h-4 w-4 shrink-0" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path stroke-linecap="round" stroke-linejoin="round" d="M17 16l4-4m0 0l-4-4m4 4H9m8 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h8a3 3 0 013 3v1" />
</svg>
<span>Sign Out</span>

View file

@ -17,9 +17,9 @@ type KpiCard = {
};
const KPI: KpiCard[] = [
{ label: 'Total Users', value: '—', badge: '+0%', badgeColor: 'text-emerald-600 bg-emerald-50', href: '/admin/users', icon: Users },
{ label: 'Total Users', value: '—', badge: '+0%', badgeColor: 'text-emerald-700 bg-emerald-50', href: '/admin/users', icon: Users },
{ label: 'Total Revenue', value: '—', badge: 'Weekly', badgeColor: 'text-[#0a1d37] bg-slate-100', href: '/admin/invoice', icon: Receipt },
{ label: 'Active Roles', value: '—', badge: 'Live', badgeColor: 'text-amber-600 bg-amber-50', href: '/admin/runtime-roles', icon: Briefcase},
{ label: 'Active Roles', value: '—', badge: 'Live', badgeColor: 'text-amber-700 bg-amber-50', href: '/admin/runtime-roles', icon: Briefcase},
{ label: 'Pending Approvals', value: '—', badge: 'Action Required', badgeColor: 'text-white bg-red-500', href: '/admin/approval', icon: Clock },
];
@ -35,8 +35,8 @@ const QUICK_ACTIONS = [
];
const PIPELINE = [
{ name: 'Candidate Roles', type: 'External Role', status: 'Active', statusColor: 'text-emerald-600 bg-emerald-50', progress: 85, barColor: 'bg-[#fd6216]' },
{ name: 'Onboarding Flows', type: 'Schema Builder', status: 'Pending', statusColor: 'text-amber-600 bg-amber-50', progress: 42, barColor: 'bg-amber-400' },
{ name: 'Candidate Roles', type: 'External Role', status: 'Active', statusColor: 'text-emerald-700 bg-emerald-50', progress: 85, barColor: 'bg-[#0a1d37]' },
{ name: 'Onboarding Flows', type: 'Schema Builder', status: 'Pending', statusColor: 'text-amber-700 bg-amber-50', progress: 42, barColor: 'bg-amber-400' },
{ name: 'Dashboard Config', type: 'UI Config', status: 'Draft', statusColor: 'text-slate-500 bg-slate-100', progress: 12, barColor: 'bg-slate-300' },
];
@ -45,7 +45,7 @@ const CONTROL: Array<{ label: string; href: string; desc: string; icon: Componen
{ label: 'External Roles', href: '/admin/runtime-roles', desc: 'Modules, credits & capabilities per external role.', icon: UserCog, iconBg: 'bg-violet-50', iconFg: 'text-violet-600' },
{ label: 'Onboarding Flows', href: '/admin/onboarding-schemas', desc: 'Schema-driven onboarding forms per external role.', icon: FormInput, iconBg: 'bg-amber-50', iconFg: 'text-amber-600' },
{ label: 'External Dashboards', href: '/admin/external-dashboard-management', desc: 'Sidebar, widgets & runtimeConfig per role.', icon: LayoutDashboard, iconBg: 'bg-teal-50', iconFg: 'text-teal-600' },
{ label: 'Internal Dashboards', href: '/admin/internal-dashboard-management', desc: 'Home widgets & KPI panels for internal staff.', icon: LayoutDashboard, iconBg: 'bg-orange-50', iconFg: 'text-orange-600' },
{ label: 'Internal Dashboards', href: '/admin/internal-dashboard-management', desc: 'Home widgets & KPI panels for internal staff.', icon: LayoutDashboard, iconBg: 'bg-sky-50', iconFg: 'text-sky-600' },
{ label: 'Approval Queue', href: '/admin/approval', desc: 'Review, approve or reject pending action requests.', icon: BadgeCheck, iconBg: 'bg-rose-50', iconFg: 'text-rose-600' },
];
@ -55,33 +55,38 @@ export default function AdminDashboard() {
<div class="space-y-6">
{/* ── KPI Cards ── */}
<div class="grid grid-cols-2 gap-4 lg:grid-cols-4">
<section class="rounded-xl border border-slate-200/70 bg-white p-5 shadow-[0_4px_16px_-8px_rgba(10,29,55,0.18)]">
<div class="mb-4 flex items-center justify-between">
<h2 class="text-[15px] font-black tracking-tight text-[#0a1d37]">Quick Stats</h2>
<span class="rounded-md bg-[#0a1d37]/8 px-2.5 py-1 text-[10px] font-bold uppercase tracking-[0.14em] text-[#0a1d37]">Live Overview</span>
</div>
<div class="grid grid-cols-1 gap-3 sm:grid-cols-2 xl:grid-cols-4">
{KPI.map((kpi) => {
const Icon = kpi.icon;
return (
<A
href={kpi.href}
class="relative overflow-hidden rounded-xl bg-white p-5 shadow-[0_4px_20px_rgba(10,29,55,0.06)] transition-all duration-200 hover:-translate-y-px hover:shadow-[0_8px_28px_rgba(10,29,55,0.10)]"
class="relative overflow-hidden rounded-lg border border-slate-200/70 bg-[#f9fafc] p-4 transition-all duration-200 hover:-translate-y-px hover:border-[#0a1d37]/20 hover:shadow-[0_8px_20px_-12px_rgba(10,29,55,0.35)]"
>
{/* Left navy accent bar */}
<div class="absolute left-0 top-0 h-full w-1.5 rounded-l-xl bg-[#0a1d37]" />
<div class="absolute left-0 top-0 h-full w-1 rounded-l-lg bg-[#0a1d37]" />
<div class="flex items-start justify-between pl-1">
<div class="flex h-10 w-10 items-center justify-center rounded-lg bg-[#0a1d37]/6">
<Icon class="h-5 w-5 text-[#0a1d37]" />
<div class="flex items-start justify-between pl-2">
<div class="flex h-8 w-8 items-center justify-center rounded-md bg-[#0a1d37]/8">
<Icon class="h-4 w-4 text-[#0a1d37]" />
</div>
{kpi.badge && (
<span class={`rounded-md px-2 py-0.5 text-[10px] font-bold ${kpi.badgeColor}`}>
<span class={`rounded px-1.5 py-0.5 text-[10px] font-bold ${kpi.badgeColor}`}>
{kpi.badge}
</span>
)}
</div>
<div class="mt-4 pl-1">
<div class="mt-3 pl-2">
<h3 class="text-[10px] font-bold uppercase tracking-widest text-slate-400">
{kpi.label}
</h3>
<p class="mt-1 text-3xl font-black tracking-tight text-[#0a1d37]">
<p class="mt-1 text-2xl font-black tabular-nums tracking-tight text-[#0a1d37]">
{kpi.value}
</p>
</div>
@ -89,27 +94,28 @@ export default function AdminDashboard() {
);
})}
</div>
</section>
{/* ── Activity + Intelligence Hub ── */}
<div class="grid grid-cols-1 gap-4 lg:grid-cols-3">
{/* Activity Chart */}
<div class="lg:col-span-2 rounded-xl bg-white p-6 shadow-[0_4px_20px_rgba(10,29,55,0.06)]">
<div class="mb-6 flex items-end justify-between">
<div class="lg:col-span-2 rounded-xl border border-slate-200/70 bg-white p-5 shadow-[0_4px_16px_-8px_rgba(10,29,55,0.18)]">
<div class="mb-5 flex items-end justify-between">
<div>
<h2 class="text-lg font-black tracking-tight text-[#0a1d37]">System Activity</h2>
<p class="mt-0.5 text-sm text-slate-400">Platform traffic & Tracecoin velocity</p>
<h2 class="text-[15px] font-black tracking-tight text-[#0a1d37]">System Activity</h2>
<p class="mt-0.5 text-[12px] text-slate-400">Platform traffic & Tracecoin velocity</p>
</div>
<select class="rounded-lg border border-slate-200 bg-white py-1.5 pl-3 pr-8 text-xs font-semibold text-[#0a1d37] focus:outline-none focus:ring-2 focus:ring-[#0a1d37]/10">
<select class="rounded-md border border-slate-200 bg-white py-1.5 pl-3 pr-7 text-[11px] font-semibold text-[#0a1d37] focus:outline-none focus:ring-2 focus:ring-[#0a1d37]/10">
<option>Last 7 Days</option>
<option>Last 30 Days</option>
</select>
</div>
<div class="flex h-44 items-end justify-between gap-2">
<div class="flex h-40 items-end justify-between gap-2">
{ACTIVITY.map((d) => (
<div class="group flex flex-1 flex-col items-center gap-1.5">
<div
class={`w-full rounded-t-md transition-all duration-200 ${d.day === 'WED' ? 'bg-[#0a1d37] shadow-lg shadow-[#0a1d37]/20' : 'bg-slate-100 group-hover:bg-slate-300'}`}
class={`w-full rounded-t transition-all duration-200 ${d.day === 'WED' ? 'bg-[#0a1d37] shadow-md shadow-[#0a1d37]/20' : 'bg-slate-100 group-hover:bg-slate-200'}`}
style={`height: ${d.h}%`}
/>
<span class={`text-[10px] font-bold ${d.day === 'WED' ? 'text-[#0a1d37]' : 'text-slate-400'}`}>
@ -121,21 +127,21 @@ export default function AdminDashboard() {
</div>
{/* Intelligence Hub + Pipeline */}
<div class="space-y-4">
<div class="space-y-3">
{/* Intelligence Hub */}
<div class="relative overflow-hidden rounded-xl bg-[#0a1d37] p-5 text-white">
<div class="relative overflow-hidden rounded-xl bg-[#0a1d37] p-4 text-white shadow-[0_8px_24px_-10px_rgba(10,29,55,0.55)]">
<div class="absolute -right-4 -top-4 h-20 w-20 rounded-full bg-white/5 blur-2xl" />
<h2 class="mb-3 text-base font-black">Intelligence Hub</h2>
<div class="space-y-2">
<h2 class="mb-3 text-[13px] font-black">Intelligence Hub</h2>
<div class="space-y-1.5">
{QUICK_ACTIONS.map((a) => {
const Icon = a.icon;
return (
<A
href={a.href}
class="group flex w-full items-center justify-between rounded-lg bg-white/10 px-3.5 py-3 transition-all hover:bg-white/15"
class="group flex w-full items-center justify-between rounded-md bg-white/10 px-3 py-2.5 transition-all hover:bg-white/15"
>
<span class="text-[13px] font-semibold">{a.label}</span>
<Icon class="h-4 w-4 text-[#fd6216] transition-transform group-hover:translate-x-0.5" />
<span class="text-[12.5px] font-semibold">{a.label}</span>
<Icon class="h-3.5 w-3.5 text-white/60 transition-transform group-hover:translate-x-0.5" />
</A>
);
})}
@ -143,23 +149,23 @@ export default function AdminDashboard() {
</div>
{/* Pipeline Status */}
<div class="rounded-xl bg-white p-5 shadow-[0_4px_20px_rgba(10,29,55,0.06)]">
<h2 class="mb-4 text-[10px] font-bold uppercase tracking-widest text-[#0a1d37]">
<div class="rounded-xl border border-slate-200/70 bg-white p-4 shadow-[0_4px_16px_-8px_rgba(10,29,55,0.18)]">
<h2 class="mb-3 text-[10px] font-bold uppercase tracking-widest text-[#0a1d37]">
Pipeline Status
</h2>
<div class="space-y-4">
<div class="space-y-3.5">
{PIPELINE.map((p) => (
<div>
<div class="mb-1.5 flex items-center justify-between gap-2">
<div class="min-w-0">
<p class="truncate text-[13px] font-bold text-[#0a1d37]">{p.name}</p>
<p class="truncate text-[12.5px] font-bold text-[#0a1d37]">{p.name}</p>
<p class="text-[10px] text-slate-400">{p.type}</p>
</div>
<span class={`shrink-0 rounded-md px-2 py-0.5 text-[10px] font-bold uppercase ${p.statusColor}`}>
<span class={`shrink-0 rounded px-1.5 py-0.5 text-[10px] font-bold uppercase ${p.statusColor}`}>
{p.status}
</span>
</div>
<div class="h-1.5 w-full overflow-hidden rounded-full bg-slate-100">
<div class="h-1 w-full overflow-hidden rounded-full bg-slate-100">
<div class={`h-full rounded-full ${p.barColor}`} style={`width: ${p.progress}%`} />
</div>
</div>
@ -171,9 +177,9 @@ export default function AdminDashboard() {
{/* ── Control Plane ── */}
<div>
<div class="mb-4 flex items-center justify-between">
<h2 class="text-lg font-black tracking-tight text-[#0a1d37]">Control Plane</h2>
<span class="rounded-md border border-slate-200 bg-white px-2.5 py-1 text-[10px] font-bold uppercase tracking-widest text-slate-400">
<div class="mb-3 flex items-center justify-between">
<h2 class="text-[15px] font-black tracking-tight text-[#0a1d37]">Control Plane</h2>
<span class="rounded border border-slate-200 bg-white px-2 py-0.5 text-[10px] font-bold uppercase tracking-widest text-slate-400">
{CONTROL.length} modules
</span>
</div>
@ -183,13 +189,13 @@ export default function AdminDashboard() {
return (
<A
href={item.href}
class="group flex items-start gap-3.5 rounded-xl border border-slate-100 bg-white p-4 shadow-[0_4px_20px_rgba(10,29,55,0.04)] transition-all duration-200 hover:-translate-y-px hover:border-[#0a1d37]/10 hover:shadow-[0_8px_28px_rgba(10,29,55,0.08)]"
class="group flex items-start gap-3 rounded-lg border border-slate-200/70 bg-white p-3.5 shadow-[0_2px_8px_-4px_rgba(10,29,55,0.18)] transition-all duration-200 hover:-translate-y-px hover:border-[#0a1d37]/20 hover:shadow-[0_8px_20px_-8px_rgba(10,29,55,0.28)]"
>
<div class={`mt-0.5 flex h-8 w-8 shrink-0 items-center justify-center rounded-lg ${item.iconBg}`}>
<Icon class={`h-4 w-4 ${item.iconFg}`} />
<div class={`mt-0.5 flex h-7 w-7 shrink-0 items-center justify-center rounded-md ${item.iconBg}`}>
<Icon class={`h-3.5 w-3.5 ${item.iconFg}`} />
</div>
<div class="min-w-0 flex-1">
<p class="truncate text-[13px] font-bold text-[#0a1d37] transition-colors group-hover:text-[#fd6216]">{item.label}</p>
<p class="truncate text-[13px] font-bold text-[#0a1d37] transition-colors group-hover:text-[#0a1d37]">{item.label}</p>
<p class="mt-0.5 text-[11px] leading-relaxed text-slate-400">{item.desc}</p>
</div>
<ArrowRight class="mt-0.5 h-3.5 w-3.5 shrink-0 text-slate-200 transition-all group-hover:translate-x-0.5 group-hover:text-[#0a1d37]" />