Add admin hooks, components, and tests for statistics, navigation, and access control
- Introduced `useAdminStats`, `useAdminUsers`, and `useAdminOrganizations` hooks for admin data fetching with React Query. - Added `AdminSidebar`, `Breadcrumbs`, and related navigation components for the admin section. - Implemented comprehensive unit and integration tests for admin components. - Created E2E tests for admin access control, navigation, and dashboard functionality. - Updated exports to include new admin components.
This commit is contained in:
92
frontend/src/components/admin/Breadcrumbs.tsx
Normal file
92
frontend/src/components/admin/Breadcrumbs.tsx
Normal file
@@ -0,0 +1,92 @@
|
||||
/**
|
||||
* Admin Breadcrumbs
|
||||
* Displays navigation breadcrumb trail for admin pages
|
||||
*/
|
||||
|
||||
'use client';
|
||||
|
||||
import Link from 'next/link';
|
||||
import { usePathname } from 'next/navigation';
|
||||
import { ChevronRight } from 'lucide-react';
|
||||
|
||||
interface BreadcrumbItem {
|
||||
label: string;
|
||||
href: string;
|
||||
}
|
||||
|
||||
const pathLabels: Record<string, string> = {
|
||||
admin: 'Admin',
|
||||
users: 'Users',
|
||||
organizations: 'Organizations',
|
||||
settings: 'Settings',
|
||||
};
|
||||
|
||||
export function Breadcrumbs() {
|
||||
const pathname = usePathname();
|
||||
|
||||
// Generate breadcrumb items from pathname
|
||||
const generateBreadcrumbs = (): BreadcrumbItem[] => {
|
||||
const segments = pathname.split('/').filter(Boolean);
|
||||
const breadcrumbs: BreadcrumbItem[] = [];
|
||||
|
||||
let currentPath = '';
|
||||
segments.forEach((segment) => {
|
||||
currentPath += `/${segment}`;
|
||||
const label = pathLabels[segment] || segment;
|
||||
breadcrumbs.push({
|
||||
label,
|
||||
href: currentPath,
|
||||
});
|
||||
});
|
||||
|
||||
return breadcrumbs;
|
||||
};
|
||||
|
||||
const breadcrumbs = generateBreadcrumbs();
|
||||
|
||||
if (breadcrumbs.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<nav
|
||||
aria-label="Breadcrumb"
|
||||
className="border-b bg-background px-6 py-3"
|
||||
data-testid="breadcrumbs"
|
||||
>
|
||||
<ol className="flex items-center space-x-2 text-sm">
|
||||
{breadcrumbs.map((breadcrumb, index) => {
|
||||
const isLast = index === breadcrumbs.length - 1;
|
||||
|
||||
return (
|
||||
<li key={breadcrumb.href} className="flex items-center">
|
||||
{index > 0 && (
|
||||
<ChevronRight
|
||||
className="mx-2 h-4 w-4 text-muted-foreground"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
)}
|
||||
{isLast ? (
|
||||
<span
|
||||
className="font-medium text-foreground"
|
||||
aria-current="page"
|
||||
data-testid={`breadcrumb-${breadcrumb.label.toLowerCase()}`}
|
||||
>
|
||||
{breadcrumb.label}
|
||||
</span>
|
||||
) : (
|
||||
<Link
|
||||
href={breadcrumb.href}
|
||||
className="text-muted-foreground hover:text-foreground transition-colors"
|
||||
data-testid={`breadcrumb-${breadcrumb.label.toLowerCase()}`}
|
||||
>
|
||||
{breadcrumb.label}
|
||||
</Link>
|
||||
)}
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ol>
|
||||
</nav>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user