From ad363c3d178d5f695b44ee7b5b3ac6dcbc29b25c Mon Sep 17 00:00:00 2001 From: Tara Kaviani Date: Thu, 22 Jan 2026 22:12:40 -0800 Subject: [PATCH 1/3] sidebar feature: add role based navigation of side bar and skeleton pages --- app/(main)/components/sidebar.tsx | 83 ++++++++++++++++++++++++++++ app/(main)/hq/page.tsx | 8 +++ app/(main)/incoming-tickets/page.tsx | 8 +++ app/(main)/manage/page.tsx | 8 +++ app/(main)/request/page.tsx | 8 +++ app/(main)/team/page.tsx | 8 +++ 6 files changed, 123 insertions(+) create mode 100644 app/(main)/components/sidebar.tsx create mode 100644 app/(main)/hq/page.tsx create mode 100644 app/(main)/incoming-tickets/page.tsx create mode 100644 app/(main)/manage/page.tsx create mode 100644 app/(main)/request/page.tsx create mode 100644 app/(main)/team/page.tsx diff --git a/app/(main)/components/sidebar.tsx b/app/(main)/components/sidebar.tsx new file mode 100644 index 0000000..f47c24e --- /dev/null +++ b/app/(main)/components/sidebar.tsx @@ -0,0 +1,83 @@ +import Link from 'next/link'; +import { createClient } from '@/app/lib/supabase/server-client'; + +type Role = 'DEFAULT' | 'REQUESTOR' | 'ADMIN' | 'SUPERADMIN' | 'OWNER'; + +type SidebarLink = { + label: string; + href: string; + allowedRoles: Role[]; +}; + +export default async function Sidebar() { + const supabase = await createClient(); + + // Get user claims (JWT), uses user_role claim, (reference: auth test component) + const { data } = await supabase.auth.getClaims(); + const role = data?.claims?.user_role as Role | undefined; + + // Not logged in, no sidebar shows + if (!role) return null; + + // the only routes from sitemap: /home, /profile, /request, /outgoing-tickets, /manage, /incoming-tickets, /team, /hq + const links: SidebarLink[] = [ + { + label: 'Home', + href: '/home', + allowedRoles: ['DEFAULT', 'REQUESTOR', 'ADMIN', 'SUPERADMIN', 'OWNER'], + }, + { + label: 'Profile', + href: '/profile', + allowedRoles: ['REQUESTOR', 'ADMIN', 'SUPERADMIN', 'OWNER'], + }, + { + label: 'Request', + href: '/request', + allowedRoles: ['REQUESTOR', 'ADMIN', 'SUPERADMIN', 'OWNER'], + }, + { + label: 'Outgoing Tickets', + href: '/outgoing-tickets', + allowedRoles: ['REQUESTOR', 'ADMIN', 'SUPERADMIN', 'OWNER'], + }, + { + label: 'Manage', + href: '/manage', + allowedRoles: ['ADMIN', 'SUPERADMIN', 'OWNER'], + }, + { + label: 'Incoming Tickets', + href: '/incoming-tickets', + allowedRoles: ['ADMIN', 'SUPERADMIN', 'OWNER'], + }, + { + label: 'Team', + href: '/team', + allowedRoles: ['REQUESTOR', 'ADMIN', 'SUPERADMIN', 'OWNER'], + }, + { + label: 'HQ', + href: '/hq', + allowedRoles: ['SUPERADMIN', 'OWNER'], + }, + ]; + + return ( + + ); +} diff --git a/app/(main)/hq/page.tsx b/app/(main)/hq/page.tsx new file mode 100644 index 0000000..496af6e --- /dev/null +++ b/app/(main)/hq/page.tsx @@ -0,0 +1,8 @@ +export default function HQPage() { + return ( +
+

HQ

+

This page is accessible to SUPERADMIN and OWNER users.

+
+ ); +} diff --git a/app/(main)/incoming-tickets/page.tsx b/app/(main)/incoming-tickets/page.tsx new file mode 100644 index 0000000..1bc80b0 --- /dev/null +++ b/app/(main)/incoming-tickets/page.tsx @@ -0,0 +1,8 @@ +export default function IncomingTicketsPage() { + return ( +
+

Incoming Tickets

+

Manage incoming requests, to be completed.

+
+ ); +} diff --git a/app/(main)/manage/page.tsx b/app/(main)/manage/page.tsx new file mode 100644 index 0000000..a55525e --- /dev/null +++ b/app/(main)/manage/page.tsx @@ -0,0 +1,8 @@ +export default function ManagePage() { + return ( +
+

Manage

+

Manage page, to be completed.

+
+ ); +} diff --git a/app/(main)/request/page.tsx b/app/(main)/request/page.tsx new file mode 100644 index 0000000..5fcb02b --- /dev/null +++ b/app/(main)/request/page.tsx @@ -0,0 +1,8 @@ +export default function RequestPage() { + return ( +
+

Request

+

Request page, to be completed.

+
+ ); +} diff --git a/app/(main)/team/page.tsx b/app/(main)/team/page.tsx new file mode 100644 index 0000000..cf6045a --- /dev/null +++ b/app/(main)/team/page.tsx @@ -0,0 +1,8 @@ +export default function TeamPage() { + return ( +
+

Team

+

Team page, to be completed.

+
+ ); +} From 528d2d55a9576d590a048ba350357543e1f1b247 Mon Sep 17 00:00:00 2001 From: Tara Kaviani Date: Thu, 22 Jan 2026 22:16:09 -0800 Subject: [PATCH 2/3] layout file which exports server component main layout & displays sidebar component/children --- app/(main)/layout.tsx | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 app/(main)/layout.tsx diff --git a/app/(main)/layout.tsx b/app/(main)/layout.tsx new file mode 100644 index 0000000..9c3befb --- /dev/null +++ b/app/(main)/layout.tsx @@ -0,0 +1,20 @@ +import type { ReactNode } from 'react'; +import Sidebar from './components/sidebar'; + +export default function MainLayout({ + children, +}: { + children: ReactNode; +}) { + return ( +
+ {/* Sidebar */} + + + {/* Main page content */} +
+ {children} +
+
+ ); +} From bb30b578015e3ba215ce3ca6f857a3ec1e4c04e0 Mon Sep 17 00:00:00 2001 From: Tara Kaviani Date: Thu, 22 Jan 2026 23:30:07 -0800 Subject: [PATCH 3/3] change roles to lowercase to sync w backend --- app/(main)/components/sidebar.tsx | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/app/(main)/components/sidebar.tsx b/app/(main)/components/sidebar.tsx index f47c24e..22443f8 100644 --- a/app/(main)/components/sidebar.tsx +++ b/app/(main)/components/sidebar.tsx @@ -1,7 +1,7 @@ import Link from 'next/link'; import { createClient } from '@/app/lib/supabase/server-client'; -type Role = 'DEFAULT' | 'REQUESTOR' | 'ADMIN' | 'SUPERADMIN' | 'OWNER'; +type Role = 'default' | 'requestor' | 'admin' | 'superadmin' | 'owner'; type SidebarLink = { label: string; @@ -12,54 +12,53 @@ type SidebarLink = { export default async function Sidebar() { const supabase = await createClient(); - // Get user claims (JWT), uses user_role claim, (reference: auth test component) + // Get user claims (JWT) const { data } = await supabase.auth.getClaims(); const role = data?.claims?.user_role as Role | undefined; - // Not logged in, no sidebar shows + // Not logged in → no sidebar if (!role) return null; - // the only routes from sitemap: /home, /profile, /request, /outgoing-tickets, /manage, /incoming-tickets, /team, /hq const links: SidebarLink[] = [ { label: 'Home', href: '/home', - allowedRoles: ['DEFAULT', 'REQUESTOR', 'ADMIN', 'SUPERADMIN', 'OWNER'], + allowedRoles: ['default', 'requestor', 'admin', 'superadmin', 'owner'], }, { label: 'Profile', href: '/profile', - allowedRoles: ['REQUESTOR', 'ADMIN', 'SUPERADMIN', 'OWNER'], + allowedRoles: ['requestor', 'admin', 'superadmin', 'owner'], }, { label: 'Request', href: '/request', - allowedRoles: ['REQUESTOR', 'ADMIN', 'SUPERADMIN', 'OWNER'], + allowedRoles: ['requestor', 'admin', 'superadmin', 'owner'], }, { label: 'Outgoing Tickets', href: '/outgoing-tickets', - allowedRoles: ['REQUESTOR', 'ADMIN', 'SUPERADMIN', 'OWNER'], + allowedRoles: ['requestor', 'admin', 'superadmin', 'owner'], }, { label: 'Manage', href: '/manage', - allowedRoles: ['ADMIN', 'SUPERADMIN', 'OWNER'], + allowedRoles: ['admin', 'superadmin', 'owner'], }, { label: 'Incoming Tickets', href: '/incoming-tickets', - allowedRoles: ['ADMIN', 'SUPERADMIN', 'OWNER'], + allowedRoles: ['admin', 'superadmin', 'owner'], }, { label: 'Team', href: '/team', - allowedRoles: ['REQUESTOR', 'ADMIN', 'SUPERADMIN', 'OWNER'], + allowedRoles: ['requestor', 'admin', 'superadmin', 'owner'], }, { label: 'HQ', href: '/hq', - allowedRoles: ['SUPERADMIN', 'OWNER'], + allowedRoles: ['superadmin', 'owner'], }, ]; @@ -68,11 +67,9 @@ export default async function Sidebar() {