From 521212afde3505cf976331f682ae6865f2a70ecc Mon Sep 17 00:00:00 2001 From: admin Date: Fri, 7 Aug 2026 05:33:59 +0000 Subject: [PATCH] Modified by www.SourceFiles.app --- src/hooks/use-auth.ts | 51 +++++++++++++++++++++++++++++++++++++------ 1 file changed, 44 insertions(+), 7 deletions(-) diff --git a/src/hooks/use-auth.ts b/src/hooks/use-auth.ts index 3b220c0..9afd271 100644 --- a/src/hooks/use-auth.ts +++ b/src/hooks/use-auth.ts @@ -2,7 +2,20 @@ import { useEffect, useState } from "react"; import type { User } from "@supabase/supabase-js"; import { supabase } from "@/integrations/supabase/client"; -export type Role = "admin" | "teacher" | "parent" | "student"; +// Mirrors the public.app_role enum. "student" has no enum member but is kept +// because existing screens branch on it. +export type Role = + | "super_admin" + | "org_admin" + | "admin" + | "campus_admin" + | "management" + | "billing_admin" + | "teacher" + | "staff" + | "parent" + | "auditor" + | "student"; export interface AuthState { user: User | null; @@ -26,7 +39,8 @@ export function useAuth(): AuthState { supabase.auth.getSession().then(({ data }) => { if (!mounted) return; setUser(data.session?.user ?? null); - if (data.session?.user) loadRoles(data.session.user.id).finally(() => mounted && setLoading(false)); + if (data.session?.user) + loadRoles(data.session.user.id).finally(() => mounted && setLoading(false)); else setLoading(false); }); @@ -48,10 +62,33 @@ export function useAuth(): AuthState { return { user, roles, loading }; } +const ROLE_SENIORITY: Role[] = [ + "super_admin", + "org_admin", + "admin", + "campus_admin", + "management", + "billing_admin", + "teacher", + "staff", + "parent", + "auditor", + "student", +]; + export function highestRole(roles: Role[]): Role | null { - if (roles.includes("admin")) return "admin"; - if (roles.includes("teacher")) return "teacher"; - if (roles.includes("parent")) return "parent"; - if (roles.includes("student")) return "student"; - return null; + return ROLE_SENIORITY.find((r) => roles.includes(r)) ?? null; +} + +/** + * Organization-wide administrative reach. Mirrors public.is_org_admin() — + * keep the two in step, or the UI will offer actions that RLS then refuses. + */ +export function isOrgAdmin(roles: Role[]): boolean { + return roles.some((r) => r === "admin" || r === "org_admin" || r === "super_admin"); +} + +/** Mirrors public.is_billing_admin(). */ +export function canBill(roles: Role[]): boolean { + return isOrgAdmin(roles) || roles.includes("billing_admin"); }