added "members" for an event that help the admins to moderate. members must be invited via email.

This commit is contained in:
Codex Agent
2025-11-09 22:24:40 +01:00
parent 082b78cd43
commit 7ec3db9c59
23 changed files with 836 additions and 101 deletions

View File

@@ -15,16 +15,20 @@ export type AuthStatus = 'loading' | 'authenticated' | 'unauthenticated';
export interface TenantProfile {
id: number;
tenant_id: number;
role?: string | null;
name?: string;
slug?: string;
email?: string | null;
event_credits_balance?: number | null;
features?: Record<string, unknown>;
[key: string]: unknown;
}
interface AuthContextValue {
status: AuthStatus;
user: TenantProfile | null;
abilities: string[];
hasAbility: (ability: string) => boolean;
refreshProfile: () => Promise<void>;
logout: (options?: { redirect?: string }) => Promise<void>;
applyToken: (token: string, abilities: string[]) => Promise<void>;
@@ -80,6 +84,7 @@ async function exchangeSessionForToken(): Promise<{ token: string; abilities: st
export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
const [status, setStatus] = React.useState<AuthStatus>('loading');
const [user, setUser] = React.useState<TenantProfile | null>(null);
const [abilities, setAbilities] = React.useState<string[]>([]);
const queryClient = useQueryClient();
const profileQueryKey = React.useMemo(() => ['tenantProfile'], []);
@@ -88,6 +93,7 @@ export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children
invalidateTenantApiCache();
queryClient.removeQueries({ queryKey: profileQueryKey });
setUser(null);
setAbilities([]);
setStatus('unauthenticated');
}, [profileQueryKey, queryClient]);
@@ -127,6 +133,7 @@ export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children
: data.user;
setUser(composed ?? null);
setAbilities(Array.isArray(data?.abilities) ? data.abilities : []);
setStatus('authenticated');
} catch (error) {
console.error('[Auth] Failed to refresh profile', error);
@@ -141,8 +148,9 @@ export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children
}
}, [handleAuthFailure, profileQueryKey, queryClient]);
const applyToken = React.useCallback(async (token: string, abilities: string[]) => {
storePersonalAccessToken(token, abilities);
const applyToken = React.useCallback(async (token: string, tokenAbilities: string[]) => {
storePersonalAccessToken(token, tokenAbilities);
setAbilities(tokenAbilities);
await refreshProfile();
}, [refreshProfile]);
@@ -221,9 +229,11 @@ export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children
}
}, [handleAuthFailure]);
const hasAbility = React.useCallback((ability: string) => abilities.includes(ability), [abilities]);
const value = React.useMemo<AuthContextValue>(
() => ({ status, user, refreshProfile, logout, applyToken }),
[status, user, refreshProfile, logout, applyToken]
() => ({ status, user, abilities, hasAbility, refreshProfile, logout, applyToken }),
[status, user, abilities, hasAbility, refreshProfile, logout, applyToken]
);
return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;