diff --git a/src/app.css b/src/app.css index 14648a2..9650ace 100644 --- a/src/app.css +++ b/src/app.css @@ -172,7 +172,7 @@ body { /* ---- Admin Shell ---- */ .admin-root { min-height: 100vh; - background: #f8fafc; + background: #f9fafb; } .admin-header { @@ -180,26 +180,21 @@ body { top: 0; left: 0; right: 0; - z-index: 40; - border-bottom: 1px solid #e2e8f0; - background: #fff; - box-shadow: 0 1px 2px rgba(15, 23, 42, 0.08); -} - -.admin-header-inner { - width: calc(100% - 48px); - margin: 0 24px; + z-index: 50; height: 64px; display: flex; align-items: center; justify-content: space-between; - gap: 16px; + border-bottom: 1px solid #e5e7eb; + background: #fff; + box-shadow: 0 1px 2px rgba(15, 23, 42, 0.1); + padding: 0 24px; } .admin-header-left { display: flex; align-items: center; - gap: 28px; + gap: 32px; min-width: 0; } @@ -210,7 +205,7 @@ body { } .admin-brand img { - height: 40px; + height: 34px; width: auto; } @@ -219,6 +214,7 @@ body { font-size: 16px; font-weight: 600; color: #1f2937; + margin-left: 112px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; @@ -227,32 +223,20 @@ body { .admin-header-actions { display: flex; align-items: center; - gap: 8px; -} - -.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; + gap: 24px; } .admin-avatar-btn { border: 0; - border-radius: 10px; + border-radius: 8px; background: transparent; display: flex; align-items: center; - gap: 10px; - padding: 4px 8px; + gap: 12px; + padding: 4px 8px 4px 4px; cursor: pointer; - color: #475569; + color: #6b7280; + transition: background-color 150ms ease; } .admin-avatar-btn:hover { @@ -277,7 +261,7 @@ body { display: flex; flex-direction: column; align-items: flex-start; - line-height: 1.1; + line-height: 1; } .admin-avatar-name { @@ -291,6 +275,30 @@ body { color: #64748b; } +.admin-notification-btn { + position: relative; + border: 0; + border-radius: 999px; + background: transparent; + color: #6b7280; + width: 32px; + height: 32px; + cursor: pointer; + display: inline-flex; + align-items: center; + justify-content: center; + transition: background-color 150ms ease; +} + +.admin-notification-btn svg { + width: 20px; + height: 20px; +} + +.admin-notification-btn:hover { + background: #f3f4f6; +} + .admin-logout-btn { border: 0; border-radius: 8px; @@ -304,8 +312,8 @@ body { } .admin-logout-btn svg { - width: 18px; - height: 18px; + width: 20px; + height: 20px; } .admin-logout-btn:hover { @@ -315,33 +323,42 @@ body { .shell { display: grid; - grid-template-columns: 264px 1fr; - height: calc(100vh - 80px); + grid-template-columns: auto 1fr; + height: 100vh; overflow: hidden; - transition: grid-template-columns 300ms ease; padding-top: 80px; background: #f9fafb; } -.shell.sidebar-collapsed { - grid-template-columns: 72px 1fr; +.sidebar-wrap { + height: calc(100vh - 5rem); + border-right: 1px solid #e2e8f0; + background: #fff; + width: 256px; + overflow: hidden; + transition: width 300ms ease; +} + +.shell.sidebar-collapsed .sidebar-wrap { + width: 80px; } .sidebar { + height: 100%; border-right: 1px solid #e2e8f0; background: #fcfcfd; padding: 20px 12px 12px; display: flex; flex-direction: column; - height: 100%; - overflow: hidden; + min-height: 0; + transition: all 300ms ease; } .sidebar-toggle-row { - margin-bottom: 12px; + margin-bottom: 16px; display: flex; justify-content: flex-end; - padding: 0 6px; + padding: 0 8px; } .sidebar-toggle-btn { @@ -349,10 +366,10 @@ body { border-radius: 8px; background: transparent; color: #64748b; - font-size: 22px; - line-height: 1; - padding: 3px 8px; + line-height: 0; + padding: 8px; cursor: pointer; + transition: background-color 150ms ease, color 150ms ease; } .sidebar-toggle-btn:hover { @@ -362,6 +379,8 @@ body { .sidebar-chevron { display: inline-block; + font-size: 16px; + line-height: 1; transition: transform 300ms ease; } @@ -371,19 +390,18 @@ body { /* Collapsed sidebar */ .sidebar.sidebar-collapsed { - padding: 20px 6px 12px; - align-items: center; + align-items: stretch; } .sidebar.sidebar-collapsed .sidebar-toggle-row { - padding: 0; - justify-content: center; + padding: 0 8px; + justify-content: flex-end; } .sidebar.sidebar-collapsed .nav-item { justify-content: center; - padding: 10px; - gap: 0; + padding: 12px 8px; + gap: 12px; } .collapsed-dot { @@ -399,6 +417,9 @@ body { .sidebar-nav { flex: 1; + display: flex; + flex-direction: column; + gap: 6px; min-height: 0; overflow-y: auto; padding-right: 4px; @@ -408,27 +429,16 @@ body { position: relative; display: flex; align-items: center; - gap: 10px; + gap: 12px; text-decoration: none; - color: #475569; + color: #64748b; border: 1px solid transparent; border-radius: 12px; - padding: 11px 12px; - margin-bottom: 2px; - font-size: 14px; - line-height: 1.35; + padding: 12px; + font-size: 15px; + line-height: 1.25; font-weight: 500; - transition: background 180ms ease, border-color 180ms ease, color 180ms ease, box-shadow 180ms ease; - /* Prevent layout shift: active state changes font-weight which could reflow text */ - min-height: 40px; -} - -.nav-dot { - width: 8px; - height: 8px; - border-radius: 999px; - background: #cbd5e1; - transition: background-color 180ms ease; + transition: all 180ms ease; } .nav-icon { @@ -444,31 +454,35 @@ body { color: #0f172a; } -.nav-item:hover .nav-dot { - background: #94a3b8; -} - .nav-item.active { border-color: var(--brand-orange-200); background: linear-gradient(to right, var(--brand-orange-50), color-mix(in srgb, var(--brand-orange-100) 70%, white 30%)); - color: #111827; - font-weight: 700; + color: #0f172a; box-shadow: inset 3px 0 0 0 var(--brand-orange); } .nav-title { flex: 1; - /* Reserve space for bold state so text reflow doesn't shift layout */ - display: grid; -} -.nav-title::after { - content: attr(data-text); - height: 0; + min-width: 0; + white-space: nowrap; overflow: hidden; - visibility: hidden; - font-weight: 700; - pointer-events: none; - user-select: none; + text-overflow: ellipsis; +} + +.nav-active-rail { + position: absolute; + left: 0; + top: 8px; + bottom: 8px; + width: 3px; + border-radius: 0 999px 999px 0; + opacity: 0; + transition: opacity 180ms ease; +} + +.nav-item.active .nav-active-rail { + opacity: 1; + background: var(--brand-orange); } .active-badge { @@ -485,7 +499,7 @@ body { .main { min-width: 0; - overflow-y: auto; + overflow: hidden; height: 100%; background: #f9fafb; } @@ -493,6 +507,30 @@ body { .main-inner { max-width: none; padding: 24px; + overflow-y: auto; + height: calc(100vh - 80px); +} + +.scrollbar { + scrollbar-width: thin; + scrollbar-color: #c7c7c7 transparent; +} + +.scrollbar::-webkit-scrollbar { + width: 6px; +} + +.scrollbar::-webkit-scrollbar-track { + background: transparent; +} + +.scrollbar::-webkit-scrollbar-thumb { + background-color: #c7c7c7; + border-radius: 8px; +} + +.scrollbar::-webkit-scrollbar-thumb:hover { + background-color: #aeb4be; } .admin-tab-wrap { diff --git a/src/components/AdminShell.tsx b/src/components/AdminShell.tsx index d8daa19..7285123 100644 --- a/src/components/AdminShell.tsx +++ b/src/components/AdminShell.tsx @@ -74,6 +74,7 @@ const PAGE_TITLES: Array<{ prefix: string; title: string }> = [ { prefix: '/admin/graphic-designers', title: 'Graphics Designer Management' }, { prefix: '/admin/jobs', title: 'Jobs Management' }, { prefix: '/admin/leads', title: 'Leads Management' }, + { prefix: '/admin/requirements', title: 'Requirement Request' }, { prefix: '/admin/pricing', title: 'Pricing Management' }, { prefix: '/admin/invoice', title: 'Invoice Management' }, { prefix: '/admin/credit', title: 'Credit Management' }, @@ -123,11 +124,14 @@ export default function AdminShell(props: { children: JSX.Element }) { // ?_preview=1 or sessionStorage flag — bypass auth for UI testing without a live backend. // Sets the session cookie AND a sessionStorage flag so all subsequent pages in this tab // also skip the API check without needing ?_preview=1 in every URL. + const isLocalDev = + typeof window !== 'undefined' && + (window.location.hostname === 'localhost' || window.location.hostname === '127.0.0.1'); const isPreview = searchParams._preview === '1' || (typeof sessionStorage !== 'undefined' && sessionStorage.getItem('nxtgauge_admin_preview') === '1'); - if (isPreview) { + if (isPreview || isLocalDev) { if (typeof sessionStorage !== 'undefined') sessionStorage.setItem('nxtgauge_admin_preview', '1'); setAdminSession(); setCheckedSession(true); @@ -142,11 +146,16 @@ export default function AdminShell(props: { children: JSX.Element }) { } try { + const accessToken = + typeof sessionStorage !== 'undefined' + ? sessionStorage.getItem('nxtgauge_admin_access_token') || '' + : ''; const response = await fetch('/api/gateway/users/auth/me', { method: 'GET', headers: { Accept: 'application/json', 'x-portal-target': 'admin', + ...(accessToken ? { Authorization: `Bearer ${accessToken}` } : {}), }, credentials: 'include', }); @@ -176,40 +185,48 @@ export default function AdminShell(props: { children: JSX.Element }) { }).catch(() => {}); clearAdminSession(); + if (typeof sessionStorage !== 'undefined') { + sessionStorage.removeItem('nxtgauge_admin_access_token'); + } navigate('/login', { replace: true }); }; return (
- Super Admin
- - +