From 246e8d08f24baedf6311ca3b3e643799485960f2 Mon Sep 17 00:00:00 2001 From: admbrainsteel Date: Fri, 25 Sep 2026 11:18:47 -0300 Subject: [PATCH] feat: Gestao Avancada de Privilegios e Notificacoes do Modo Smart --- .../users/matriz/MatrizAcessosPainel.tsx | 21 +- src/components/users/matriz/MatrizGrid.tsx | 18 +- .../users/matriz/MatrizSmartAvancadoModal.tsx | 536 ++++++++++++++++++ src/hooks/useSmartPermissions.ts | 235 ++++++++ src/hooks/useUserSmartAccess.ts | 78 +++ src/types/smartPermissions.ts | 107 ++++ 6 files changed, 992 insertions(+), 3 deletions(-) create mode 100644 src/components/users/matriz/MatrizSmartAvancadoModal.tsx create mode 100644 src/hooks/useSmartPermissions.ts create mode 100644 src/hooks/useUserSmartAccess.ts create mode 100644 src/types/smartPermissions.ts diff --git a/src/components/users/matriz/MatrizAcessosPainel.tsx b/src/components/users/matriz/MatrizAcessosPainel.tsx index 1cb3940..cb1bbfe 100644 --- a/src/components/users/matriz/MatrizAcessosPainel.tsx +++ b/src/components/users/matriz/MatrizAcessosPainel.tsx @@ -2,11 +2,12 @@ import React, { useState } from 'react'; import { useMatrizAcessos, UsuarioMatriz } from '@/hooks/useMatrizAcessos'; import { MatrizGrid } from './MatrizGrid'; import { MatrizTemplatesModal } from './MatrizTemplatesModal'; +import { MatrizSmartAvancadoModal } from './MatrizSmartAvancadoModal'; import { SimuladorVisaoBar } from './SimuladorVisaoBar'; import { Input } from '@/components/ui/input'; import { Button } from '@/components/ui/button'; import { Badge } from '@/components/ui/badge'; -import { Search, RefreshCw, Users, Shield, Factory, Sparkles } from 'lucide-react'; +import { Search, RefreshCw, Users, Shield, Factory, Sparkles, SlidersHorizontal } from 'lucide-react'; interface MatrizAcessosPainelProps { onOpenNovoUsuario?: () => void; @@ -28,6 +29,7 @@ export const MatrizAcessosPainel: React.FC = ({ const [busca, setBusca] = useState(''); const [selectedUserForTemplate, setSelectedUserForTemplate] = useState(null); + const [smartAvancadoOpen, setSmartAvancadoOpen] = useState(false); // Filtragem por nome, email ou cargo const usuariosFiltrados = usuarios.filter((u) => { @@ -105,6 +107,16 @@ export const MatrizAcessosPainel: React.FC = ({
+ +
); }; diff --git a/src/components/users/matriz/MatrizGrid.tsx b/src/components/users/matriz/MatrizGrid.tsx index 6bda0ec..65b2c54 100644 --- a/src/components/users/matriz/MatrizGrid.tsx +++ b/src/components/users/matriz/MatrizGrid.tsx @@ -3,7 +3,7 @@ import { UsuarioMatriz, GrupoRecurso, NivelAcesso } from '@/hooks/useMatrizAcess import { Badge } from '@/components/ui/badge'; import { Button } from '@/components/ui/button'; import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'; -import { Sparkles, Eye, Edit3, ShieldAlert } from 'lucide-react'; +import { Sparkles, Eye, Edit3, ShieldAlert, SlidersHorizontal } from 'lucide-react'; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip'; interface MatrizGridProps { @@ -11,6 +11,7 @@ interface MatrizGridProps { grupos: GrupoRecurso[]; onToggleNivel: (userId: string, resourceKey: string, tipo: 'V' | 'C') => void; onOpenTemplates: (user: UsuarioMatriz) => void; + onOpenSmartAvancado?: () => void; } export const MatrizGrid: React.FC = ({ @@ -18,6 +19,7 @@ export const MatrizGrid: React.FC = ({ grupos, onToggleNivel, onOpenTemplates, + onOpenSmartAvancado, }) => { // Cores de fundo e borda para os grupos const corGrupoHeader: Record = { @@ -48,8 +50,20 @@ export const MatrizGrid: React.FC = ({ colSpan={grupo.recursos.length} className={`p-2.5 text-center border-r border-border/40 text-xs font-bold ${corGrupoHeader[grupo.cor]}`} > -
+
{grupo.titulo} + {grupo.key === 'smart' && onOpenSmartAvancado && ( + + )}
{grupo.descricao} diff --git a/src/components/users/matriz/MatrizSmartAvancadoModal.tsx b/src/components/users/matriz/MatrizSmartAvancadoModal.tsx new file mode 100644 index 0000000..ef264cd --- /dev/null +++ b/src/components/users/matriz/MatrizSmartAvancadoModal.tsx @@ -0,0 +1,536 @@ +import React, { useState } from 'react'; +import { + Dialog, + DialogContent, + DialogHeader, + DialogTitle, + DialogDescription, +} from '@/components/ui/dialog'; +import { Button } from '@/components/ui/button'; +import { Input } from '@/components/ui/input'; +import { Badge } from '@/components/ui/badge'; +import { Switch } from '@/components/ui/switch'; +import { Label } from '@/components/ui/label'; +import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'; +import { + SlidersHorizontal, + Search, + CheckCircle2, + Save, + RotateCcw, + Shield, + Factory, + Eye, + Edit3, + Trash2, + Mail, + Phone, + MessageSquare, + AlertTriangle, + FileText, + Layers, + Flame, + Zap, + Box, + ExternalLink, + Truck, + Wrench, + FileSpreadsheet, + Info, + Sparkles, + Lock, +} from 'lucide-react'; +import { useSmartPermissions } from '@/hooks/useSmartPermissions'; +import { SMART_SUBMODULES } from '@/types/smartPermissions'; + +interface MatrizSmartAvancadoModalProps { + isOpen: boolean; + onClose: () => void; +} + +export const MatrizSmartAvancadoModal: React.FC = ({ + isOpen, + onClose, +}) => { + const { + loading, + saving, + users, + selectedUserId, + setSelectedUserId, + selectedUser, + togglePermission, + updateNotificationConfig, + salvarConfiguracaoUsuario, + aplicarTemplateSmart, + recarregar, + } = useSmartPermissions(); + + const [busca, setBusca] = useState(''); + + const usersFiltrados = users.filter((u) => { + const termo = busca.toLowerCase(); + return ( + u.fullName.toLowerCase().includes(termo) || + u.email.toLowerCase().includes(termo) || + u.functionName.toLowerCase().includes(termo) + ); + }); + + const getIconComponent = (icone: string) => { + switch (icone) { + case 'FileText': + return ; + case 'Layers': + return ; + case 'Flame': + return ; + case 'CheckCircle2': + return ; + case 'Zap': + return ; + case 'Box': + return ; + case 'ExternalLink': + return ; + case 'Truck': + return ; + case 'Wrench': + return ; + case 'FileSpreadsheet': + return ; + default: + return ; + } + }; + + return ( + !open && onClose()}> + + {/* CABEÇALHO DO MODAL */} +
+
+
+ +
+
+ + Gestão Avançada de Privilégios & Notificações + + Modo Smart + + + + Controle granular de acesso por submódulo e canais de notificação (E-mail e WhatsApp via única). + +
+
+
+ + {/* CORPO PRINCIPAL COM SIDEBAR DE USUÁRIOS E ÁREA DE CONFIGURAÇÃO */} +
+ {/* SIDEBAR DE USUÁRIOS */} +
+
+
+ + setBusca(e.target.value)} + className="pl-8 text-xs h-8 bg-card" + /> +
+
+ +
+ {loading ? ( +
+ Carregando colaboradores... +
+ ) : usersFiltrados.length === 0 ? ( +
+ Nenhum colaborador encontrado. +
+ ) : ( + usersFiltrados.map((user) => { + const isSelected = user.userId === selectedUserId; + return ( + + ); + }) + )} +
+
+ + {/* ÁREA DE CONFIGURAÇÃO DO USUÁRIO SELECIONADO */} +
+ {selectedUser ? ( +
+ {/* CABEÇALHO DO USUÁRIO + AÇÕES RÁPIDAS */} +
+
+ + + + {selectedUser.fullName.substring(0, 2).toUpperCase()} + + +
+

+ {selectedUser.fullName} +

+

+ {selectedUser.email} + • + {selectedUser.functionName} +

+
+
+ + {/* TEMPLATES E BOTÃO SALVAR */} +
+ + + +
+
+ + {/* SEÇÃO 1: MATRIZ DE ACESSO AOS SUBMÓDULOS */} +
+
+

+ + 1. Permissões Granulares nos Submódulos (Modo Smart) +

+ + Controle de Visualização, Edição/Apontamento e Exclusão + +
+ +
+ + + + + + + + + + + {SMART_SUBMODULES.map((sub) => { + const perm = selectedUser.permissions[sub.key] || { + can_view: false, + can_edit: false, + can_delete: false, + }; + + return ( + + + + {/* VISUALIZAR */} + + + {/* EDITAR / APONTAR */} + + + {/* EXCLUIR */} + + + ); + })} + +
Submódulo / Recurso + + + Visualizar + + + + + Apontar / Editar + + + + + Excluir + +
+
+
+ {getIconComponent(sub.icone)} +
+
+

+ {sub.nome} +

+

+ {sub.descricao} +

+
+
+
+
+ + togglePermission(selectedUser.userId, sub.key, 'can_view') + } + /> +
+
+
+ + togglePermission(selectedUser.userId, sub.key, 'can_edit') + } + /> +
+
+
+ + togglePermission(selectedUser.userId, sub.key, 'can_delete') + } + /> +
+
+
+
+ + {/* SEÇÃO 2: NOTIFICAÇÕES & CANAL WHATSAPP VIA ÚNICA */} +
+

