From bbaeb1b2a9931572dbdfe5f38f062d02084d5884 Mon Sep 17 00:00:00 2001 From: Ashwin Kumar Date: Thu, 19 Mar 2026 03:36:46 +0100 Subject: [PATCH] feat(admin): align nextjs sidebar/ui parity and module flow bridge --- public/sidebar-icons/approval.svg | 3 + public/sidebar-icons/candidate.svg | 3 + public/sidebar-icons/company.svg | 3 + public/sidebar-icons/coupon.svg | 3 + public/sidebar-icons/credits.svg | 3 + public/sidebar-icons/dashboard.svg | 3 + public/sidebar-icons/department.svg | 3 + public/sidebar-icons/designation.svg | 3 + public/sidebar-icons/developers.svg | 3 + public/sidebar-icons/discount.svg | 4 + public/sidebar-icons/invoice.svg | 3 + public/sidebar-icons/jobs.svg | 3 + public/sidebar-icons/leads.svg | 3 + public/sidebar-icons/ledger.svg | 3 + public/sidebar-icons/makeup-artist.svg | 3 + public/sidebar-icons/order.svg | 3 + public/sidebar-icons/photographer.svg | 3 + public/sidebar-icons/pricing.svg | 3 + public/sidebar-icons/report.svg | 3 + public/sidebar-icons/reviews.svg | 4 + public/sidebar-icons/role.svg | 3 + public/sidebar-icons/support.svg | 6 + public/sidebar-icons/tax.svg | 3 + public/sidebar-icons/tutor.svg | 3 + public/sidebar-icons/users.svg | 3 + src/app.css | 555 ++++++++++++++++-- src/components/AdminShell.tsx | 80 ++- src/components/AdminSidebar.tsx | 80 ++- src/lib/admin-session.ts | 17 + src/routes/admin/[...module].tsx | 57 ++ src/routes/admin/onboarding-schemas/index.tsx | 44 +- src/routes/admin/role-ui-configs/index.tsx | 44 +- src/routes/admin/runtime-roles/index.tsx | 44 +- src/routes/index.tsx | 14 +- src/routes/login.tsx | 400 +++++++++++++ 35 files changed, 1273 insertions(+), 142 deletions(-) create mode 100644 public/sidebar-icons/approval.svg create mode 100644 public/sidebar-icons/candidate.svg create mode 100644 public/sidebar-icons/company.svg create mode 100644 public/sidebar-icons/coupon.svg create mode 100644 public/sidebar-icons/credits.svg create mode 100644 public/sidebar-icons/dashboard.svg create mode 100644 public/sidebar-icons/department.svg create mode 100644 public/sidebar-icons/designation.svg create mode 100644 public/sidebar-icons/developers.svg create mode 100644 public/sidebar-icons/discount.svg create mode 100644 public/sidebar-icons/invoice.svg create mode 100644 public/sidebar-icons/jobs.svg create mode 100644 public/sidebar-icons/leads.svg create mode 100644 public/sidebar-icons/ledger.svg create mode 100644 public/sidebar-icons/makeup-artist.svg create mode 100644 public/sidebar-icons/order.svg create mode 100644 public/sidebar-icons/photographer.svg create mode 100644 public/sidebar-icons/pricing.svg create mode 100644 public/sidebar-icons/report.svg create mode 100644 public/sidebar-icons/reviews.svg create mode 100644 public/sidebar-icons/role.svg create mode 100644 public/sidebar-icons/support.svg create mode 100644 public/sidebar-icons/tax.svg create mode 100644 public/sidebar-icons/tutor.svg create mode 100644 public/sidebar-icons/users.svg create mode 100644 src/lib/admin-session.ts create mode 100644 src/routes/admin/[...module].tsx create mode 100644 src/routes/login.tsx diff --git a/public/sidebar-icons/approval.svg b/public/sidebar-icons/approval.svg new file mode 100644 index 0000000..530dfd0 --- /dev/null +++ b/public/sidebar-icons/approval.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/sidebar-icons/candidate.svg b/public/sidebar-icons/candidate.svg new file mode 100644 index 0000000..577a510 --- /dev/null +++ b/public/sidebar-icons/candidate.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/sidebar-icons/company.svg b/public/sidebar-icons/company.svg new file mode 100644 index 0000000..22e2127 --- /dev/null +++ b/public/sidebar-icons/company.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/sidebar-icons/coupon.svg b/public/sidebar-icons/coupon.svg new file mode 100644 index 0000000..536c820 --- /dev/null +++ b/public/sidebar-icons/coupon.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/sidebar-icons/credits.svg b/public/sidebar-icons/credits.svg new file mode 100644 index 0000000..ce9909a --- /dev/null +++ b/public/sidebar-icons/credits.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/sidebar-icons/dashboard.svg b/public/sidebar-icons/dashboard.svg new file mode 100644 index 0000000..4c5d244 --- /dev/null +++ b/public/sidebar-icons/dashboard.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/sidebar-icons/department.svg b/public/sidebar-icons/department.svg new file mode 100644 index 0000000..af49671 --- /dev/null +++ b/public/sidebar-icons/department.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/sidebar-icons/designation.svg b/public/sidebar-icons/designation.svg new file mode 100644 index 0000000..04c75d6 --- /dev/null +++ b/public/sidebar-icons/designation.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/sidebar-icons/developers.svg b/public/sidebar-icons/developers.svg new file mode 100644 index 0000000..fddb650 --- /dev/null +++ b/public/sidebar-icons/developers.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/sidebar-icons/discount.svg b/public/sidebar-icons/discount.svg new file mode 100644 index 0000000..8f0f3ab --- /dev/null +++ b/public/sidebar-icons/discount.svg @@ -0,0 +1,4 @@ + + + + diff --git a/public/sidebar-icons/invoice.svg b/public/sidebar-icons/invoice.svg new file mode 100644 index 0000000..a93fb34 --- /dev/null +++ b/public/sidebar-icons/invoice.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/sidebar-icons/jobs.svg b/public/sidebar-icons/jobs.svg new file mode 100644 index 0000000..d689de2 --- /dev/null +++ b/public/sidebar-icons/jobs.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/sidebar-icons/leads.svg b/public/sidebar-icons/leads.svg new file mode 100644 index 0000000..8627818 --- /dev/null +++ b/public/sidebar-icons/leads.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/sidebar-icons/ledger.svg b/public/sidebar-icons/ledger.svg new file mode 100644 index 0000000..a7d6fb0 --- /dev/null +++ b/public/sidebar-icons/ledger.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/sidebar-icons/makeup-artist.svg b/public/sidebar-icons/makeup-artist.svg new file mode 100644 index 0000000..79c01fb --- /dev/null +++ b/public/sidebar-icons/makeup-artist.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/sidebar-icons/order.svg b/public/sidebar-icons/order.svg new file mode 100644 index 0000000..c914546 --- /dev/null +++ b/public/sidebar-icons/order.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/sidebar-icons/photographer.svg b/public/sidebar-icons/photographer.svg new file mode 100644 index 0000000..510e9c5 --- /dev/null +++ b/public/sidebar-icons/photographer.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/sidebar-icons/pricing.svg b/public/sidebar-icons/pricing.svg new file mode 100644 index 0000000..3a96b51 --- /dev/null +++ b/public/sidebar-icons/pricing.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/sidebar-icons/report.svg b/public/sidebar-icons/report.svg new file mode 100644 index 0000000..25f8b78 --- /dev/null +++ b/public/sidebar-icons/report.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/sidebar-icons/reviews.svg b/public/sidebar-icons/reviews.svg new file mode 100644 index 0000000..e94ad5d --- /dev/null +++ b/public/sidebar-icons/reviews.svg @@ -0,0 +1,4 @@ + + + + diff --git a/public/sidebar-icons/role.svg b/public/sidebar-icons/role.svg new file mode 100644 index 0000000..45d3098 --- /dev/null +++ b/public/sidebar-icons/role.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/sidebar-icons/support.svg b/public/sidebar-icons/support.svg new file mode 100644 index 0000000..839049b --- /dev/null +++ b/public/sidebar-icons/support.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/public/sidebar-icons/tax.svg b/public/sidebar-icons/tax.svg new file mode 100644 index 0000000..be9a339 --- /dev/null +++ b/public/sidebar-icons/tax.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/sidebar-icons/tutor.svg b/public/sidebar-icons/tutor.svg new file mode 100644 index 0000000..01e72a4 --- /dev/null +++ b/public/sidebar-icons/tutor.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/sidebar-icons/users.svg b/public/sidebar-icons/users.svg new file mode 100644 index 0000000..0a193c1 --- /dev/null +++ b/public/sidebar-icons/users.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/app.css b/src/app.css index c67105e..1ae6224 100644 --- a/src/app.css +++ b/src/app.css @@ -6,6 +6,7 @@ --brand-orange-50: #fff1e8; --brand-orange-100: #ffe2d2; --brand-orange-200: #ffc9ac; + --ink: #0f172a; } * { @@ -14,67 +15,390 @@ body { margin: 0; - background: #f3f4f8; font-family: 'Exo 2', sans-serif; + background: #f8fafc; + color: var(--ink); -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; +} + +/* ---- Auth / Login ---- */ +.auth-page { + position: relative; + min-height: 100vh; + display: grid; + place-items: center; + padding: 20px; + overflow: hidden; +} + +.auth-bg { + position: absolute; + inset: 0; + background: + radial-gradient(circle at 20% 20%, rgba(253, 98, 22, 0.24), transparent 42%), + radial-gradient(circle at 80% 10%, rgba(99, 102, 241, 0.16), transparent 34%), + linear-gradient(180deg, #100b2f 0%, #0c0828 52%, #07051d 100%); +} + +.auth-layout { + position: relative; + z-index: 1; + width: min(1120px, 100%); + display: grid; + grid-template-columns: 1.08fr 0.92fr; + gap: 24px; + align-items: stretch; +} + +.auth-visual { + border-radius: 24px; + border: 1px solid rgba(255, 255, 255, 0.18); + background: rgba(255, 255, 255, 0.08); + padding: 28px; + box-shadow: 0 24px 72px -34px rgba(0, 0, 0, 0.72); +} + +.auth-visual-kicker { + margin: 0; + display: inline-flex; + border-radius: 999px; + border: 1px solid rgba(255, 255, 255, 0.2); + background: rgba(255, 255, 255, 0.1); + color: #ffd7c2; + font-size: 11px; + font-weight: 700; + letter-spacing: 0.12em; + text-transform: uppercase; + padding: 6px 10px; +} + +.auth-visual h1 { + margin: 16px 0 0; + font-size: clamp(30px, 3.4vw, 42px); + line-height: 1.1; + color: #fff; +} + +.auth-visual p { + margin: 12px 0 0; + color: rgba(255, 255, 255, 0.82); +} + +.auth-visual img { + margin-top: 18px; + width: 100%; + height: 280px; + border-radius: 16px; + object-fit: cover; +} + +.auth-card { + position: relative; + z-index: 1; + border-radius: 24px; + border: 1px solid rgba(255, 255, 255, 0.22); + background: rgba(255, 255, 255, 0.96); + box-shadow: 0 24px 72px -34px rgba(0, 0, 0, 0.72); + padding: 30px; + color: #0f172a; +} + +.auth-form-card { + display: flex; + flex-direction: column; +} + +.auth-logo { + height: 52px; + width: auto; + object-fit: contain; + margin: 0 auto; +} + +.auth-title { + margin: 18px 0 0; + text-align: center; + font-size: 32px; + font-weight: 800; + color: #0f172a; +} + +.auth-copy { + margin: 12px 0 0; + text-align: center; + color: #475569; +} + +.auth-form-grid { + margin-top: 24px; +} + +.auth-switch { + display: flex; + justify-content: flex-end; + margin-top: -2px; +} + +.auth-switch.split { + justify-content: space-between; +} + +.auth-link-btn { + border: 0; + padding: 0; + background: transparent; + color: var(--brand-orange); + font-size: 13px; + font-weight: 600; + cursor: pointer; +} + +.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 ---- */ +.admin-root { + min-height: 100vh; + background: #f8fafc; +} + +.admin-header { + position: sticky; + top: 0; + z-index: 40; + border-bottom: 1px solid #e2e8f0; + background: rgba(255, 255, 255, 0.94); + backdrop-filter: blur(12px); +} + +.admin-header-inner { + width: min(1440px, calc(100% - 36px)); + margin: 0 auto; + min-height: 64px; + display: flex; + align-items: center; + justify-content: space-between; + gap: 14px; +} + +.admin-brand { + display: flex; + align-items: center; + gap: 12px; +} + +.admin-brand img { + height: 44px; + width: auto; +} + +.admin-brand-kicker { + margin: 0; + color: #64748b; + font-size: 11px; + font-weight: 700; + letter-spacing: 0.1em; + text-transform: uppercase; +} + +.admin-brand h1 { + margin: 2px 0 0; + font-size: 17px; + font-weight: 800; color: var(--brand-navy); } +.admin-header-actions { + display: flex; + align-items: center; + gap: 10px; +} + +.admin-role-chip { + margin: 0; + border: 1px solid #fdba74; + background: #fff7ed; + color: #9a3412; + border-radius: 999px; + padding: 6px 11px; + font-size: 11px; + font-weight: 700; + letter-spacing: 0.08em; + text-transform: uppercase; +} + .shell { display: grid; - grid-template-columns: 280px 1fr; - min-height: 100vh; + grid-template-columns: 264px 1fr; + min-height: calc(100vh - 64px); } .sidebar { - border-right: 1px solid #dbe1ec; + border-right: 1px solid #e2e8f0; background: #fcfcfd; - padding: 16px; + padding: 20px 12px 12px; + display: flex; + flex-direction: column; + min-height: 0; } -.brand { - margin: 4px 0 18px; +.sidebar-toggle-row { + margin-bottom: 12px; + display: flex; + justify-content: flex-end; + padding: 0 6px; } -.brand img { - height: 42px; - width: auto; - object-fit: contain; +.sidebar-toggle-btn { + border: 0; + border-radius: 8px; + background: transparent; + color: #64748b; + font-size: 22px; + line-height: 1; + padding: 3px 8px; + cursor: pointer; +} + +.sidebar-toggle-btn:hover { + background: #f1f5f9; + color: #334155; +} + +.sidebar-nav { + flex: 1; + min-height: 0; + overflow-y: auto; + padding-right: 4px; } .nav-item { - display: block; + position: relative; + display: flex; + align-items: center; + gap: 10px; text-decoration: none; color: #475569; border: 1px solid transparent; - background: #fff; border-radius: 12px; - padding: 10px 12px; - margin-bottom: 8px; - font-size: 14px; + padding: 11px 12px; + margin-bottom: 6px; + font-size: 15px; + line-height: 1.35; + font-weight: 500; + transition: all 180ms ease; +} + +.nav-dot { + width: 8px; + height: 8px; + border-radius: 999px; + background: #cbd5e1; + transition: background-color 180ms ease; +} + +.nav-icon { + width: 18px; + height: 18px; + flex: 0 0 18px; + object-fit: contain; } .nav-item:hover { - border-color: #dbe1ec; - color: #1f2937; + border-color: #e2e8f0; + background: #fff; + color: #0f172a; +} + +.nav-item:hover .nav-dot { + background: #94a3b8; } .nav-item.active { border-color: var(--brand-orange-200); - background: linear-gradient(90deg, var(--brand-orange-50), var(--brand-orange-100)); + background: linear-gradient(to right, var(--brand-orange-50), color-mix(in srgb, var(--brand-orange-100) 70%, white 30%)); color: #111827; - font-weight: 600; + font-weight: 700; + box-shadow: inset 3px 0 0 0 var(--brand-orange); +} + +.nav-title { + flex: 1; +} + +.active-badge { + border: 1px solid var(--brand-orange-200); + border-radius: 999px; + background: var(--brand-orange-100); + color: #b45309; + font-size: 10px; + font-weight: 700; + letter-spacing: 0.04em; + text-transform: uppercase; + padding: 2px 7px; } .main { - padding: 24px; + min-width: 0; + padding: 14px 16px 16px; } +.main-inner { + max-width: 1180px; +} + +.admin-tab-wrap { + border-bottom: 1px solid #e2e8f0; + background: #fff; + margin: -2px -16px 16px; + padding: 0 16px; +} + +.admin-tabs { + display: flex; + flex-wrap: wrap; + gap: 20px; +} + +.admin-tab { + text-decoration: none; + border-bottom: 2px solid transparent; + padding: 12px 0; + font-size: 14px; + font-weight: 600; + color: #475569; + margin-bottom: -1px; + transition: color 140ms ease, border-color 140ms ease; +} + +.admin-tab:hover { + color: #0f172a; + border-bottom-color: #fd6216; +} + +.admin-tab.active { + border-bottom-color: #fd6216; + color: #050026; +} + +/* ---- Shared Content ---- */ .page-title { margin: 0; - font-size: 28px; - font-weight: 700; + font-size: 30px; + font-weight: 800; color: #0f172a; } @@ -87,29 +411,31 @@ body { display: grid; grid-template-columns: 1.2fr 1fr; gap: 16px; - margin-top: 20px; + margin-top: 16px; } .card { background: #fff; - border: 1px solid #dbe1ec; - border-radius: 16px; - padding: 16px; + border: 1px solid #e2e8f0; + border-radius: 12px; + padding: 18px; + box-shadow: 0 12px 32px -28px rgba(15, 23, 42, 0.35); } .card h2 { margin: 0 0 12px; - font-size: 18px; + font-size: 19px; + color: #111827; } .field { - margin-bottom: 12px; + margin-bottom: 14px; } .field label { display: block; margin-bottom: 6px; - font-size: 13px; + font-size: 12px; font-weight: 600; color: #334155; } @@ -119,26 +445,48 @@ body { .field select { width: 100%; border: 1px solid #cbd5e1; - border-radius: 10px; - padding: 10px; + border-radius: 8px; + padding: 10px 12px; font-size: 14px; background: #fff; + outline: none; + transition: border-color 160ms ease, box-shadow 160ms ease; +} + +.field input:focus, +.field textarea:focus, +.field select:focus { + border-color: var(--brand-orange); + box-shadow: 0 0 0 3px rgba(253, 98, 22, 0.14); } .actions { display: flex; gap: 10px; margin-top: 16px; + flex-wrap: wrap; } .btn { border: 1px solid #cbd5e1; background: #fff; color: #334155; - border-radius: 10px; - padding: 10px 14px; - font-weight: 600; + border-radius: 8px; + padding: 9px 14px; + font-weight: 700; + text-decoration: none; cursor: pointer; + transition: all 160ms ease; +} + +.btn:hover { + border-color: #94a3b8; + color: #0f172a; +} + +.btn:disabled { + opacity: 0.6; + cursor: not-allowed; } .btn.primary { @@ -147,6 +495,17 @@ body { color: #fff; } +.btn.primary:hover { + border-color: #ea580c; + background: #ea580c; +} + +.block-btn { + width: 100%; + justify-content: center; + text-align: center; +} + .json { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 12px; @@ -160,31 +519,16 @@ body { } .notice { - margin-top: 8px; + margin-top: 10px; font-size: 12px; color: #64748b; } -@media (max-width: 1000px) { - .shell { - grid-template-columns: 1fr; - } - - .sidebar { - border-right: 0; - border-bottom: 1px solid #dbe1ec; - } - - .grid { - grid-template-columns: 1fr; - } -} - .inline-note { margin-top: 10px; font-size: 12px; color: #0f766e; - font-weight: 600; + font-weight: 700; } .list-header { @@ -201,6 +545,66 @@ body { gap: 12px; } +.table-wrap { + overflow-x: auto; +} + +.list-table { + width: 100%; + border-collapse: separate; + border-spacing: 0; + font-size: 14px; +} + +.list-table thead th { + background: #050026; + color: #fff; + text-align: left; + font-size: 12px; + font-weight: 700; + letter-spacing: 0.04em; + text-transform: uppercase; + padding: 12px; +} + +.list-table tbody td { + border-bottom: 1px solid #e2e8f0; + padding: 12px; + color: #334155; + vertical-align: middle; +} + +.list-table tbody tr:hover td { + background: #fff7ed; +} + +.align-right { + text-align: right !important; +} + +.table-actions { + display: flex; + justify-content: flex-end; + gap: 8px; +} + +.status-chip { + display: inline-flex; + align-items: center; + border-radius: 999px; + padding: 4px 10px; + font-size: 11px; + font-weight: 700; + text-transform: uppercase; + background: #f1f5f9; + color: #475569; +} + +.status-chip.active { + background: #ecfdf5; + color: #047857; +} + .list-item { border: 1px solid #dbe1ec; border-radius: 12px; @@ -219,17 +623,6 @@ body { color: #475569; } -@media (max-width: 1000px) { - .list-grid { - grid-template-columns: 1fr; - } - - .list-header { - flex-direction: column; - align-items: flex-start; - } -} - .json-input { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 12px; @@ -241,5 +634,39 @@ body { margin-top: 8px; font-size: 12px; color: #b91c1c; - font-weight: 600; + font-weight: 700; +} + +@media (max-width: 1180px) { + .auth-layout { + grid-template-columns: 1fr; + } + + .auth-visual { + display: none; + } +} + +@media (max-width: 1000px) { + .shell { + grid-template-columns: 1fr; + } + + .sidebar { + border-right: 0; + border-bottom: 1px solid #dbe1ec; + } + + .grid, + .list-grid { + grid-template-columns: 1fr; + } + + .admin-header-inner { + min-height: 62px; + } + + .admin-brand img { + height: 36px; + } } diff --git a/src/components/AdminShell.tsx b/src/components/AdminShell.tsx index 748a8d6..bfd9cc7 100644 --- a/src/components/AdminShell.tsx +++ b/src/components/AdminShell.tsx @@ -1,11 +1,83 @@ -import type { JSX } from 'solid-js'; +import { A, useLocation, useNavigate } from '@solidjs/router'; +import { createSignal, onMount, type JSX } from 'solid-js'; import AdminSidebar from './AdminSidebar'; +import { clearAdminSession, hasAdminSession } from '~/lib/admin-session'; export default function AdminShell(props: { children: JSX.Element }) { + const location = useLocation(); + const navigate = useNavigate(); + const [checkedSession, setCheckedSession] = createSignal(false); + const tabs = [ + { href: '/admin/runtime-roles', label: 'View Roles' }, + { href: '/admin/runtime-roles/new', label: 'Create Role' }, + { href: '/admin/role-ui-configs', label: 'Inspector' }, + { href: '/admin/onboarding-schemas', label: 'Onboarding' }, + ]; + + const isTabActive = (href: string) => { + if (href === '/admin/runtime-roles') { + return location.pathname === href || (location.pathname.startsWith('/admin/runtime-roles/') && location.pathname !== '/admin/runtime-roles/new'); + } + return location.pathname === href || location.pathname.startsWith(`${href}/`); + }; + + onMount(() => { + if (!hasAdminSession()) { + const from = encodeURIComponent(location.pathname + location.search); + navigate(`/login?from=${from}`, { replace: true }); + return; + } + setCheckedSession(true); + }); + + const onLogout = () => { + clearAdminSession(); + navigate('/login', { replace: true }); + }; + return ( -
- -
{props.children}
+
+
+
+
+ NXTGAUGE +
+

