diff --git a/src/components/layout/config/menuItems.ts b/src/components/layout/config/menuItems.ts index 5ea48e82..0c0316fc 100644 --- a/src/components/layout/config/menuItems.ts +++ b/src/components/layout/config/menuItems.ts @@ -23,6 +23,7 @@ import { GraduationCap, Shield, Package, + ShieldCheck, } from 'lucide-react'; export interface MenuItem { @@ -234,6 +235,13 @@ export const getCustomerMenuItems = (t: (key: string) => string): MenuItem[] => resource: 'access_tokens', action: 'read', }, + { + name: t('menu.settings.roles'), + href: '/settings/roles', + icon: ShieldCheck, + resource: 'roles', + action: 'read', + }, { name: t('menu.settings.admin'), href: '/settings/admin', diff --git a/src/i18n/config.ts b/src/i18n/config.ts index ce7cacad..5b30fa90 100644 --- a/src/i18n/config.ts +++ b/src/i18n/config.ts @@ -49,6 +49,7 @@ import ptBRInstagram from './locales/pt-BR/instagram.json'; import ptBRMessenger from './locales/pt-BR/messenger.json'; import ptBRCustomerDashboard from './locales/pt-BR/customerDashboard.json'; import ptBRAdminSettings from './locales/pt-BR/adminSettings.json'; +import ptBRRoles from './locales/pt-BR/roles.json'; import ptBRTutorials from './locales/pt-BR/tutorials.json'; import ptAuth from './locales/pt/auth.json'; import ptChangePassword from './locales/pt/changePassword.json'; @@ -97,6 +98,7 @@ import ptInstagram from './locales/pt/instagram.json'; import ptMessenger from './locales/pt/messenger.json'; import ptCustomerDashboard from './locales/pt/customerDashboard.json'; import ptAdminSettings from './locales/pt/adminSettings.json'; +import ptRoles from './locales/pt/roles.json'; import ptTutorials from './locales/pt/tutorials.json'; import enAuth from './locales/en/auth.json'; import enChangePassword from './locales/en/changePassword.json'; @@ -146,6 +148,7 @@ import enInstagram from './locales/en/instagram.json'; import enMessenger from './locales/en/messenger.json'; import enCustomerDashboard from './locales/en/customerDashboard.json'; import enAdminSettings from './locales/en/adminSettings.json'; +import enRoles from './locales/en/roles.json'; import enTutorials from './locales/en/tutorials.json'; import esAuth from './locales/es/auth.json'; import esChangePassword from './locales/es/changePassword.json'; @@ -194,6 +197,7 @@ import esInstagram from './locales/es/instagram.json'; import esMessenger from './locales/es/messenger.json'; import esCustomerDashboard from './locales/es/customerDashboard.json'; import esAdminSettings from './locales/es/adminSettings.json'; +import esRoles from './locales/es/roles.json'; import esTutorials from './locales/es/tutorials.json'; import frAuth from './locales/fr/auth.json'; import frChangePassword from './locales/fr/changePassword.json'; @@ -242,6 +246,7 @@ import frInstagram from './locales/fr/instagram.json'; import frMessenger from './locales/fr/messenger.json'; import frCustomerDashboard from './locales/fr/customerDashboard.json'; import frAdminSettings from './locales/fr/adminSettings.json'; +import frRoles from './locales/fr/roles.json'; import frTutorials from './locales/fr/tutorials.json'; import itAuth from './locales/it/auth.json'; import itChangePassword from './locales/it/changePassword.json'; @@ -290,6 +295,7 @@ import itInstagram from './locales/it/instagram.json'; import itMessenger from './locales/it/messenger.json'; import itCustomerDashboard from './locales/it/customerDashboard.json'; import itAdminSettings from './locales/it/adminSettings.json'; +import itRoles from './locales/it/roles.json'; import ptBRTours from './locales/pt-BR/tours.json'; import ptTours from './locales/pt/tours.json'; import enTours from './locales/en/tours.json'; @@ -381,6 +387,7 @@ const resources = { messenger: ptBRMessenger, customerDashboard: ptBRCustomerDashboard, adminSettings: ptBRAdminSettings, + roles: ptBRRoles, tours: ptBRTours, tutorials: ptBRTutorials, }, @@ -432,6 +439,7 @@ const resources = { messenger: ptMessenger, customerDashboard: ptCustomerDashboard, adminSettings: ptAdminSettings, + roles: ptRoles, tours: ptTours, tutorials: ptTutorials, }, @@ -484,6 +492,7 @@ const resources = { messenger: enMessenger, customerDashboard: enCustomerDashboard, adminSettings: enAdminSettings, + roles: enRoles, tours: enTours, tutorials: enTutorials, }, @@ -535,6 +544,7 @@ const resources = { messenger: esMessenger, customerDashboard: esCustomerDashboard, adminSettings: esAdminSettings, + roles: esRoles, tours: esTours, tutorials: esTutorials, }, @@ -586,6 +596,7 @@ const resources = { messenger: frMessenger, customerDashboard: frCustomerDashboard, adminSettings: frAdminSettings, + roles: frRoles, tours: frTours, tutorials: frTutorials, }, @@ -637,6 +648,7 @@ const resources = { messenger: itMessenger, customerDashboard: itCustomerDashboard, adminSettings: itAdminSettings, + roles: itRoles, tours: itTours, tutorials: itTutorials, }, diff --git a/src/i18n/locales/en/layout.json b/src/i18n/locales/en/layout.json index b48e0340..9c7064d2 100644 --- a/src/i18n/locales/en/layout.json +++ b/src/i18n/locales/en/layout.json @@ -111,6 +111,7 @@ "templates": "Templates", "integrations": "Integrations", "accessTokens": "Access Tokens", + "roles": "Roles & Permissions", "admin": "Admin Settings" } } diff --git a/src/i18n/locales/en/roles.json b/src/i18n/locales/en/roles.json new file mode 100644 index 00000000..a3e3c620 --- /dev/null +++ b/src/i18n/locales/en/roles.json @@ -0,0 +1,72 @@ +{ + "title": "Roles & Permissions", + "description": "Manage roles and their permissions. System roles cannot be deleted, but their permissions can be edited.", + "header": { + "subtitle": "{{count}} role(s) configured", + "searchPlaceholder": "Search roles..." + }, + "addRole": "New Role", + "editRole": "Edit Role", + "deleteRole": "Delete Role", + "backToList": "Back to Roles", + "savePermissions": "Save Permissions", + "saving": "Saving...", + "noRoles": "No roles found", + "noRolesDescription": "Create a custom role to get started.", + "table": { + "name": "Name", + "description": "Description", + "type": "Type", + "permissions": "Permissions", + "users": "Users", + "actions": "Actions" + }, + "type": { + "user": "User", + "account": "Account" + }, + "badges": { + "system": "System", + "custom": "Custom" + }, + "detail": { + "permissionsTitle": "Permissions", + "permissionsDescription": "Check the permissions this role should have. Changes take effect on the user's next session refresh.", + "permissionsSelected": "permissions selected", + "selectAll": "Select all", + "deselectAll": "Deselect all", + "noPermissions": "No permissions available." + }, + "createModal": { + "title": "Create Role", + "description": "Define a name and optional description for the new role. You can configure permissions after creation.", + "nameLabel": "Name", + "namePlaceholder": "e.g. Support Agent", + "descriptionLabel": "Description", + "descriptionPlaceholder": "Brief description of this role", + "cancel": "Cancel", + "create": "Create Role", + "creating": "Creating..." + }, + "deleteDialog": { + "title": "Delete Role", + "description": "Are you sure you want to delete the role \"{{name}}\"? This action cannot be undone.", + "cancel": "Cancel", + "confirm": "Delete", + "deleting": "Deleting..." + }, + "messages": { + "createSuccess": "Role created successfully", + "createError": "Failed to create role", + "updateSuccess": "Role updated successfully", + "updateError": "Failed to update role", + "deleteSuccess": "Role deleted successfully", + "deleteError": "Failed to delete role", + "permissionsSuccess": "Permissions saved successfully", + "permissionsError": "Failed to save permissions", + "loadError": "Failed to load roles", + "systemRoleCannotDelete": "System roles cannot be deleted", + "roleHasUsers": "Cannot delete a role that has assigned users" + }, + "saveChanges": "Save Changes" +} diff --git a/src/i18n/locales/es/layout.json b/src/i18n/locales/es/layout.json index b150c5b9..c4622729 100644 --- a/src/i18n/locales/es/layout.json +++ b/src/i18n/locales/es/layout.json @@ -111,6 +111,7 @@ "templates": "Plantillas", "integrations": "Integraciones", "accessTokens": "Tokens de Acceso", + "roles": "Perfiles y Permisos", "admin": "Configuración Admin" } } diff --git a/src/i18n/locales/es/roles.json b/src/i18n/locales/es/roles.json new file mode 100644 index 00000000..483a25f3 --- /dev/null +++ b/src/i18n/locales/es/roles.json @@ -0,0 +1,72 @@ +{ + "title": "Perfiles y Permisos", + "description": "Gestione perfiles y sus permisos. Los perfiles del sistema no pueden eliminarse, pero sus permisos pueden editarse.", + "header": { + "subtitle": "{{count}} perfil(es) configurado(s)", + "searchPlaceholder": "Buscar perfiles..." + }, + "addRole": "Nuevo Perfil", + "editRole": "Editar Perfil", + "deleteRole": "Eliminar Perfil", + "backToList": "Volver a Perfiles", + "savePermissions": "Guardar Permisos", + "saving": "Guardando...", + "noRoles": "No se encontraron perfiles", + "noRolesDescription": "Crea un perfil personalizado para empezar.", + "table": { + "name": "Nombre", + "description": "Descripción", + "type": "Tipo", + "permissions": "Permisos", + "users": "Usuarios", + "actions": "Acciones" + }, + "type": { + "user": "Usuario", + "account": "Cuenta" + }, + "badges": { + "system": "Sistema", + "custom": "Personalizado" + }, + "detail": { + "permissionsTitle": "Permisos", + "permissionsDescription": "Marca los permisos que debe tener este perfil. Los cambios tienen efecto en el próximo acceso del usuario.", + "permissionsSelected": "permisos seleccionados", + "selectAll": "Seleccionar todos", + "deselectAll": "Deseleccionar todos", + "noPermissions": "No hay permisos disponibles." + }, + "createModal": { + "title": "Crear Perfil", + "description": "Define un nombre y descripción opcional para el nuevo perfil. Puedes configurar los permisos después de crearlo.", + "nameLabel": "Nombre", + "namePlaceholder": "ej. Agente de Soporte", + "descriptionLabel": "Descripción", + "descriptionPlaceholder": "Breve descripción de este perfil", + "cancel": "Cancelar", + "create": "Crear Perfil", + "creating": "Creando..." + }, + "deleteDialog": { + "title": "Eliminar Perfil", + "description": "¿Estás seguro de que quieres eliminar el perfil \"{{name}}\"? Esta acción no se puede deshacer.", + "cancel": "Cancelar", + "confirm": "Eliminar", + "deleting": "Eliminando..." + }, + "messages": { + "createSuccess": "Perfil creado exitosamente", + "createError": "Error al crear el perfil", + "updateSuccess": "Perfil actualizado exitosamente", + "updateError": "Error al actualizar el perfil", + "deleteSuccess": "Perfil eliminado exitosamente", + "deleteError": "Error al eliminar el perfil", + "permissionsSuccess": "Permisos guardados exitosamente", + "permissionsError": "Error al guardar los permisos", + "loadError": "Error al cargar los perfiles", + "systemRoleCannotDelete": "Los perfiles del sistema no pueden eliminarse", + "roleHasUsers": "No se puede eliminar un perfil con usuarios asignados" + }, + "saveChanges": "Guardar Cambios" +} diff --git a/src/i18n/locales/fr/layout.json b/src/i18n/locales/fr/layout.json index 875dc521..ac64a141 100644 --- a/src/i18n/locales/fr/layout.json +++ b/src/i18n/locales/fr/layout.json @@ -111,6 +111,7 @@ "templates": "Modèles", "integrations": "Intégrations", "accessTokens": "Jetons D'accès", + "roles": "Profils et Permissions", "admin": "Paramètres Admin" } } diff --git a/src/i18n/locales/fr/roles.json b/src/i18n/locales/fr/roles.json new file mode 100644 index 00000000..96453a05 --- /dev/null +++ b/src/i18n/locales/fr/roles.json @@ -0,0 +1,72 @@ +{ + "title": "Profils et Permissions", + "description": "Gérez les profils et leurs permissions. Les profils système ne peuvent pas être supprimés, mais leurs permissions peuvent être modifiées.", + "header": { + "subtitle": "{{count}} profil(s) configuré(s)", + "searchPlaceholder": "Rechercher des profils..." + }, + "addRole": "Nouveau Profil", + "editRole": "Modifier le Profil", + "deleteRole": "Supprimer le Profil", + "backToList": "Retour aux Profils", + "savePermissions": "Enregistrer les Permissions", + "saving": "Enregistrement...", + "noRoles": "Aucun profil trouvé", + "noRolesDescription": "Créez un profil personnalisé pour commencer.", + "table": { + "name": "Nom", + "description": "Description", + "type": "Type", + "permissions": "Permissions", + "users": "Utilisateurs", + "actions": "Actions" + }, + "type": { + "user": "Utilisateur", + "account": "Compte" + }, + "badges": { + "system": "Système", + "custom": "Personnalisé" + }, + "detail": { + "permissionsTitle": "Permissions", + "permissionsDescription": "Cochez les permissions que ce profil doit avoir. Les modifications prennent effet à la prochaine connexion de l'utilisateur.", + "permissionsSelected": "permissions sélectionnées", + "selectAll": "Tout sélectionner", + "deselectAll": "Tout désélectionner", + "noPermissions": "Aucune permission disponible." + }, + "createModal": { + "title": "Créer un Profil", + "description": "Définissez un nom et une description optionnelle pour le nouveau profil. Vous pouvez configurer les permissions après la création.", + "nameLabel": "Nom", + "namePlaceholder": "ex. Agent Support", + "descriptionLabel": "Description", + "descriptionPlaceholder": "Brève description de ce profil", + "cancel": "Annuler", + "create": "Créer le Profil", + "creating": "Création..." + }, + "deleteDialog": { + "title": "Supprimer le Profil", + "description": "Êtes-vous sûr de vouloir supprimer le profil \"{{name}}\" ? Cette action est irréversible.", + "cancel": "Annuler", + "confirm": "Supprimer", + "deleting": "Suppression..." + }, + "messages": { + "createSuccess": "Profil créé avec succès", + "createError": "Échec de la création du profil", + "updateSuccess": "Profil mis à jour avec succès", + "updateError": "Échec de la mise à jour du profil", + "deleteSuccess": "Profil supprimé avec succès", + "deleteError": "Échec de la suppression du profil", + "permissionsSuccess": "Permissions enregistrées avec succès", + "permissionsError": "Échec de l'enregistrement des permissions", + "loadError": "Échec du chargement des profils", + "systemRoleCannotDelete": "Les profils système ne peuvent pas être supprimés", + "roleHasUsers": "Impossible de supprimer un profil avec des utilisateurs assignés" + }, + "saveChanges": "Enregistrer les modifications" +} diff --git a/src/i18n/locales/it/layout.json b/src/i18n/locales/it/layout.json index 7a69dfc6..c000ce90 100644 --- a/src/i18n/locales/it/layout.json +++ b/src/i18n/locales/it/layout.json @@ -111,6 +111,7 @@ "templates": "Modelli", "integrations": "Integrazioni", "accessTokens": "Tokens di Accesso", + "roles": "Profili e Permessi", "admin": "Impostazioni Admin" } } diff --git a/src/i18n/locales/it/roles.json b/src/i18n/locales/it/roles.json new file mode 100644 index 00000000..7451fb56 --- /dev/null +++ b/src/i18n/locales/it/roles.json @@ -0,0 +1,72 @@ +{ + "title": "Profili e Permessi", + "description": "Gestisci i profili e i loro permessi. I profili di sistema non possono essere eliminati, ma i loro permessi possono essere modificati.", + "header": { + "subtitle": "{{count}} profilo(i) configurato(i)", + "searchPlaceholder": "Cerca profili..." + }, + "addRole": "Nuovo Profilo", + "editRole": "Modifica Profilo", + "deleteRole": "Elimina Profilo", + "backToList": "Torna ai Profili", + "savePermissions": "Salva Permessi", + "saving": "Salvataggio...", + "noRoles": "Nessun profilo trovato", + "noRolesDescription": "Crea un profilo personalizzato per iniziare.", + "table": { + "name": "Nome", + "description": "Descrizione", + "type": "Tipo", + "permissions": "Permessi", + "users": "Utenti", + "actions": "Azioni" + }, + "type": { + "user": "Utente", + "account": "Account" + }, + "badges": { + "system": "Sistema", + "custom": "Personalizzato" + }, + "detail": { + "permissionsTitle": "Permessi", + "permissionsDescription": "Seleziona i permessi che questo profilo deve avere. Le modifiche hanno effetto al prossimo accesso dell'utente.", + "permissionsSelected": "permessi selezionati", + "selectAll": "Seleziona tutti", + "deselectAll": "Deseleziona tutti", + "noPermissions": "Nessun permesso disponibile." + }, + "createModal": { + "title": "Crea Profilo", + "description": "Definisci un nome e una descrizione opzionale per il nuovo profilo. Puoi configurare i permessi dopo la creazione.", + "nameLabel": "Nome", + "namePlaceholder": "es. Agente Supporto", + "descriptionLabel": "Descrizione", + "descriptionPlaceholder": "Breve descrizione di questo profilo", + "cancel": "Annulla", + "create": "Crea Profilo", + "creating": "Creazione..." + }, + "deleteDialog": { + "title": "Elimina Profilo", + "description": "Sei sicuro di voler eliminare il profilo \"{{name}}\"? Questa azione non può essere annullata.", + "cancel": "Annulla", + "confirm": "Elimina", + "deleting": "Eliminazione..." + }, + "messages": { + "createSuccess": "Profilo creato con successo", + "createError": "Errore nella creazione del profilo", + "updateSuccess": "Profilo aggiornato con successo", + "updateError": "Errore nell'aggiornamento del profilo", + "deleteSuccess": "Profilo eliminato con successo", + "deleteError": "Errore nell'eliminazione del profilo", + "permissionsSuccess": "Permessi salvati con successo", + "permissionsError": "Errore nel salvataggio dei permessi", + "loadError": "Errore nel caricamento dei profili", + "systemRoleCannotDelete": "I profili di sistema non possono essere eliminati", + "roleHasUsers": "Impossibile eliminare un profilo con utenti assegnati" + }, + "saveChanges": "Salva modifiche" +} diff --git a/src/i18n/locales/pt-BR/layout.json b/src/i18n/locales/pt-BR/layout.json index f5743d94..fa4b3662 100644 --- a/src/i18n/locales/pt-BR/layout.json +++ b/src/i18n/locales/pt-BR/layout.json @@ -111,6 +111,7 @@ "templates": "Templates", "integrations": "Integrações", "accessTokens": "Tokens de Acesso", + "roles": "Perfis e Permissões", "admin": "Configurações Admin" } } diff --git a/src/i18n/locales/pt-BR/roles.json b/src/i18n/locales/pt-BR/roles.json new file mode 100644 index 00000000..5ea96973 --- /dev/null +++ b/src/i18n/locales/pt-BR/roles.json @@ -0,0 +1,72 @@ +{ + "title": "Perfis e Permissões", + "description": "Gerencie perfis e suas permissões. Perfis do sistema não podem ser excluídos, mas suas permissões podem ser editadas.", + "header": { + "subtitle": "{{count}} perfil(is) configurado(s)", + "searchPlaceholder": "Buscar perfis..." + }, + "addRole": "Novo Perfil", + "editRole": "Editar Perfil", + "deleteRole": "Excluir Perfil", + "backToList": "Voltar para Perfis", + "savePermissions": "Salvar Permissões", + "saving": "Salvando...", + "noRoles": "Nenhum perfil encontrado", + "noRolesDescription": "Crie um perfil personalizado para começar.", + "table": { + "name": "Nome", + "description": "Descrição", + "type": "Tipo", + "permissions": "Permissões", + "users": "Usuários", + "actions": "Ações" + }, + "type": { + "user": "Usuário", + "account": "Conta" + }, + "badges": { + "system": "Sistema", + "custom": "Personalizado" + }, + "detail": { + "permissionsTitle": "Permissões", + "permissionsDescription": "Marque as permissões que este perfil deve ter. As alterações entram em vigor no próximo acesso do usuário.", + "permissionsSelected": "permissões selecionadas", + "selectAll": "Selecionar todos", + "deselectAll": "Desmarcar todos", + "noPermissions": "Nenhuma permissão disponível." + }, + "createModal": { + "title": "Criar Perfil", + "description": "Defina um nome e descrição opcional para o novo perfil. Você pode configurar as permissões após a criação.", + "nameLabel": "Nome", + "namePlaceholder": "ex: Agente de Suporte", + "descriptionLabel": "Descrição", + "descriptionPlaceholder": "Breve descrição deste perfil", + "cancel": "Cancelar", + "create": "Criar Perfil", + "creating": "Criando..." + }, + "deleteDialog": { + "title": "Excluir Perfil", + "description": "Tem certeza que deseja excluir o perfil \"{{name}}\"? Esta ação não pode ser desfeita.", + "cancel": "Cancelar", + "confirm": "Excluir", + "deleting": "Excluindo..." + }, + "messages": { + "createSuccess": "Perfil criado com sucesso", + "createError": "Falha ao criar perfil", + "updateSuccess": "Perfil atualizado com sucesso", + "updateError": "Falha ao atualizar perfil", + "deleteSuccess": "Perfil excluído com sucesso", + "deleteError": "Falha ao excluir perfil", + "permissionsSuccess": "Permissões salvas com sucesso", + "permissionsError": "Falha ao salvar permissões", + "loadError": "Falha ao carregar perfis", + "systemRoleCannotDelete": "Perfis do sistema não podem ser excluídos", + "roleHasUsers": "Não é possível excluir um perfil com usuários atribuídos" + }, + "saveChanges": "Salvar Alterações" +} diff --git a/src/i18n/locales/pt/layout.json b/src/i18n/locales/pt/layout.json index f5743d94..fa4b3662 100644 --- a/src/i18n/locales/pt/layout.json +++ b/src/i18n/locales/pt/layout.json @@ -111,6 +111,7 @@ "templates": "Templates", "integrations": "Integrações", "accessTokens": "Tokens de Acesso", + "roles": "Perfis e Permissões", "admin": "Configurações Admin" } } diff --git a/src/i18n/locales/pt/roles.json b/src/i18n/locales/pt/roles.json new file mode 100644 index 00000000..2487d9a8 --- /dev/null +++ b/src/i18n/locales/pt/roles.json @@ -0,0 +1,72 @@ +{ + "title": "Perfis e Permissões", + "description": "Gerencie perfis e suas permissões. Perfis do sistema não podem ser excluídos, mas suas permissões podem ser editadas.", + "header": { + "subtitle": "{{count}} perfil(is) configurado(s)", + "searchPlaceholder": "Buscar perfis..." + }, + "addRole": "Novo Perfil", + "editRole": "Editar Perfil", + "deleteRole": "Excluir Perfil", + "backToList": "Voltar para Perfis", + "savePermissions": "Salvar Permissões", + "saving": "Salvando...", + "noRoles": "Nenhum perfil encontrado", + "noRolesDescription": "Crie um perfil personalizado para começar.", + "table": { + "name": "Nome", + "description": "Descrição", + "type": "Tipo", + "permissions": "Permissões", + "users": "Usuários", + "actions": "Ações" + }, + "type": { + "user": "Usuário", + "account": "Conta" + }, + "badges": { + "system": "Sistema", + "custom": "Personalizado" + }, + "detail": { + "permissionsTitle": "Permissões", + "permissionsDescription": "Marque as permissões que este perfil deve ter. As alterações entram em vigor no próximo acesso do usuário.", + "permissionsSelected": "permissões selecionadas", + "selectAll": "Selecionar todos", + "deselectAll": "Desmarcar todos", + "noPermissions": "Nenhuma permissão disponível." + }, + "createModal": { + "title": "Criar Perfil", + "description": "Defina um nome e descrição opcional para o novo perfil. Você pode configurar as permissões após a criação.", + "nameLabel": "Nome", + "namePlaceholder": "ex: Agente de Suporte", + "descriptionLabel": "Descrição", + "descriptionPlaceholder": "Breve descrição deste perfil", + "cancel": "Cancelar", + "create": "Criar Perfil", + "creating": "Criando..." + }, + "deleteDialog": { + "title": "Excluir Perfil", + "description": "Tem certeza que deseja excluir o perfil \"{{name}}\"? Esta ação não pode ser desfeita.", + "cancel": "Cancelar", + "confirm": "Excluir", + "deleting": "Excluindo..." + }, + "messages": { + "createSuccess": "Perfil criado com sucesso", + "createError": "Falha ao criar perfil", + "updateSuccess": "Perfil atualizado com sucesso", + "updateError": "Falha ao atualizar perfil", + "deleteSuccess": "Perfil excluído com sucesso", + "deleteError": "Falha ao excluir perfil", + "permissionsSuccess": "Permissões salvas com sucesso", + "permissionsError": "Falha ao salvar permissões", + "loadError": "Falha ao carregar perfis", + "systemRoleCannotDelete": "Perfis do sistema não podem ser excluídos", + "roleHasUsers": "Não é possível excluir um perfil com usuários atribuídos" + }, + "saveChanges": "Guardar Alterações" +} diff --git a/src/pages/Admin/Roles/RoleDetail.tsx b/src/pages/Admin/Roles/RoleDetail.tsx new file mode 100644 index 00000000..096152ff --- /dev/null +++ b/src/pages/Admin/Roles/RoleDetail.tsx @@ -0,0 +1,285 @@ +import { useState, useEffect, useCallback } from 'react'; +import { useParams, useNavigate } from 'react-router-dom'; +import { useLanguage } from '@/hooks/useLanguage'; +import { toast } from 'sonner'; +import { + Badge, + Button, + Card, + CardContent, + CardHeader, + CardTitle, + Checkbox, + Input, + Label, + Textarea, +} from '@evoapi/design-system'; +import { ArrowLeft, Loader2, Pencil, Save, X } from 'lucide-react'; +import BaseHeader from '@/components/base/BaseHeader'; +import { rolesService, type Role } from '@/services/roles/rolesService'; +import { permissionsService } from '@/services/permissions'; +import { useUserPermissions } from '@/hooks/useUserPermissions'; +import type { ResourceActionsData } from '@/types/auth/permissions'; + +export default function RoleDetail() { + const { id } = useParams<{ id: string }>(); + const { t } = useLanguage('roles'); + const navigate = useNavigate(); + const { can } = useUserPermissions(); + + const [role, setRole] = useState(null); + const [resourceActions, setResourceActions] = useState(null); + const [selected, setSelected] = useState>(new Set()); + const [loading, setLoading] = useState(true); + const [saving, setSaving] = useState(false); + const [editingMeta, setEditingMeta] = useState(false); + const [metaForm, setMetaForm] = useState({ name: '', description: '' }); + const [savingMeta, setSavingMeta] = useState(false); + + const loadData = useCallback(async () => { + if (!id) return; + setLoading(true); + try { + const [roleData, actionsData] = await Promise.all([ + rolesService.get(id), + permissionsService.getResourceActions(), + ]); + setRole(roleData); + setResourceActions(actionsData.data); + + const initialSelected = new Set(); + Object.entries(roleData.permissions_by_resource).forEach(([resource, actions]) => { + (actions as string[]).forEach(action => initialSelected.add(`${resource}.${action}`)); + }); + setSelected(initialSelected); + } catch { + toast.error(t('messages.loadError')); + navigate('/settings/roles'); + } finally { + setLoading(false); + } + }, [id, t, navigate]); + + useEffect(() => { + loadData(); + }, [loadData]); + + const togglePermission = (key: string) => { + setSelected(prev => { + const next = new Set(prev); + next.has(key) ? next.delete(key) : next.add(key); + return next; + }); + }; + + const toggleResource = (resource: string) => { + if (!resourceActions) return; + const keys = Object.keys(resourceActions.resources[resource]?.actions ?? {}).map(a => `${resource}.${a}`); + const allSelected = keys.every(k => selected.has(k)); + setSelected(prev => { + const next = new Set(prev); + allSelected ? keys.forEach(k => next.delete(k)) : keys.forEach(k => next.add(k)); + return next; + }); + }; + + const handleSave = async () => { + if (!role || !resourceActions) return; + setSaving(true); + try { + const knownKeys = Array.from(selected).filter(key => { + const [resource, action] = key.split('.'); + return resourceActions.resources[resource]?.actions?.[action] !== undefined; + }); + const updated = await rolesService.bulkUpdatePermissions(role.id, knownKeys); + setRole(updated); + permissionsService.clearPermissionsCache(); + toast.success(t('messages.permissionsSuccess')); + } catch (err: unknown) { + const apiErr = (err as { response?: { data?: { error?: { message?: string } } } })?.response?.data?.error; + toast.error(apiErr?.message ?? t('messages.permissionsError')); + } finally { + setSaving(false); + } + }; + + const startEditMeta = () => { + if (!role) return; + setMetaForm({ name: role.name, description: role.description ?? '' }); + setEditingMeta(true); + }; + + const cancelEditMeta = () => { + setEditingMeta(false); + }; + + const handleSaveMeta = async () => { + if (!role || !metaForm.name.trim()) return; + setSavingMeta(true); + try { + const updated = await rolesService.update(role.id, { + name: metaForm.name.trim(), + description: metaForm.description.trim() || undefined, + }); + setRole(updated); + toast.success(t('messages.updateSuccess')); + setEditingMeta(false); + } catch (err: unknown) { + const apiErr = (err as { response?: { data?: { error?: { message?: string } } } })?.response?.data?.error; + toast.error(apiErr?.message ?? t('messages.updateError')); + } finally { + setSavingMeta(false); + } + }; + + if (loading) { + return ( +
+ +
+ ); + } + + if (!role || !resourceActions) return null; + + const canEdit = can('roles', 'bulk_update_permissions'); + const canUpdate = can('roles', 'update'); + const resources = resourceActions.resources; + + return ( +
+ : , + onClick: handleSave, + disabled: saving, + } + : undefined + } + secondaryActions={[ + { + label: t('backToList'), + icon: , + onClick: () => navigate('/settings/roles'), + variant: 'outline', + }, + ]} + > +
+ {role.system && {t('badges.system')}} + {t(`type.${role.type}`)} + + {selected.size} {t('detail.permissionsSelected')} + + {canUpdate && !role.system && !editingMeta && ( + + )} +
+
+ + {editingMeta && ( +
+
+ + setMetaForm(prev => ({ ...prev, name: e.target.value }))} + disabled={savingMeta} + /> +
+
+ +