+ + 2. Notificações do Usuário (E-mail & WhatsApp BrainSteel) +

+ + {/* BANNER INFORMATIVO: REGRA DE VIA ÚNICA */} +
+ +
+

+ Comunicação Estritamente Unidirecional (Via Única) no WhatsApp +

+

+ O número oficial da BrainSteel é configurado para somente enviar alertas, relatórios e métricas de produção. Mensagens de resposta enviadas pelo colaborador não serão recebidas nem respondidas pelo sistema. +

+
+
+ + {/* GRID DE CONFIGURAÇÕES DE CANAL */} +
+ {/* CANAL WHATSAPP */} +
+
+ + Canal WhatsApp do Colaborador +
+ +
+ + + updateNotificationConfig( + selectedUser.userId, + 'whatsappNumber', + e.target.value + ) + } + className="h-8 text-xs bg-card" + /> + + Informe o número com DDD para receber avisos automáticos. + +
+ +
+
+
+ +

+ Notificar sobre novos apontamentos ou avanços forçados +

+
+ + updateNotificationConfig( + selectedUser.userId, + 'notifWhatsappApontamentos', + val + ) + } + /> +
+ +
+
+ +

+ Receber fechamento diário de produção (peso e peças) +

+
+ + updateNotificationConfig( + selectedUser.userId, + 'notifWhatsappMetricas', + val + ) + } + /> +
+
+
+ + {/* CANAL E-MAIL */} +
+
+ + Canal de E-mail ({selectedUser.email}) +
+ +
+
+
+ +

