99 lines
2.8 KiB
TypeScript
99 lines
2.8 KiB
TypeScript
import React, { createContext, useContext, useMemo, useCallback, ReactNode } from 'react';
|
|
import { useQuery, useQueryClient } from '@tanstack/react-query';
|
|
import { useAuth } from './AuthContext';
|
|
import { permissionsApi } from '../services/permissions';
|
|
|
|
interface PermissionContextType {
|
|
permissions: Set<string>;
|
|
maintenance: Record<string, boolean>;
|
|
isAdmin: boolean;
|
|
isLoading: boolean;
|
|
hasPermission: (perm: string) => boolean;
|
|
hasAnyPermission: (...perms: string[]) => boolean;
|
|
isFeatureEnabled: (featureGroup: string) => boolean;
|
|
refetch: () => void;
|
|
}
|
|
|
|
const PermissionContext = createContext<PermissionContextType | undefined>(undefined);
|
|
|
|
interface PermissionProviderProps {
|
|
children: ReactNode;
|
|
}
|
|
|
|
export const PermissionProvider: React.FC<PermissionProviderProps> = ({ children }) => {
|
|
const { isAuthenticated } = useAuth();
|
|
const queryClient = useQueryClient();
|
|
|
|
const { data, isLoading } = useQuery({
|
|
queryKey: ['my-permissions'],
|
|
queryFn: permissionsApi.getMyPermissions,
|
|
enabled: isAuthenticated,
|
|
staleTime: 5 * 60 * 1000,
|
|
});
|
|
|
|
const permissions = useMemo(
|
|
() => new Set(data?.permissions ?? []),
|
|
[data?.permissions]
|
|
);
|
|
|
|
const maintenance = data?.maintenance ?? {};
|
|
const isAdmin = data?.isAdmin ?? false;
|
|
|
|
const isFeatureEnabled = useCallback(
|
|
(featureGroup: string): boolean => {
|
|
if (isAdmin) return true;
|
|
return !maintenance[featureGroup];
|
|
},
|
|
[isAdmin, maintenance]
|
|
);
|
|
|
|
const hasPermission = useCallback(
|
|
(perm: string): boolean => {
|
|
if (isAdmin) return true;
|
|
const featureGroup = perm.split(':')[0];
|
|
if (!isFeatureEnabled(featureGroup)) return false;
|
|
return permissions.has(perm);
|
|
},
|
|
[isAdmin, permissions, isFeatureEnabled]
|
|
);
|
|
|
|
const hasAnyPermission = useCallback(
|
|
(...perms: string[]): boolean => {
|
|
return perms.some(p => hasPermission(p));
|
|
},
|
|
[hasPermission]
|
|
);
|
|
|
|
const refetch = useCallback(() => {
|
|
queryClient.invalidateQueries({ queryKey: ['my-permissions'] });
|
|
}, [queryClient]);
|
|
|
|
const value = useMemo(
|
|
(): PermissionContextType => ({
|
|
permissions,
|
|
maintenance,
|
|
isAdmin,
|
|
isLoading: isAuthenticated ? isLoading : false,
|
|
hasPermission,
|
|
hasAnyPermission,
|
|
isFeatureEnabled,
|
|
refetch,
|
|
}),
|
|
[permissions, maintenance, isAdmin, isAuthenticated, isLoading, hasPermission, hasAnyPermission, isFeatureEnabled, refetch]
|
|
);
|
|
|
|
return (
|
|
<PermissionContext.Provider value={value}>
|
|
{children}
|
|
</PermissionContext.Provider>
|
|
);
|
|
};
|
|
|
|
export const usePermissionContext = (): PermissionContextType => {
|
|
const context = useContext(PermissionContext);
|
|
if (context === undefined) {
|
|
throw new Error('usePermissionContext must be used within a PermissionProvider');
|
|
}
|
|
return context;
|
|
};
|