feat: add admin user management page and support authroizeOnly for nav item

- Introduced a new admin section with user management capabilities, including a dedicated AdminUsersPage.
- Updated localization files to support new admin titles in English and Chinese.
- Added routing for the admin users page and integrated it into the sidebar configuration.
- Created necessary layout and loading components for the admin section.
- Implemented a data structure for user management, enhancing the overall admin functionality.
This commit is contained in:
javayhu 2025-04-12 09:54:55 +08:00
parent dd6fb4df85
commit 6e1189afc4
25 changed files with 735 additions and 36 deletions

View File

@ -436,6 +436,12 @@
"dashboard": {
"title": "Dashboard"
},
"admin": {
"title": "Admin",
"users": {
"title": "Users"
}
},
"settings": {
"title": "Settings",
"profile": {

View File

@ -437,6 +437,12 @@
"dashboard": {
"title": "仪表盘"
},
"admin": {
"title": "系统管理",
"users": {
"title": "用户管理"
}
},
"settings": {
"title": "设置",
"profile": {

View File

@ -0,0 +1,46 @@
import { ChartAreaInteractive } from '@/components/dashboard/chart-area-interactive';
import { DashboardHeader } from '@/components/dashboard/dashboard-header';
import { DataTable } from '@/components/dashboard/data-table';
import { SectionCards } from '@/components/dashboard/section-cards';
import { useTranslations } from 'next-intl';
import data from "./data.json";
/**
* Admin users page
*
* NOTICE: This is a demo page for the admin, no real data is used,
* we will show real data in the future
*/
export default function AdminUsersPage() {
const t = useTranslations();
const breadcrumbs = [
{
label: t('Dashboard.admin.title'),
isCurrentPage: false,
},
{
label: t('Dashboard.admin.users.title'),
isCurrentPage: true,
},
];
return (
<>
<DashboardHeader breadcrumbs={breadcrumbs} />
<div className="flex flex-1 flex-col">
<div className="@container/main flex flex-1 flex-col gap-2">
<div className="flex flex-col gap-4 py-4 md:gap-6 md:py-6">
<SectionCards />
<div className="px-4 lg:px-6">
<ChartAreaInteractive />
</div>
<DataTable data={data} />
</div>
</div>
</div>
</>
);
}

View File

@ -0,0 +1,614 @@
[
{
"id": 1,
"header": "Cover page",
"type": "Cover page",
"status": "In Process",
"target": "18",
"limit": "5",
"reviewer": "Eddie Lake"
},
{
"id": 2,
"header": "Table of contents",
"type": "Table of contents",
"status": "Done",
"target": "29",
"limit": "24",
"reviewer": "Eddie Lake"
},
{
"id": 3,
"header": "Executive summary",
"type": "Narrative",
"status": "Done",
"target": "10",
"limit": "13",
"reviewer": "Eddie Lake"
},
{
"id": 4,
"header": "Technical approach",
"type": "Narrative",
"status": "Done",
"target": "27",
"limit": "23",
"reviewer": "Jamik Tashpulatov"
},
{
"id": 5,
"header": "Design",
"type": "Narrative",
"status": "In Process",
"target": "2",
"limit": "16",
"reviewer": "Jamik Tashpulatov"
},
{
"id": 6,
"header": "Capabilities",
"type": "Narrative",
"status": "In Process",
"target": "20",
"limit": "8",
"reviewer": "Jamik Tashpulatov"
},
{
"id": 7,
"header": "Integration with existing systems",
"type": "Narrative",
"status": "In Process",
"target": "19",
"limit": "21",
"reviewer": "Jamik Tashpulatov"
},
{
"id": 8,
"header": "Innovation and Advantages",
"type": "Narrative",
"status": "Done",
"target": "25",
"limit": "26",
"reviewer": "Assign reviewer"
},
{
"id": 9,
"header": "Overview of EMR's Innovative Solutions",
"type": "Technical content",
"status": "Done",
"target": "7",
"limit": "23",
"reviewer": "Assign reviewer"
},
{
"id": 10,
"header": "Advanced Algorithms and Machine Learning",
"type": "Narrative",
"status": "Done",
"target": "30",
"limit": "28",
"reviewer": "Assign reviewer"
},
{
"id": 11,
"header": "Adaptive Communication Protocols",
"type": "Narrative",
"status": "Done",
"target": "9",
"limit": "31",
"reviewer": "Assign reviewer"
},
{
"id": 12,
"header": "Advantages Over Current Technologies",
"type": "Narrative",
"status": "Done",
"target": "12",
"limit": "0",
"reviewer": "Assign reviewer"
},
{
"id": 13,
"header": "Past Performance",
"type": "Narrative",
"status": "Done",
"target": "22",
"limit": "33",
"reviewer": "Assign reviewer"
},
{
"id": 14,
"header": "Customer Feedback and Satisfaction Levels",
"type": "Narrative",
"status": "Done",
"target": "15",
"limit": "34",
"reviewer": "Assign reviewer"
},
{
"id": 15,
"header": "Implementation Challenges and Solutions",
"type": "Narrative",
"status": "Done",
"target": "3",
"limit": "35",
"reviewer": "Assign reviewer"
},
{
"id": 16,
"header": "Security Measures and Data Protection Policies",
"type": "Narrative",
"status": "In Process",
"target": "6",
"limit": "36",
"reviewer": "Assign reviewer"
},
{
"id": 17,
"header": "Scalability and Future Proofing",
"type": "Narrative",
"status": "Done",
"target": "4",
"limit": "37",
"reviewer": "Assign reviewer"
},
{
"id": 18,
"header": "Cost-Benefit Analysis",
"type": "Plain language",
"status": "Done",
"target": "14",
"limit": "38",
"reviewer": "Assign reviewer"
},
{
"id": 19,
"header": "User Training and Onboarding Experience",
"type": "Narrative",
"status": "Done",
"target": "17",
"limit": "39",
"reviewer": "Assign reviewer"
},
{
"id": 20,
"header": "Future Development Roadmap",
"type": "Narrative",
"status": "Done",
"target": "11",
"limit": "40",
"reviewer": "Assign reviewer"
},
{
"id": 21,
"header": "System Architecture Overview",
"type": "Technical content",
"status": "In Process",
"target": "24",
"limit": "18",
"reviewer": "Maya Johnson"
},
{
"id": 22,
"header": "Risk Management Plan",
"type": "Narrative",
"status": "Done",
"target": "15",
"limit": "22",
"reviewer": "Carlos Rodriguez"
},
{
"id": 23,
"header": "Compliance Documentation",
"type": "Legal",
"status": "In Process",
"target": "31",
"limit": "27",
"reviewer": "Sarah Chen"
},
{
"id": 24,
"header": "API Documentation",
"type": "Technical content",
"status": "Done",
"target": "8",
"limit": "12",
"reviewer": "Raj Patel"
},
{
"id": 25,
"header": "User Interface Mockups",
"type": "Visual",
"status": "In Process",
"target": "19",
"limit": "25",
"reviewer": "Leila Ahmadi"
},
{
"id": 26,
"header": "Database Schema",
"type": "Technical content",
"status": "Done",
"target": "22",
"limit": "20",
"reviewer": "Thomas Wilson"
},
{
"id": 27,
"header": "Testing Methodology",
"type": "Technical content",
"status": "In Process",
"target": "17",
"limit": "14",
"reviewer": "Assign reviewer"
},
{
"id": 28,
"header": "Deployment Strategy",
"type": "Narrative",
"status": "Done",
"target": "26",
"limit": "30",
"reviewer": "Eddie Lake"
},
{
"id": 29,
"header": "Budget Breakdown",
"type": "Financial",
"status": "In Process",
"target": "13",
"limit": "16",
"reviewer": "Jamik Tashpulatov"
},
{
"id": 30,
"header": "Market Analysis",
"type": "Research",
"status": "Done",
"target": "29",
"limit": "32",
"reviewer": "Sophia Martinez"
},
{
"id": 31,
"header": "Competitor Comparison",
"type": "Research",
"status": "In Process",
"target": "21",
"limit": "19",
"reviewer": "Assign reviewer"
},
{
"id": 32,
"header": "Maintenance Plan",
"type": "Technical content",
"status": "Done",
"target": "16",
"limit": "23",
"reviewer": "Alex Thompson"
},
{
"id": 33,
"header": "User Personas",
"type": "Research",
"status": "In Process",
"target": "27",
"limit": "24",
"reviewer": "Nina Patel"
},
{
"id": 34,
"header": "Accessibility Compliance",
"type": "Legal",
"status": "Done",
"target": "18",
"limit": "21",
"reviewer": "Assign reviewer"
},
{
"id": 35,
"header": "Performance Metrics",
"type": "Technical content",
"status": "In Process",
"target": "23",
"limit": "26",
"reviewer": "David Kim"
},
{
"id": 36,
"header": "Disaster Recovery Plan",
"type": "Technical content",
"status": "Done",
"target": "14",
"limit": "17",
"reviewer": "Jamik Tashpulatov"
},
{
"id": 37,
"header": "Third-party Integrations",
"type": "Technical content",
"status": "In Process",
"target": "25",
"limit": "28",
"reviewer": "Eddie Lake"
},
{
"id": 38,
"header": "User Feedback Summary",
"type": "Research",
"status": "Done",
"target": "20",
"limit": "15",
"reviewer": "Assign reviewer"
},
{
"id": 39,
"header": "Localization Strategy",
"type": "Narrative",
"status": "In Process",
"target": "12",
"limit": "19",
"reviewer": "Maria Garcia"
},
{
"id": 40,
"header": "Mobile Compatibility",
"type": "Technical content",
"status": "Done",
"target": "28",
"limit": "31",
"reviewer": "James Wilson"
},
{
"id": 41,
"header": "Data Migration Plan",
"type": "Technical content",
"status": "In Process",
"target": "19",
"limit": "22",
"reviewer": "Assign reviewer"
},
{
"id": 42,
"header": "Quality Assurance Protocols",
"type": "Technical content",
"status": "Done",
"target": "30",
"limit": "33",
"reviewer": "Priya Singh"
},
{
"id": 43,
"header": "Stakeholder Analysis",
"type": "Research",
"status": "In Process",
"target": "11",
"limit": "14",
"reviewer": "Eddie Lake"
},
{
"id": 44,
"header": "Environmental Impact Assessment",
"type": "Research",
"status": "Done",
"target": "24",
"limit": "27",
"reviewer": "Assign reviewer"
},
{
"id": 45,
"header": "Intellectual Property Rights",
"type": "Legal",
"status": "In Process",
"target": "17",
"limit": "20",
"reviewer": "Sarah Johnson"
},
{
"id": 46,
"header": "Customer Support Framework",
"type": "Narrative",
"status": "Done",
"target": "22",
"limit": "25",
"reviewer": "Jamik Tashpulatov"
},
{
"id": 47,
"header": "Version Control Strategy",
"type": "Technical content",
"status": "In Process",
"target": "15",
"limit": "18",
"reviewer": "Assign reviewer"
},
{
"id": 48,
"header": "Continuous Integration Pipeline",
"type": "Technical content",
"status": "Done",
"target": "26",
"limit": "29",
"reviewer": "Michael Chen"
},
{
"id": 49,
"header": "Regulatory Compliance",
"type": "Legal",
"status": "In Process",
"target": "13",
"limit": "16",
"reviewer": "Assign reviewer"
},
{
"id": 50,
"header": "User Authentication System",
"type": "Technical content",
"status": "Done",
"target": "28",
"limit": "31",
"reviewer": "Eddie Lake"
},
{
"id": 51,
"header": "Data Analytics Framework",
"type": "Technical content",
"status": "In Process",
"target": "21",
"limit": "24",
"reviewer": "Jamik Tashpulatov"
},
{
"id": 52,
"header": "Cloud Infrastructure",
"type": "Technical content",
"status": "Done",
"target": "16",
"limit": "19",
"reviewer": "Assign reviewer"
},
{
"id": 53,
"header": "Network Security Measures",
"type": "Technical content",
"status": "In Process",
"target": "29",
"limit": "32",
"reviewer": "Lisa Wong"
},
{
"id": 54,
"header": "Project Timeline",
"type": "Planning",
"status": "Done",
"target": "14",
"limit": "17",
"reviewer": "Eddie Lake"
},
{
"id": 55,
"header": "Resource Allocation",
"type": "Planning",
"status": "In Process",
"target": "27",
"limit": "30",
"reviewer": "Assign reviewer"
},
{
"id": 56,
"header": "Team Structure and Roles",
"type": "Planning",
"status": "Done",
"target": "20",
"limit": "23",
"reviewer": "Jamik Tashpulatov"
},
{
"id": 57,
"header": "Communication Protocols",
"type": "Planning",
"status": "In Process",
"target": "15",
"limit": "18",
"reviewer": "Assign reviewer"
},
{
"id": 58,
"header": "Success Metrics",
"type": "Planning",
"status": "Done",
"target": "30",
"limit": "33",
"reviewer": "Eddie Lake"
},
{
"id": 59,
"header": "Internationalization Support",
"type": "Technical content",
"status": "In Process",
"target": "23",
"limit": "26",
"reviewer": "Jamik Tashpulatov"
},
{
"id": 60,
"header": "Backup and Recovery Procedures",
"type": "Technical content",
"status": "Done",
"target": "18",
"limit": "21",
"reviewer": "Assign reviewer"
},
{
"id": 61,
"header": "Monitoring and Alerting System",
"type": "Technical content",
"status": "In Process",
"target": "25",
"limit": "28",
"reviewer": "Daniel Park"
},
{
"id": 62,
"header": "Code Review Guidelines",
"type": "Technical content",
"status": "Done",
"target": "12",
"limit": "15",
"reviewer": "Eddie Lake"
},
{
"id": 63,
"header": "Documentation Standards",
"type": "Technical content",
"status": "In Process",
"target": "27",
"limit": "30",
"reviewer": "Jamik Tashpulatov"
},
{
"id": 64,
"header": "Release Management Process",
"type": "Planning",
"status": "Done",
"target": "22",
"limit": "25",
"reviewer": "Assign reviewer"
},
{
"id": 65,
"header": "Feature Prioritization Matrix",
"type": "Planning",
"status": "In Process",
"target": "19",
"limit": "22",
"reviewer": "Emma Davis"
},
{
"id": 66,
"header": "Technical Debt Assessment",
"type": "Technical content",
"status": "Done",
"target": "24",
"limit": "27",
"reviewer": "Eddie Lake"
},
{
"id": 67,
"header": "Capacity Planning",
"type": "Planning",
"status": "In Process",
"target": "21",
"limit": "24",
"reviewer": "Jamik Tashpulatov"
},
{
"id": 68,
"header": "Service Level Agreements",
"type": "Legal",
"status": "Done",
"target": "26",
"limit": "29",
"reviewer": "Assign reviewer"
}
]

View File

@ -9,7 +9,8 @@ import data from "./data.json";
/**
* Dashboard page
*
* This is a demo page for the dashboard, no real data is used
* NOTICE: This is a demo page for the dashboard, no real data is used,
* we will show real data in the future
*/
export default function DashboardPage() {
const t = useTranslations();

View File

@ -29,6 +29,12 @@ export function DashboardSidebar({ ...props }: React.ComponentProps<typeof Sideb
const { data: session, isPending } = authClient.useSession();
const currentUser = session?.user;
// console.log('sidebar currentUser:', currentUser);
const filteredSidebarLinks = sidebarLinks.filter((link) => {
if (link.authorizeOnly) {
return link.authorizeOnly.includes(currentUser?.role || '');
}
return true;
});
return (
<Sidebar collapsible="icon" {...props}>
@ -51,7 +57,7 @@ export function DashboardSidebar({ ...props }: React.ComponentProps<typeof Sideb
</SidebarHeader>
<SidebarContent>
<SidebarMain items={sidebarLinks} />
<SidebarMain items={filteredSidebarLinks} />
</SidebarContent>
<SidebarFooter className="flex flex-col gap-4">

View File

@ -69,28 +69,28 @@ export function getNavbarConfig(): NestedMenuItem[] {
{
title: t('ai.items.text.title'),
description: t('ai.items.text.description'),
icon: <SquarePenIcon className="site-4 shrink-0" />,
icon: <SquarePenIcon className="size-4 shrink-0" />,
href: Routes.AIText,
external: false,
},
{
title: t('ai.items.image.title'),
description: t('ai.items.image.description'),
icon: <ImageIcon className="site-4 shrink-0" />,
icon: <ImageIcon className="size-4 shrink-0" />,
href: Routes.AIImage,
external: false,
},
{
title: t('ai.items.video.title'),
description: t('ai.items.video.description'),
icon: <FilmIcon className="site-4 shrink-0" />,
icon: <FilmIcon className="size-4 shrink-0" />,
href: Routes.AIVideo,
external: false,
},
{
title: t('ai.items.audio.title'),
description: t('ai.items.audio.description'),
icon: <AudioLinesIcon className="site-4 shrink-0" />,
icon: <AudioLinesIcon className="size-4 shrink-0" />,
href: Routes.AIAudio,
external: false,
},
@ -102,56 +102,56 @@ export function getNavbarConfig(): NestedMenuItem[] {
{
title: t('pages.items.about.title'),
description: t('pages.items.about.description'),
icon: <BuildingIcon className="site-4 shrink-0" />,
icon: <BuildingIcon className="size-4 shrink-0" />,
href: Routes.About,
external: false,
},
{
title: t('pages.items.contact.title'),
description: t('pages.items.contact.description'),
icon: <MailIcon className="site-4 shrink-0" />,
icon: <MailIcon className="size-4 shrink-0" />,
href: Routes.Contact,
external: false,
},
{
title: t('pages.items.waitlist.title'),
description: t('pages.items.waitlist.description'),
icon: <MailboxIcon className="site-4 shrink-0" />,
icon: <MailboxIcon className="size-4 shrink-0" />,
href: Routes.Waitlist,
external: false,
},
{
title: t('pages.items.roadmap.title'),
description: t('pages.items.roadmap.description'),
icon: <SquareKanbanIcon className="site-4 shrink-0" />,
icon: <SquareKanbanIcon className="size-4 shrink-0" />,
href: Routes.Roadmap,
external: true,
},
{
title: t('pages.items.changelog.title'),
description: t('pages.items.changelog.description'),
icon: <ListChecksIcon className="site-4 shrink-0" />,
icon: <ListChecksIcon className="size-4 shrink-0" />,
href: Routes.Changelog,
external: false,
},
{
title: t('pages.items.cookiePolicy.title'),
description: t('pages.items.cookiePolicy.description'),
icon: <CookieIcon className="site-4 shrink-0" />,
icon: <CookieIcon className="size-4 shrink-0" />,
href: Routes.CookiePolicy,
external: false,
},
{
title: t('pages.items.privacyPolicy.title'),
description: t('pages.items.privacyPolicy.description'),
icon: <ShieldCheckIcon className="site-4 shrink-0" />,
icon: <ShieldCheckIcon className="size-4 shrink-0" />,
href: Routes.PrivacyPolicy,
external: false,
},
{
title: t('pages.items.termsOfService.title'),
description: t('pages.items.termsOfService.description'),
icon: <FileTextIcon className="site-4 shrink-0" />,
icon: <FileTextIcon className="size-4 shrink-0" />,
href: Routes.TermsOfService,
external: false,
},
@ -162,91 +162,91 @@ export function getNavbarConfig(): NestedMenuItem[] {
items: [
{
title: t('blocks.items.hero.title'),
icon: <FlameIcon className="site-4 shrink-0" />,
icon: <FlameIcon className="size-4 shrink-0" />,
href: Routes.HeroBlocks,
external: false,
},
{
title: t('blocks.items.logo.title'),
icon: <SquareCodeIcon className="site-4 shrink-0" />,
icon: <SquareCodeIcon className="size-4 shrink-0" />,
href: Routes.LogoBlocks,
external: false,
},
{
title: t('blocks.items.features.title'),
icon: <WandSparklesIcon className="site-4 shrink-0" />,
icon: <WandSparklesIcon className="size-4 shrink-0" />,
href: Routes.FeaturesBlocks,
external: false,
},
{
title: t('blocks.items.content.title'),
icon: <NewspaperIcon className="site-4 shrink-0" />,
icon: <NewspaperIcon className="size-4 shrink-0" />,
href: Routes.ContentBlocks,
external: false,
},
{
title: t('blocks.items.stats.title'),
icon: <ChartNoAxesCombinedIcon className="site-4 shrink-0" />,
icon: <ChartNoAxesCombinedIcon className="size-4 shrink-0" />,
href: Routes.StatsBlocks,
external: false,
},
{
title: t('blocks.items.team.title'),
icon: <UsersIcon className="site-4 shrink-0" />,
icon: <UsersIcon className="size-4 shrink-0" />,
href: Routes.TeamBlocks,
external: false,
},
{
title: t('blocks.items.testimonials.title'),
icon: <ThumbsUpIcon className="site-4 shrink-0" />,
icon: <ThumbsUpIcon className="size-4 shrink-0" />,
href: Routes.TestimonialsBlocks,
external: false,
},
{
title: t('blocks.items.callToAction.title'),
icon: <RocketIcon className="site-4 shrink-0" />,
icon: <RocketIcon className="size-4 shrink-0" />,
href: Routes.CallToActionBlocks,
external: false,
},
{
title: t('blocks.items.footer.title'),
icon: <FootprintsIcon className="site-4 shrink-0" />,
icon: <FootprintsIcon className="size-4 shrink-0" />,
href: Routes.FooterBlocks,
external: false,
},
{
title: t('blocks.items.pricing.title'),
icon: <CircleDollarSignIcon className="site-4 shrink-0" />,
icon: <CircleDollarSignIcon className="size-4 shrink-0" />,
href: Routes.PricingBlocks,
external: false,
},
{
title: t('blocks.items.comparator.title'),
icon: <SplitSquareVerticalIcon className="site-4 shrink-0" />,
icon: <SplitSquareVerticalIcon className="size-4 shrink-0" />,
href: Routes.ComparatorBlocks,
external: false,
},
{
title: t('blocks.items.faq.title'),
icon: <CircleHelpIcon className="site-4 shrink-0" />,
icon: <CircleHelpIcon className="size-4 shrink-0" />,
href: Routes.FAQBlocks,
external: false,
},
{
title: t('blocks.items.login.title'),
icon: <LogInIcon className="site-4 shrink-0" />,
icon: <LogInIcon className="size-4 shrink-0" />,
href: Routes.LoginBlocks,
external: false,
},
{
title: t('blocks.items.signup.title'),
icon: <UserPlusIcon className="site-4 shrink-0" />,
icon: <UserPlusIcon className="size-4 shrink-0" />,
href: Routes.SignupBlocks,
external: false,
},
{
title: t('blocks.items.contact.title'),
icon: <MailIcon className="site-4 shrink-0" />,
icon: <MailIcon className="size-4 shrink-0" />,
href: Routes.ContactBlocks,
external: false,
},

View File

@ -8,7 +8,10 @@ import {
CreditCardIcon,
LayoutDashboardIcon,
LockKeyholeIcon,
Settings2Icon
Settings2Icon,
SettingsIcon,
UsersIcon,
UsersRoundIcon
} from 'lucide-react';
import { useTranslations } from 'next-intl';
@ -25,35 +28,48 @@ export function getSidebarConfig(): NestedMenuItem[] {
return [
{
title: t('dashboard.title'),
icon: <LayoutDashboardIcon className="site-4 shrink-0" />,
icon: <LayoutDashboardIcon className="size-4 shrink-0" />,
href: Routes.Dashboard,
external: false,
},
{
title: t('admin.title'),
icon: <SettingsIcon className="size-4 shrink-0" />,
authorizeOnly: ['admin'],
items: [
{
title: t('admin.users.title'),
icon: <UsersRoundIcon className="size-4 shrink-0" />,
href: Routes.AdminUsers,
external: false,
}
],
},
{
title: t('settings.title'),
icon: <Settings2Icon className="site-4 shrink-0" />,
icon: <Settings2Icon className="size-4 shrink-0" />,
items: [
{
title: t('settings.profile.title'),
icon: <CircleUserRoundIcon className="site-4 shrink-0" />,
icon: <CircleUserRoundIcon className="size-4 shrink-0" />,
href: Routes.SettingsProfile,
external: false,
},
{
title: t('settings.billing.title'),
icon: <CreditCardIcon className="site-4 shrink-0" />,
icon: <CreditCardIcon className="size-4 shrink-0" />,
href: Routes.SettingsBilling,
external: false,
},
{
title: t('settings.security.title'),
icon: <LockKeyholeIcon className="site-4 shrink-0" />,
icon: <LockKeyholeIcon className="size-4 shrink-0" />,
href: Routes.SettingsSecurity,
external: false,
},
{
title: t('settings.notification.title'),
icon: <BellIcon className="site-4 shrink-0" />,
icon: <BellIcon className="size-4 shrink-0" />,
href: Routes.SettingsNotifications,
external: false,
}

View File

@ -30,6 +30,7 @@ export enum Routes {
// dashboard routes
Dashboard = '/dashboard',
AdminUsers = '/admin/users',
SettingsProfile = '/settings/profile',
SettingsBilling = '/settings/billing',
SettingsSecurity = '/settings/security',

View File

@ -42,6 +42,7 @@ export type WebsiteConfig = {
* icon?: the icon to display
* href?: the url to link to
* external?: whether the link is external
* authorizeOnly?: the roles that are authorized to see the item
*/
export type MenuItem = {
title: string;
@ -49,6 +50,7 @@ export type MenuItem = {
icon?: ReactNode;
href?: string;
external?: boolean;
authorizeOnly?: string[];
};
/**
@ -60,5 +62,6 @@ export type NestedMenuItem = {
icon?: ReactNode;
href?: string;
external?: boolean;
authorizeOnly?: string[];
items?: MenuItem[];
};