import { useState, useMemo } from 'react'; import { useNavigate } from 'react-router'; import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import { useTranslation } from 'react-i18next'; import { rbacApi } from '@/api/rbac'; import { PermissionGate } from '@/components/auth/PermissionGate'; import { usePermissionStore } from '@/store/permissions'; import { usePlatform } from '@/platform/hooks/usePlatform'; // === Icons === const BackIcon = () => ( ); const PlusIcon = () => ( ); const EditIcon = () => ( ); const TrashIcon = () => ( ); const ShieldIcon = () => ( ); // === Main Page === export default function AdminRoles() { const { t } = useTranslation(); const navigate = useNavigate(); const queryClient = useQueryClient(); const { capabilities } = usePlatform(); const canManageRole = usePermissionStore((s) => s.canManageRole); const [deleteConfirm, setDeleteConfirm] = useState(null); const [formError, setFormError] = useState(null); // Queries const { data: roles, isLoading: rolesLoading, error: rolesError, } = useQuery({ queryKey: ['admin-roles'], queryFn: rbacApi.getRoles, }); // Mutations const deleteMutation = useMutation({ mutationFn: rbacApi.deleteRole, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['admin-roles'] }); setDeleteConfirm(null); }, onError: () => { setDeleteConfirm(null); setFormError(t('admin.roles.errors.deleteFailed')); }, }); // Sorted roles by level descending const sortedRoles = useMemo(() => { if (!roles) return []; return [...roles].sort((a, b) => b.level - a.level); }, [roles]); return ( {/* Header */} {!capabilities.hasBackButton && ( navigate('/admin')} className="flex h-10 w-10 items-center justify-center rounded-xl border border-dark-700 bg-dark-800 transition-colors hover:border-dark-600" > )} {t('admin.roles.title')} {t('admin.roles.subtitle')} navigate('/admin/roles/create')} className="flex items-center justify-center gap-2 rounded-lg bg-accent-500 px-4 py-2 text-white transition-colors hover:bg-accent-600" > {t('admin.roles.createRole')} {/* Error message */} {formError && ( {formError} )} {/* Stats Overview */} {sortedRoles.length > 0 && ( {sortedRoles.length} {t('admin.roles.stats.totalRoles')} {sortedRoles.filter((r) => r.is_active).length} {t('admin.roles.stats.active')} {sortedRoles.filter((r) => r.is_system).length} {t('admin.roles.stats.system')} )} {/* Roles List */} {rolesLoading ? ( ) : rolesError ? ( {t('admin.roles.errors.loadFailed')} ) : sortedRoles.length === 0 ? ( {t('admin.roles.noRoles')} ) : ( {sortedRoles.map((role) => ( {/* Role name with color badge */} {role.name} {role.is_system && ( {t('admin.roles.systemBadge')} )} {!role.is_active && ( {t('admin.roles.inactiveBadge')} )} {/* Info */} {t('admin.roles.levelLabel')}: {role.level} {role.description && {role.description}} {t('admin.roles.usersCount', { count: role.user_count ?? 0 })} {t('admin.roles.permissionsCount', { count: role.permissions.length, })} {/* Actions */} navigate(`/admin/roles/${role.id}/edit`)} disabled={!canManageRole(role.level)} className="flex-1 rounded-lg bg-dark-700 p-2 text-dark-300 transition-colors hover:bg-dark-600 hover:text-dark-100 disabled:cursor-not-allowed disabled:opacity-40 sm:flex-none" title={t('admin.roles.actions.edit')} > setDeleteConfirm(role.id)} disabled={role.is_system || !canManageRole(role.level)} className="flex-1 rounded-lg bg-dark-700 p-2 text-dark-300 transition-colors hover:bg-error-500/20 hover:text-error-400 disabled:cursor-not-allowed disabled:opacity-40 sm:flex-none" title={t('admin.roles.actions.delete')} > ))} )} {/* Delete Confirmation */} {deleteConfirm !== null && ( setDeleteConfirm(null)} aria-hidden="true" /> {t('admin.roles.confirm.title')} {t('admin.roles.confirm.text')} setDeleteConfirm(null)} className="px-4 py-2 text-dark-300 transition-colors hover:text-dark-100" > {t('admin.roles.confirm.cancel')} deleteMutation.mutate(deleteConfirm)} disabled={deleteMutation.isPending} className="rounded-lg bg-error-500 px-4 py-2 text-white transition-colors hover:bg-error-600 disabled:opacity-50" > {deleteMutation.isPending ? t('admin.roles.confirm.deleting') : t('admin.roles.confirm.delete')} )} ); }
{t('admin.roles.subtitle')}
{formError}
{t('admin.roles.errors.loadFailed')}
{t('admin.roles.noRoles')}
{t('admin.roles.confirm.text')}