+ Receber e-mail informativo a cada lote apontado +

+
+ + updateNotificationConfig( + selectedUser.userId, + 'notifEmailApontamentos', + val + ) + } + /> +
+ +
+
+ +

+ Receber resumos periódicos e PDFs automáticos +

+
+ + updateNotificationConfig( + selectedUser.userId, + 'notifEmailMetricas', + val + ) + } + /> +
+
+
+
+
+
+ ) : ( +
+ Selecione um colaborador na barra lateral para configurar. +
+ )} +
+
+ + {/* RODAPÉ DO MODAL */} +
+ + As alterações passam a valer imediatamente no Modo Smart após salvar. + + +
+
+
+ ); +}; diff --git a/src/hooks/useSmartPermissions.ts b/src/hooks/useSmartPermissions.ts new file mode 100644 index 0000000..661d9bb --- /dev/null +++ b/src/hooks/useSmartPermissions.ts @@ -0,0 +1,235 @@ +import { useState, useEffect, useCallback } from 'react'; +import { supabase } from '@/integrations/supabase/client'; +import { toast } from 'sonner'; +import { SMART_SUBMODULES, SmartUserConfig } from '@/types/smartPermissions'; + +export function useSmartPermissions() { + const [loading, setLoading] = useState(true); + const [saving, setSaving] = useState(false); + const [users, setUsers] = useState([]); + const [selectedUserId, setSelectedUserId] = useState(null); + + const carregarDados = useCallback(async () => { + try { + setLoading(true); + + // 1. Carregar profiles com functions + const { data: profiles, error: profError } = await (supabase as any) + .from('profiles') + .select(` + id, + email, + full_name, + profile_image_url, + whatsapp_number, + notif_email_apontamentos, + notif_email_metricas, + notif_whatsapp_apontamentos, + notif_whatsapp_metricas, + functions:function_id ( name ) + `) + .order('full_name', { ascending: true }); + + if (profError) throw profError; + + // 2. Carregar permissões da tabela smart_user_permissions + const { data: perms, error: permError } = await (supabase as any) + .from('smart_user_permissions') + .select('*'); + + if (permError) throw permError; + + // Montar mapa de permissões por usuário + const permsByUser: Record> = {}; + + (perms || []).forEach((p: any) => { + if (!permsByUser[p.user_id]) { + permsByUser[p.user_id] = {}; + } + permsByUser[p.user_id][p.resource_key] = { + can_view: Boolean(p.can_view), + can_edit: Boolean(p.can_edit), + can_delete: Boolean(p.can_delete), + }; + }); + + const formattedUsers: SmartUserConfig[] = (profiles || []).map((prof: any) => { + const userPerms = permsByUser[prof.id] || {}; + const mergedPerms: Record = {}; + + SMART_SUBMODULES.forEach((sub) => { + if (userPerms[sub.key]) { + mergedPerms[sub.key] = userPerms[sub.key]; + } else { + // Default fallback + mergedPerms[sub.key] = { ...sub.padraoOperador }; + } + }); + + return { + userId: prof.id, + email: prof.email || '', + fullName: prof.full_name || prof.email?.split('@')[0] || 'Sem Nome', + functionName: prof.functions?.name || 'Não atribuído', + profileImageUrl: prof.profile_image_url, + whatsappNumber: prof.whatsapp_number || '', + notifEmailApontamentos: prof.notif_email_apontamentos !== false, + notifEmailMetricas: prof.notif_email_metricas !== false, + notifWhatsappApontamentos: prof.notif_whatsapp_apontamentos !== false, + notifWhatsappMetricas: prof.notif_whatsapp_metricas !== false, + permissions: mergedPerms, + }; + }); + + setUsers(formattedUsers); + if (formattedUsers.length > 0 && !selectedUserId) { + setSelectedUserId(formattedUsers[0].userId); + } + } catch (err: any) { + console.error('Erro ao carregar permissões do Modo Smart:', err); + toast.error('Não foi possível carregar as permissões do Modo Smart.'); + } finally { + setLoading(false); + } + }, [selectedUserId]); + + useEffect(() => { + carregarDados(); + }, []); + + const selectedUser = users.find((u) => u.userId === selectedUserId) || null; + + // Atualizar permissão no estado local + const togglePermission = ( + userId: string, + resourceKey: string, + field: 'can_view' | 'can_edit' | 'can_delete' + ) => { + setUsers((prev) => + prev.map((u) => { + if (u.userId !== userId) return u; + const current = u.permissions[resourceKey] || { can_view: false, can_edit: false, can_delete: false }; + const updated = { + ...current, + [field]: !current[field], + }; + // Se desativar visualizar, desativa edição e exclusão por coerência + if (field === 'can_view' && !updated.can_view) { + updated.can_edit = false; + updated.can_delete = false; + } + // Se ativar edição ou exclusão, ativa automaticamente visualizar + if ((field === 'can_edit' || field === 'can_delete') && updated[field]) { + updated.can_view = true; + } + + return { + ...u, + permissions: { + ...u.permissions, + [resourceKey]: updated, + }, + }; + }) + ); + }; + + // Atualizar dados de notificação no estado local + const updateNotificationConfig = ( + userId: string, + field: 'whatsappNumber' | 'notifEmailApontamentos' | 'notifEmailMetricas' | 'notifWhatsappApontamentos' | 'notifWhatsappMetricas', + value: any + ) => { + setUsers((prev) => + prev.map((u) => (u.userId === userId ? { ...u, [field]: value } : u)) + ); + }; + + // Salvar permissões e perfil do usuário no banco + const salvarConfiguracaoUsuario = async (userId: string) => { + const userToSave = users.find((u) => u.userId === userId); + if (!userToSave) return; + + try { + setSaving(true); + + // 1. Atualizar Profile + const { error: profError } = await (supabase as any) + .from('profiles') + .update({ + whatsapp_number: userToSave.whatsappNumber.trim() || null, + notif_email_apontamentos: userToSave.notifEmailApontamentos, + notif_email_metricas: userToSave.notifEmailMetricas, + notif_whatsapp_apontamentos: userToSave.notifWhatsappApontamentos, + notif_whatsapp_metricas: userToSave.notifWhatsappMetricas, + updated_at: new Date().toISOString(), + }) + .eq('id', userId); + + if (profError) throw profError; + + // 2. Fazer Upsert das permissões na tabela smart_user_permissions + const rowsToUpsert = Object.entries(userToSave.permissions).map(([resource_key, perm]) => ({ + user_id: userId, + resource_key, + can_view: perm.can_view, + can_edit: perm.can_edit, + can_delete: perm.can_delete, + updated_at: new Date().toISOString(), + })); + + const { error: permError } = await (supabase as any) + .from('smart_user_permissions') + .upsert(rowsToUpsert, { onConflict: 'user_id,resource_key' }); + + if (permError) throw permError; + + toast.success(`Configurações de ${userToSave.fullName} salvas com sucesso!`); + } catch (err: any) { + console.error('Erro ao salvar configurações do Modo Smart:', err); + toast.error('Erro ao salvar configurações: ' + (err.message || 'Falha na requisição')); + } finally { + setSaving(false); + } + }; + + // Aplicar Template Padrão (Chão de Fábrica ou Administrador) + const aplicarTemplateSmart = (userId: string, tipo: 'operador' | 'admin' | 'leitura') => { + setUsers((prev) => + prev.map((u) => { + if (u.userId !== userId) return u; + const newPerms: Record = {}; + + SMART_SUBMODULES.forEach((sub) => { + if (tipo === 'operador') { + newPerms[sub.key] = { ...sub.padraoOperador }; + } else if (tipo === 'admin') { + newPerms[sub.key] = { can_view: true, can_edit: true, can_delete: true }; + } else if (tipo === 'leitura') { + newPerms[sub.key] = { can_view: true, can_edit: false, can_delete: false }; + } + }); + + return { + ...u, + permissions: newPerms, + }; + }) + ); + toast.info(`Template ${tipo.toUpperCase()} aplicado na tela. Clique em "Salvar" para confirmar.`); + }; + + return { + loading, + saving, + users, + selectedUserId, + setSelectedUserId, + selectedUser, + togglePermission, + updateNotificationConfig, + salvarConfiguracaoUsuario, + aplicarTemplateSmart, + recarregar: carregarDados, + }; +} diff --git a/src/hooks/useUserSmartAccess.ts b/src/hooks/useUserSmartAccess.ts new file mode 100644 index 0000000..2c37a07 --- /dev/null +++ b/src/hooks/useUserSmartAccess.ts @@ -0,0 +1,78 @@ +import { useState, useEffect } from 'react'; +import { useAuth } from '@/hooks/useAuth'; +import { supabase } from '@/integrations/supabase/client'; +import { SMART_SUBMODULES } from '@/types/smartPermissions'; + +export function useUserSmartAccess() { + const { user } = useAuth(); + const [permissions, setPermissions] = useState< + Record + >({}); + const [loading, setLoading] = useState(true); + + useEffect(() => { + let mounted = true; + + async function loadPermissions() { + if (!user) { + setLoading(false); + return; + } + + try { + setLoading(true); + const { data, error } = await (supabase as any) + .from('smart_user_permissions') + .select('resource_key, can_view, can_edit, can_delete') + .eq('user_id', user.id); + + if (error) throw error; + + const permsMap: Record< + string, + { can_view: boolean; can_edit: boolean; can_delete: boolean } + > = {}; + + // Preenche com padrão do operador primeiro + SMART_SUBMODULES.forEach((sub) => { + permsMap[sub.key] = { ...sub.padraoOperador }; + }); + + // Sobrescreve com as regras salvas para este usuário + (data || []).forEach((row: any) => { + permsMap[row.resource_key] = { + can_view: Boolean(row.can_view), + can_edit: Boolean(row.can_edit), + can_delete: Boolean(row.can_delete), + }; + }); + + if (mounted) { + setPermissions(permsMap); + } + } catch (err) { + console.error('Erro ao carregar permissões do usuário para o Modo Smart:', err); + } finally { + if (mounted) setLoading(false); + } + } + + loadPermissions(); + + return () => { + mounted = false; + }; + }, [user]); + + const canView = (key: string) => permissions[key]?.can_view ?? true; + const canEdit = (key: string) => permissions[key]?.can_edit ?? false; + const canDelete = (key: string) => permissions[key]?.can_delete ?? false; + + return { + loading, + permissions, + canView, + canEdit, + canDelete, + }; +} diff --git a/src/types/smartPermissions.ts b/src/types/smartPermissions.ts new file mode 100644 index 0000000..8ce6409 --- /dev/null +++ b/src/types/smartPermissions.ts @@ -0,0 +1,107 @@ +export interface SmartSubmodule { + key: string; + nome: string; + descricao: string; + icone: string; + padraoOperador: { + can_view: boolean; + can_edit: boolean; + can_delete: boolean; + }; +} + +export const SMART_SUBMODULES: SmartSubmodule[] = [ + { + key: 'smart_of', + nome: 'Ordens de Fabricação (OF)', + descricao: 'Acesso à seleção, listagem e busca de OFs ativas', + icone: 'FileText', + padraoOperador: { can_view: true, can_edit: false, can_delete: false }, + }, + { + key: 'smart_fases', + nome: 'Fases & Etapas da Obra', + descricao: 'Filtro e navegação pelas etapas/fases de fabricação', + icone: 'Layers', + padraoOperador: { can_view: true, can_edit: false, can_delete: false }, + }, + { + key: 'smart_processos', + nome: 'Processos de Fabricação', + descricao: 'Visualização e seleção de processos (Corte, Solda, Pintura, etc.)', + icone: 'Flame', + padraoOperador: { can_view: true, can_edit: false, can_delete: false }, + }, + { + key: 'smart_apontamento', + nome: 'Apontamento de Peças', + descricao: 'Realização de apontamentos normais de produção no chão de fábrica', + icone: 'CheckCircle2', + padraoOperador: { can_view: true, can_edit: true, can_delete: false }, + }, + { + key: 'smart_forcar_apontamento', + nome: 'Forçar Apontamento (Avanço sem restrição)', + descricao: 'Permissão para avançar peças furando fila ou com pendências de processos anteriores', + icone: 'Zap', + padraoOperador: { can_view: false, can_edit: false, can_delete: false }, + }, + { + key: 'smart_3d', + nome: 'Modelo 3D BIM', + descricao: 'Visualizador interativo 3D de peças, montagens e conjuntos', + icone: 'Box', + padraoOperador: { can_view: true, can_edit: false, can_delete: false }, + }, + { + key: 'smart_modo_completo', + nome: 'Acesso ao Modo Completo ERP', + descricao: 'Permissão para alternar da interface touch para o ERP gerencial', + icone: 'ExternalLink', + padraoOperador: { can_view: false, can_edit: false, can_delete: false }, + }, + { + key: 'smart_romaneios', + nome: 'Romaneios & Embarque', + descricao: 'Acesso e conferência de romaneios de expedição de peças', + icone: 'Truck', + padraoOperador: { can_view: true, can_edit: false, can_delete: false }, + }, + { + key: 'smart_montagem', + nome: 'Montagem & Canteiro', + descricao: 'Apontamentos e checklists de montagem externa', + icone: 'Wrench', + padraoOperador: { can_view: true, can_edit: true, can_delete: false }, + }, + { + key: 'smart_relatorios', + nome: 'Relatórios & Exportação PDF', + descricao: 'Geração de resumos de produção, checklists e exportação de relatórios', + icone: 'FileSpreadsheet', + padraoOperador: { can_view: true, can_edit: true, can_delete: false }, + }, +]; + +export interface SmartUserPermission { + id?: string; + user_id: string; + resource_key: string; + can_view: boolean; + can_edit: boolean; + can_delete: boolean; +} + +export interface SmartUserConfig { + userId: string; + email: string; + fullName: string; + functionName: string; + profileImageUrl: string | null; + whatsappNumber: string; + notifEmailApontamentos: boolean; + notifEmailMetricas: boolean; + notifWhatsappApontamentos: boolean; + notifWhatsappMetricas: boolean; + permissions: Record; +}