Terminal
This commit is contained in:
@@ -1,94 +0,0 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import type { User } from "@supabase/supabase-js";
|
||||
import { supabase } from "@/integrations/supabase/client";
|
||||
|
||||
// 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;
|
||||
roles: Role[];
|
||||
loading: boolean;
|
||||
}
|
||||
|
||||
export function useAuth(): AuthState {
|
||||
const [user, setUser] = useState<User | null>(null);
|
||||
const [roles, setRoles] = useState<Role[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
let mounted = true;
|
||||
|
||||
const loadRoles = async (uid: string) => {
|
||||
const { data } = await supabase.from("user_roles").select("role").eq("user_id", uid);
|
||||
if (mounted) setRoles((data ?? []).map((r) => r.role as Role));
|
||||
};
|
||||
|
||||
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));
|
||||
else setLoading(false);
|
||||
});
|
||||
|
||||
const { data: sub } = supabase.auth.onAuthStateChange((_e, session) => {
|
||||
setUser(session?.user ?? null);
|
||||
if (session?.user) {
|
||||
setTimeout(() => loadRoles(session.user.id), 0);
|
||||
} else {
|
||||
setRoles([]);
|
||||
}
|
||||
});
|
||||
|
||||
return () => {
|
||||
mounted = false;
|
||||
sub.subscription.unsubscribe();
|
||||
};
|
||||
}, []);
|
||||
|
||||
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 {
|
||||
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");
|
||||
}
|
||||
Reference in New Issue
Block a user