Administration

+

NXTGAUGE Admin Panel

+
+
+
+

Super Admin

+ +
+
+
+ + {checkedSession() ? ( +
+ +
+
+ +
+
{props.children}
+
+
+ ) : ( +
+
+
+

Checking session...

+
+
+
+ )}
); } diff --git a/src/components/AdminSidebar.tsx b/src/components/AdminSidebar.tsx index fe0d350..4c11089 100644 --- a/src/components/AdminSidebar.tsx +++ b/src/components/AdminSidebar.tsx @@ -1,37 +1,71 @@ import { A, useLocation } from '@solidjs/router'; -const links = [ - { href: '/admin/runtime-roles/new', label: 'Create Role' }, - { href: '/admin/runtime-roles', label: 'Manage Roles' }, - { href: '/admin/role-ui-configs/new', label: 'Create Dashboard' }, - { href: '/admin/role-ui-configs', label: 'Manage Dashboards' }, - { href: '/admin/onboarding-schemas/new', label: 'Create Onboarding Flow' }, - { href: '/admin/onboarding-schemas', label: 'Manage Onboarding Flows' }, +type LinkItem = { legacyHref: string; href: string; label: string; icon: string; aliasPrefix?: string }; + +const links: LinkItem[] = [ + { legacyHref: '/', href: '/admin', label: 'Dashboard', icon: 'dashboard.svg' }, + { legacyHref: '/department', href: '/admin/department', label: 'Department Management', icon: 'department.svg' }, + { legacyHref: '/designation', href: '/admin/designation', label: 'Designation Management', icon: 'designation.svg' }, + { legacyHref: '/employees', href: '/admin/employees', label: 'Internal User Management', icon: 'users.svg' }, + { legacyHref: '/roles?scope=internal', href: '/admin/roles?scope=internal', label: 'Internal Role Management', icon: 'role.svg' }, + { legacyHref: '/runtime-roles', href: '/admin/runtime-roles', label: 'External Role Management', icon: 'role.svg' }, + { legacyHref: '/onboarding-management', href: '/admin/onboarding-management', label: 'Onboarding Management', icon: 'reviews.svg', aliasPrefix: '/admin/onboarding-schemas' }, + { legacyHref: '/internal-dashboard-management', href: '/admin/internal-dashboard-management', label: 'Internal Dashboard Management', icon: 'dashboard.svg' }, + { legacyHref: '/external-dashboard-management', href: '/admin/external-dashboard-management', label: 'External Dashboard Management', icon: 'dashboard.svg', aliasPrefix: '/admin/role-ui-configs' }, + { legacyHref: '/approval', href: '/admin/approval', label: 'Approval Management', icon: 'approval.svg' }, + { legacyHref: '/users', href: '/admin/users', label: 'External User Management', icon: 'users.svg' }, + { legacyHref: '/customer', href: '/admin/customer', label: 'Customer Management', icon: 'users.svg' }, + { legacyHref: '/company', href: '/admin/company', label: 'Company Management', icon: 'company.svg' }, + { legacyHref: '/candidate', href: '/admin/candidate', label: 'Candidate Management', icon: 'candidate.svg' }, + { legacyHref: '/photographer', href: '/admin/photographer', label: 'Photographer Management', icon: 'photographer.svg' }, + { legacyHref: '/video-editors', href: '/admin/video-editors', label: 'Video Editor Management', icon: 'developers.svg' }, + { legacyHref: '/graphic-designers', href: '/admin/graphic-designers', label: 'Graphic Designer Management', icon: 'developers.svg' }, + { legacyHref: '/social-media-managers', href: '/admin/social-media-managers', label: 'Social Media Manager Management', icon: 'developers.svg' }, + { legacyHref: '/fitness-trainers', href: '/admin/fitness-trainers', label: 'Fitness Trainer Management', icon: 'tutor.svg' }, + { legacyHref: '/catering-services', href: '/admin/catering-services', label: 'Catering Services Management', icon: 'company.svg' }, + { legacyHref: '/makeup-artist', href: '/admin/makeup-artist', label: 'Makeup Artist Management', icon: 'makeup-artist.svg' }, + { legacyHref: '/tutors', href: '/admin/tutors', label: 'Tutor Management', icon: 'tutor.svg' }, + { legacyHref: '/developers', href: '/admin/developers', label: 'Developer Management', icon: 'developers.svg' }, + { legacyHref: '/jobs', href: '/admin/jobs', label: 'Jobs Management', icon: 'jobs.svg' }, + { legacyHref: '/leads', href: '/admin/leads', label: 'Leads Management', icon: 'leads.svg' }, + { legacyHref: '/pricing', href: '/admin/pricing', label: 'Pricing Management', icon: 'pricing.svg' }, + { legacyHref: '/credit', href: '/admin/credit', label: 'Credit Management', icon: 'credits.svg' }, + { legacyHref: '/coupon', href: '/admin/coupon', label: 'Coupon Management', icon: 'coupon.svg' }, + { legacyHref: '/discount', href: '/admin/discount', label: 'Discount Management', icon: 'discount.svg' }, + { legacyHref: '/tax', href: '/admin/tax', label: 'Tax Management', icon: 'tax.svg' }, + { legacyHref: '/order', href: '/admin/order', label: 'Order Management', icon: 'order.svg' }, + { legacyHref: '/invoice', href: '/admin/invoice', label: 'Invoice Management', icon: 'invoice.svg' }, + { legacyHref: '/review', href: '/admin/review', label: 'Review Management', icon: 'reviews.svg' }, + { legacyHref: '/kb', href: '/admin/kb', label: 'Knowledge Base Management', icon: 'reviews.svg' }, + { legacyHref: '/notifications', href: '/admin/notifications', label: 'Notifications', icon: 'reviews.svg' }, + { legacyHref: '/help', href: '/admin/support', label: 'Support Management', icon: 'support.svg' }, + { legacyHref: '/report', href: '/admin/report', label: 'Report Management', icon: 'report.svg' }, + { legacyHref: '/ledger', href: '/admin/ledger', label: 'Ledger Management', icon: 'ledger.svg' }, ]; export default function AdminSidebar() { const location = useLocation(); + const isLinkActive = (href: string, aliasPrefix?: string) => { + const pathOnly = href.split('?')[0] || href; + if (pathOnly === '/admin') return location.pathname === '/admin'; + if (aliasPrefix && location.pathname.startsWith(aliasPrefix)) return true; + return location.pathname === pathOnly || location.pathname.startsWith(`${pathOnly}/`); + }; return ( ); } diff --git a/src/lib/admin-session.ts b/src/lib/admin-session.ts new file mode 100644 index 0000000..404f1eb --- /dev/null +++ b/src/lib/admin-session.ts @@ -0,0 +1,17 @@ +const SESSION_COOKIE = 'nxtgauge_admin_session'; +const SESSION_TTL_SECONDS = 60 * 60 * 12; + +export function hasAdminSession(): boolean { + if (typeof document === 'undefined') return false; + return document.cookie.split(';').some((entry) => entry.trim().startsWith(`${SESSION_COOKIE}=`)); +} + +export function setAdminSession(): void { + if (typeof document === 'undefined') return; + document.cookie = `${SESSION_COOKIE}=1; Path=/; Max-Age=${SESSION_TTL_SECONDS}; SameSite=Lax`; +} + +export function clearAdminSession(): void { + if (typeof document === 'undefined') return; + document.cookie = `${SESSION_COOKIE}=; Path=/; Max-Age=0; SameSite=Lax`; +} diff --git a/src/routes/admin/[...module].tsx b/src/routes/admin/[...module].tsx new file mode 100644 index 0000000..73e8909 --- /dev/null +++ b/src/routes/admin/[...module].tsx @@ -0,0 +1,57 @@ +import { A, useParams } from '@solidjs/router'; +import { createMemo } from 'solid-js'; +import AdminShell from '~/components/AdminShell'; + +function toTitle(value: string): string { + return value + .split(/[-_/]/g) + .filter(Boolean) + .map((part) => part.charAt(0).toUpperCase() + part.slice(1)) + .join(' '); +} + +const LEGACY_ADMIN_ORIGIN = 'http://localhost:3002'; + +function resolveLegacyPath(modulePath: string): string { + switch (modulePath) { + case 'roles': + return '/roles?scope=internal'; + case 'onboarding-management': + return '/onboarding-management'; + case 'internal-dashboard-management': + return '/internal-dashboard-management'; + case 'external-dashboard-management': + return '/external-dashboard-management'; + case 'support': + return '/help'; + default: + return `/${modulePath}`; + } +} + +export default function LegacyModuleShellPage() { + const params = useParams(); + const modulePath = String((params as any).module || '').trim(); + const moduleName = createMemo(() => toTitle(modulePath || 'Management')); + const legacyPath = createMemo(() => resolveLegacyPath(modulePath)); + const legacyUrl = createMemo(() => `${LEGACY_ADMIN_ORIGIN}${legacyPath()}`); + + return ( + +

{moduleName()}

+

+ Live legacy module embedded for exact design and functionality parity during migration. +

+
+ +