diff --git a/src/app.css b/src/app.css index 2a3d9f2..07158ac 100644 --- a/src/app.css +++ b/src/app.css @@ -176,22 +176,31 @@ body { } .admin-header { - position: sticky; + position: fixed; top: 0; + left: 0; + right: 0; z-index: 40; border-bottom: 1px solid #e2e8f0; - background: rgba(255, 255, 255, 0.94); - backdrop-filter: blur(12px); + background: #fff; + box-shadow: 0 1px 2px rgba(15, 23, 42, 0.08); } .admin-header-inner { - width: min(1440px, calc(100% - 36px)); - margin: 0 auto; - min-height: 64px; + width: calc(100% - 48px); + margin: 0 24px; + height: 64px; display: flex; align-items: center; justify-content: space-between; - gap: 14px; + gap: 16px; +} + +.admin-header-left { + display: flex; + align-items: center; + gap: 28px; + min-width: 0; } .admin-brand { @@ -201,30 +210,24 @@ body { } .admin-brand img { - height: 44px; + height: 40px; width: auto; } -.admin-brand-kicker { +.admin-page-heading { 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); + font-size: 16px; + font-weight: 600; + color: #1f2937; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; } .admin-header-actions { display: flex; align-items: center; - gap: 10px; + gap: 8px; } .admin-role-chip { @@ -240,12 +243,84 @@ body { text-transform: uppercase; } +.admin-avatar-btn { + border: 0; + border-radius: 10px; + background: transparent; + display: flex; + align-items: center; + gap: 10px; + padding: 4px 8px; + cursor: pointer; + color: #475569; +} + +.admin-avatar-btn:hover { + background: #f3f4f6; +} + +.admin-avatar { + width: 32px; + height: 32px; + border-radius: 999px; + border: 1px solid #fed7aa; + background: #ffedd5; + color: #c2410c; + font-size: 13px; + font-weight: 700; + display: inline-flex; + align-items: center; + justify-content: center; +} + +.admin-avatar-meta { + display: flex; + flex-direction: column; + align-items: flex-start; + line-height: 1.1; +} + +.admin-avatar-name { + font-size: 12px; + font-weight: 600; + color: #334155; +} + +.admin-avatar-role { + font-size: 10px; + color: #64748b; +} + +.admin-logout-btn { + border: 0; + border-radius: 8px; + background: transparent; + color: #64748b; + padding: 6px; + cursor: pointer; + display: inline-flex; + align-items: center; + justify-content: center; +} + +.admin-logout-btn svg { + width: 18px; + height: 18px; +} + +.admin-logout-btn:hover { + background: #fef2f2; + color: #dc2626; +} + .shell { display: grid; grid-template-columns: 264px 1fr; - height: calc(100vh - 64px); + height: calc(100vh - 80px); overflow: hidden; transition: grid-template-columns 300ms ease; + padding-top: 80px; + background: #f9fafb; } .shell.sidebar-collapsed { @@ -337,10 +412,10 @@ body { text-decoration: none; color: #475569; border: 1px solid transparent; - border-radius: 10px; - padding: 10px 12px; + border-radius: 12px; + padding: 11px 12px; margin-bottom: 2px; - font-size: 13.5px; + font-size: 14px; line-height: 1.35; font-weight: 500; transition: background 180ms ease, border-color 180ms ease, color 180ms ease, box-shadow 180ms ease; @@ -412,11 +487,12 @@ body { min-width: 0; overflow-y: auto; height: 100%; + background: #f9fafb; } .main-inner { - max-width: 1200px; - padding: 20px 24px 32px; + max-width: none; + padding: 24px; } .admin-tab-wrap { @@ -471,6 +547,78 @@ body { color: #64748b; } +.page-hero-card { + margin-bottom: 16px; + border: 1px solid #fed7aa; + border-radius: 16px; + background: #fff; + box-shadow: 0 10px 28px -24px rgba(15, 23, 42, 0.42); + padding: 18px; +} + +.admin-link-tabs { + display: inline-flex; + flex-wrap: wrap; + gap: 8px; + margin-bottom: 14px; +} + +.admin-link-tab { + text-decoration: none; + border: 1px solid #cbd5e1; + border-radius: 10px; + background: #fff; + color: #475569; + padding: 8px 12px; + font-size: 12px; + font-weight: 700; + letter-spacing: 0.01em; + transition: all 150ms ease; +} + +.admin-link-tab:hover { + border-color: #94a3b8; + color: #0f172a; +} + +.admin-link-tab.active { + border-color: #ffc9ac; + background: #fff1e8; + color: #c2410c; +} + +.admin-segmented { + margin: 14px 0 12px; + display: inline-flex; + gap: 6px; + background: #fff; + border: 1px solid #e2e8f0; + border-radius: 10px; + padding: 4px; +} + +.admin-segment { + border: 1px solid transparent; + background: transparent; + color: #475569; + border-radius: 8px; + padding: 7px 12px; + font-size: 12px; + font-weight: 700; + cursor: pointer; +} + +.admin-segment.active { + background: #fff1e8; + border-color: #ffc9ac; + color: #c2410c; +} + +.admin-segment:disabled { + opacity: 0.45; + cursor: not-allowed; +} + .grid { display: grid; grid-template-columns: 1.2fr 1fr; @@ -642,6 +790,10 @@ body { background: #fff7ed; } +.list-table tbody tr.row-selected td { + background: #fff7ed; +} + .align-right { text-align: right !important; } @@ -652,6 +804,42 @@ body { gap: 8px; } +.action-icon-btn { + border: 1px solid #d1d5db; + background: #fff; + color: #334155; + border-radius: 8px; + width: 30px; + height: 30px; + display: inline-flex; + align-items: center; + justify-content: center; + text-decoration: none; + cursor: pointer; + font-size: 13px; +} + +.action-icon-btn:hover { + background: #f8fafc; +} + +.action-icon-btn.danger { + color: #b91c1c; + border-color: #fca5a5; +} + +.admin-pagination { + display: flex; + align-items: center; + justify-content: flex-end; + gap: 10px; + padding: 12px; + border-top: 1px solid #e2e8f0; + background: #fff; + font-size: 12px; + color: #475569; +} + .status-chip { display: inline-flex; align-items: center; @@ -714,6 +902,8 @@ body { @media (max-width: 1000px) { .shell { grid-template-columns: 1fr; + padding-top: 72px; + height: calc(100vh - 72px); } .sidebar { @@ -727,11 +917,23 @@ body { } .admin-header-inner { - min-height: 62px; + height: 56px; + width: calc(100% - 24px); + margin: 0 12px; + gap: 10px; } .admin-brand img { - height: 36px; + height: 32px; + } + + .admin-page-heading { + font-size: 14px; + } + + .admin-role-chip, + .admin-avatar-meta { + display: none; } } diff --git a/src/components/AdminShell.tsx b/src/components/AdminShell.tsx index d1d2f42..3822f48 100644 --- a/src/components/AdminShell.tsx +++ b/src/components/AdminShell.tsx @@ -49,6 +49,29 @@ const TAB_SETS: Array<{ prefixes: string[]; tabs: Tab[] }> = [ }, ]; +const PAGE_TITLES: Array<{ prefix: string; title: string }> = [ + { prefix: '/admin/approval', title: 'Approval Management' }, + { prefix: '/admin/users', title: 'External User Management' }, + { prefix: '/admin/company', title: 'Company Management' }, + { prefix: '/admin/customer', title: 'Customer Management' }, + { prefix: '/admin/candidate', title: 'Candidate Management' }, + { prefix: '/admin/photographer', title: 'Photographer Management' }, + { prefix: '/admin/makeup-artist', title: 'Makeup Artist Management' }, + { prefix: '/admin/tutors', title: 'Tutor Management' }, + { prefix: '/admin/developers', title: 'Developer Management' }, + { prefix: '/admin/jobs', title: 'Jobs Management' }, + { prefix: '/admin/leads', title: 'Leads Management' }, + { prefix: '/admin/pricing', title: 'Pricing Management' }, + { prefix: '/admin/invoice', title: 'Invoice Management' }, + { prefix: '/admin/credit', title: 'Credit Management' }, + { prefix: '/admin/ledger', title: 'Ledger Management' }, + { prefix: '/admin/report', title: 'Report Management' }, + { prefix: '/admin/roles', title: 'Internal Role Management' }, + { prefix: '/admin/runtime-roles', title: 'External Role Management' }, + { prefix: '/admin/onboarding-schemas', title: 'Onboarding Management' }, + { prefix: '/admin', title: 'Dashboard' }, +]; + export default function AdminShell(props: { children: JSX.Element }) { const location = useLocation(); const navigate = useNavigate(); @@ -69,6 +92,14 @@ export default function AdminShell(props: { children: JSX.Element }) { return location.pathname === tab.href || location.pathname.startsWith(`${tab.href}/`); }; + const pageTitle = createMemo(() => { + const path = location.pathname; + for (const item of PAGE_TITLES) { + if (path === item.prefix || path.startsWith(`${item.prefix}/`)) return item.title; + } + return 'Dashboard'; + }); + onMount(() => { if (!hasAdminSession()) { const from = encodeURIComponent(location.pathname + location.search); @@ -87,16 +118,26 @@ export default function AdminShell(props: { children: JSX.Element }) {
- Administration
-
Super Admin
- + +Manage internal employee roles and permissions from one clean list.
@@ -59,6 +58,12 @@ export default function InternalRolesPage() { Create Internal RoleManage canonical external runtime roles, enabled modules, onboarding assignment, and approval gates from one place.
{role.displayName}
@@ -119,13 +129,15 @@ export default function RuntimeRolesPage() {