diff --git a/src/app/(admin)/admin/members/invite/page.tsx b/src/app/(admin)/admin/members/invite/page.tsx index 5706a26..e19ed0e 100644 --- a/src/app/(admin)/admin/members/invite/page.tsx +++ b/src/app/(admin)/admin/members/invite/page.tsx @@ -69,6 +69,7 @@ import { Mail, MailX, } from 'lucide-react' +import { useSession } from 'next-auth/react' import { cn } from '@/lib/utils' type Step = 'input' | 'preview' | 'sending' | 'complete' @@ -274,10 +275,10 @@ export default function MemberInvitePage() { const utils = trpc.useUtils() - // Fetch current user to check role - const { data: currentUser } = trpc.user.me.useQuery() - const isSuperAdmin = currentUser?.role === 'SUPER_ADMIN' - const isAdmin = isSuperAdmin || currentUser?.role === 'PROGRAM_ADMIN' + // Use session role directly (from JWT) — no DB query needed, works even with stale user IDs + const { data: session } = useSession() + const isSuperAdmin = session?.user?.role === 'SUPER_ADMIN' + const isAdmin = isSuperAdmin || session?.user?.role === 'PROGRAM_ADMIN' // Compute available roles as a stable list — avoids Radix Select // not re-rendering conditional children when async data loads diff --git a/src/server/routers/user.ts b/src/server/routers/user.ts index c77d37b..2d7286e 100644 --- a/src/server/routers/user.ts +++ b/src/server/routers/user.ts @@ -19,7 +19,7 @@ export const userRouter = router({ * Get current user profile */ me: protectedProcedure.query(async ({ ctx }) => { - return ctx.prisma.user.findUniqueOrThrow({ + const user = await ctx.prisma.user.findUnique({ where: { id: ctx.user.id }, select: { id: true, @@ -41,6 +41,15 @@ export const userRouter = router({ lastLoginAt: true, }, }) + + if (!user) { + throw new TRPCError({ + code: 'UNAUTHORIZED', + message: 'User session is stale. Please log out and log back in.', + }) + } + + return user }), /**