nxtgauge-admin-solid/src/components/AdminSidebar.tsx
Ashwin Kumar 2d161c4f15 feat(admin-parity): approval management, submission viewer, role builder offline fallback
- Rebuilt approval.tsx: reject with reason prompt, request changes with field
  keys, request more documents flow, RoleTypeBadge per role type, parsed
  requestReason (ONBOARDING_SUBMISSION prefix), management page routing for
  approved items, inline ApprovalDetailPanel with remarks timeline
- Rebuilt approval/[id].tsx: full onboarding submission viewer loading from
  GET /api/admin/approvals/submission/{user_id}?roleKey=X, flattenFields
  recursive flattener, detectKind image/pdf/document/url/text classifier,
  image lightbox, PDF iframe modal, approve/reject per role type routing
- Added src/lib/admin-modules.ts: STATIC_PERMISSIONS fallback (39 modules x4
  actions) for Internal Role Builder when backend is offline
- roles/create.tsx + roles/[id]/edit.tsx: use STATIC_PERMISSIONS when API
  returns empty or fails, builder now works offline
- AdminShell + AdminSidebar: alignment/style fixes from parity review
- ExternalRoleForm: extended fields for external runtime role management

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 00:34:38 +01:00

98 lines
6.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { A, useLocation } from '@solidjs/router';
import { sidebarCollapsed, toggleSidebar } from '~/lib/sidebar-state';
type LinkItem = { legacyHref: string; href: string; label: string; icon: string; aliasPrefix?: string };
const links: LinkItem[] = [
{ legacyHref: '/', href: '/admin', label: 'Dashboard', icon: 'dashboard.svg' },
{ legacyHref: '/roles?scope=internal', href: '/admin/roles', 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-schemas', label: 'External Onboarding Management', icon: 'reviews.svg' },
{ 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: '/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: 'Employee Management', icon: 'users.svg' },
{ legacyHref: '/users', href: '/admin/users', label: 'Users 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: '/customer', href: '/admin/customer', label: 'Customer Management', icon: 'users.svg' },
{ legacyHref: '/photographer', href: '/admin/photographer', label: 'Photographer Management', icon: 'photographer.svg' },
{ legacyHref: '/makeup-artist', href: '/admin/makeup-artist', label: 'Makeup Artist Management', icon: 'makeup-artist.svg' },
{ legacyHref: '/tutors', href: '/admin/tutors', label: 'Tutors Management', icon: 'tutor.svg' },
{ legacyHref: '/developers', href: '/admin/developers', label: 'Developers Management', icon: 'developers.svg' },
{ legacyHref: '/video-editors', href: '/admin/video-editors', label: 'Video Editor 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: '/graphic-designers', href: '/admin/graphic-designers', label: 'Graphics Designer Management', icon: 'developers.svg' },
{ legacyHref: '/social-media-managers', href: '/admin/social-media-managers', label: 'Social Media Manager 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: '/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' },
{ legacyHref: '/kb', href: '/admin/kb', label: 'KB Management', icon: 'reviews.svg' },
{ legacyHref: '/notifications', href: '/admin/notifications', label: 'Notifications', icon: 'reviews.svg' },
];
export default function AdminSidebar() {
const location = useLocation();
const collapsed = sidebarCollapsed;
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 (
<aside class={`sidebar${collapsed() ? ' sidebar-collapsed' : ''}`}>
<div class="sidebar-toggle-row">
<button
type="button"
class="sidebar-toggle-btn"
aria-label={collapsed() ? 'Expand sidebar' : 'Collapse sidebar'}
onClick={toggleSidebar}
>
<span class={`sidebar-chevron${collapsed() ? ' collapsed' : ''}`}></span>
</button>
</div>
<nav class="sidebar-nav">
{links.map((item) => {
const active = isLinkActive(item.href, item.aliasPrefix);
return (
<A
href={item.href}
class={`nav-item ${active ? 'active' : ''}`}
activeClass=""
inactiveClass=""
data-legacy-href={item.legacyHref}
title={collapsed() ? item.label : undefined}
>
<img class="nav-icon" src={`/sidebar-icons/${item.icon}`} alt="" />
{!collapsed() && (
<span class="nav-title" data-text={item.label}>{item.label}</span>
)}
{!collapsed() && active && (
<span class="active-badge">Active</span>
)}
{collapsed() && active && (
<span class="collapsed-dot" />
)}
</A>
);
})}
</nav>
</aside>
);
}