Desenvolvido por: Talys Matheus Cordeiro Silva (Tcordeiro) — Showcase de Portfólio Profissional
- Visão Geral da Arquitetura
- Fluxo de Dados
- Modelo de Segurança (RBAC + RLS)
- Módulos Principais
- Implementação Mobile & PWA
- Tempo Real & Performance
- Edge Functions
- Matriz de Permissões
O sistema segue uma arquitetura serverless-first utilizando o Supabase como camada principal de Backend-as-a-Service (BaaS):
┌─────────────────────────────────────────────────────────────┐
│ Camada Cliente (React 18 + TypeScript) │
│ Vite · Tailwind · shadcn/ui · React Query │
└──────────────────────────┬──────────────────────────────────┘
│
┌──────────────┼──────────────┐
│ │ │
┌──────▼──────┐ ┌─────▼──────┐ ┌────▼───────┐
│ API REST │ │ Realtime │ │ WebSocket │
│ (PostgREST) │ │ (LISTEN/ │ │ (Telemetria)│
└──────┬──────┘ │ NOTIFY) │ └────┬───────┘
│ └─────┬──────┘ │
┌──────▼──────────────▼─────────────▼──────┐
│ Plataforma Supabase │
│ PostgreSQL · Auth · Storage · Edge Fn │
└───────────────────────────────────────────┘
| Camada | Tecnologia | Propósito |
|---|---|---|
| Frontend | React 18 + TypeScript | UI tipada e reativa |
| Build | Vite 5 | HMR rápido, saída ESM |
| Estilização | Tailwind CSS + shadcn/ui | Sistema de design |
| Estado | React Query (TanStack) | Estado server-side + cache |
| Banco de Dados | PostgreSQL (Supabase) | Armazenamento principal |
| Auth | Supabase Auth (JWT) | Gerenciamento de sessão |
| Tempo Real | Supabase Realtime | Subscriptions WebSocket |
| Edge | Deno (Edge Functions) | Computação serverless |
| Telemetria | Agente WebSocket em Node.js | Monitoramento de hardware |
| Gráficos | Recharts | Visualização de dados |
| DnD | @hello-pangea/dnd | Drag-and-drop do Kanban |
| Mobile | Service Worker + Manifest | Suporte PWA |
Usuário → Formulário Novo Chamado → Setor/Tópico → JSONB Estruturado
↓
PostgreSQL RLS
↓
+------------------+------------------+
| |
Notify Realtime Edge Function
(Atualização UI Push) (send-notification)
| |
Kanban Board Push + Dispatch Webhook
atualiza automático (email do grupo + externo)
Os dados de formulário dinâmico são persistidos na coluna dados_formulario (JSONB), possibilitando:
// Prioridade 1: JSONB estruturado do formulário dinâmico
const placa = dados_formulario?.placa
|| dados_formulario?.placaVeiculo
// Prioridade 2: Regex inteligente em bullets de descrição legada
|| extractFromBullets(descricao, 'Placa')
// Normalizado para busca (remove acentos, hífens, espaços)
|| normalized(rawText)| Papel | Código | Nível de Acesso |
|---|---|---|
| Administrador | admin |
Total — todos os dados, todos os usuários |
| Desenvolvedor | dev |
Total + painéis técnicos |
| Operador | operador |
Chamados próprios + atribuídos |
| Usuário | usuario |
Apenas envios próprios |
| Visualizador | visualizar_kanban |
Acesso leitura ao Kanban |
Todas as tabelas aplicam políticas RLS que avaliam o JWT autenticado para determinar a visibilidade das linhas:
- Usuários veem apenas seus próprios chamados, a menos que sejam atribuídos como operador.
- Operadores veem chamados do próprio setor ou atribuídos diretamente a eles.
- Admins bypassam todas as restrições com acesso total de leitura/escrita.
- Visualizadores acessam o Kanban somente leitura — chat e drag-and-drop são bloqueados na UI.
Para resolver nomes de exibição de usuários sem expor dados privados (email/telefone), uma Edge Function bypassa o RLS usando a chave de serviço, retornando apenas { id, nome } — nunca dados completos do perfil.
O sistema de chamados suporta dois quadros Kanban independentes com controles de acesso separados:
- Kanban Padrão — para operadores de suporte geral
- Kanban Operacional — para operadores logísticos/frete (
kanban_acesso = 'OPERACIONAL')
Os cards exibem campos extraídos dinamicamente:
| Campo | Origem |
|---|---|
| Placa | dados_formulario.placa ou extração de bullets |
| CT-e / Nota de Frete | dados_formulario.cte ou extração de bullets |
| Cliente | dados_formulario.cliente ou extração de bullets |
| Status SLA | Calculado de created_at vs limiar de 3h |
| Responsável | Nome do operador atribuído com avatar |
Substituiu o campo manual legado de "Urgência" por uma métrica automática baseada em tempo:
export const calcularStatusPrazo = (
created_at: string,
finalizado_at: string | null
): 'No Prazo' | 'Em Atraso' => {
const prazoLimite = new Date(
new Date(created_at).getTime() + 3 * 60 * 60 * 1000 // 3 horas
);
if (finalizado_at) return 'No Prazo';
return new Date() > prazoLimite ? 'Em Atraso' : 'No Prazo';
};Exibido como badge colorido (verde/vermelho) em todos os cards do Kanban e no dashboard de relatórios.
Telemetria de hardware em tempo real via abordagem híbrida:
- PostgreSQL: Metadados persistentes de ativos (specs, localização, propriedade)
- WebSocket Server: CPU/RAM/Disco em tempo real via agentes Node.js locais
- UI: Visualização em árvore organizada por pastas com contadores online/offline por setor
Métricas KPI computadas via queries server-side no PostgreSQL e exibidas via Recharts:
- Gráfico de evolução diária (chamados criados vs finalizados)
- Donut chart de conformidade SLA (distribuição No Prazo / Em Atraso)
- Gráficos de barras de demanda por setor/filial
- Usuários mais ativos por filial
- Cálculo de MTTR (Tempo Médio de Resolução)
Rastreamento de materiais e ativos de TI com:
- Organização por sala (salas de servidores, almoxarifados, escritórios)
- Rastreamento de quantidade em tempo real
- Exportação Excel com nome de arquivo carimbado pelo usuário (
EstoqueSala_AAAA-MM-DD_<usuario>.xlsx)
Requisição → Rede disponível?
Sim → Servir + atualizar Cache
Não → Servir do Cache
Sem Cache → Página Fallback
{
"name": "IntraNet",
"short_name": "IntraNet",
"display": "standalone",
"orientation": "portrait",
"theme_color": "#0f172a",
"background_color": "#0f172a",
"icons": [
{ "src": "/icons/icon-192.png", "sizes": "192x192", "type": "image/png" },
{ "src": "/icons/icon-512.png", "sizes": "512x512", "type": "image/png" }
]
}/* iPhone notch / Dynamic Island */
padding-top: env(safe-area-inset-top);
padding-bottom: env(safe-area-inset-bottom);
padding-left: env(safe-area-inset-left);
padding-right: env(safe-area-inset-right);Sem uso de
100vwouw-screen— usa100%comoverflow: hiddenpara evitar sangramento do bounce scroll do iOS.
// Hook customizado: useSwipeGesture
// - Touch start: registra posição X se dentro de 40px da borda esquerda
// - Touch move: calcula delta, atualiza CSS transform para feedback visual
// - Touch end: se delta > 80px, abre sidebar; caso contrário, retorna ao lugar
// Listeners passivos para desempenho de rolagem em 60fps- Container com scroll horizontal e colunas
min-width: 800px scroll-snap-type: x mandatorypara navegação fluida entre colunas no touch- Drag-and-drop funcional no touch (@hello-pangea/dnd com suporte a touch)
Subscriptions do chat são estabilizadas com useRef para evitar loops causados por mudanças de referência de objeto:
// Problemático: referência instável faz useEffect re-executar
useEffect(() => { subscribe(user) }, [user])
// Estável: extrai primitivo, usa ref para não-primitivo
const userIdRef = useRef(user?.id)
useEffect(() => { subscribe(userIdRef.current) }, [user?.id])Atualizações Realtime com debounce de 500ms para evitar tempestades de requisições de eventos DB consecutivos rápidos:
const debouncedRefetch = useCallback(
debounce(() => refetchChamados(), 500), []
);
channel.on('postgres_changes', { event: '*', table: 'chamados' }, debouncedRefetch);Operações críticas (envio de mensagem, upload de arquivo, transferência de chamado) incluem timeouts de segurança de 15 segundos para desbloquear a UI caso o servidor falhe em responder.
Implantadas no runtime Deno, invocadas via supabase.functions.invoke():
| Função | Propósito |
|---|---|
send-notification |
Despacha push notifications + webhook externo com email do grupo |
admin-create-user |
Cria usuários com service role — suporta todos os tipos de perfil incluindo dev |
get-public-profile |
Resolve nome de exibição do usuário com segurança sem expor PII |
Todas as funções exigem JWT válido no header Authorization (verify_jwt = true).
| Funcionalidade | Admin | Dev | Operador | Usuário | Visualizador |
|---|---|---|---|---|---|
| Ver todos os chamados | ✅ | ✅ | Próprio setor | Apenas próprios | ✅ (leitura) |
| Criar chamados | ✅ | ✅ | ✅ | ✅ | ❌ |
| Atribuir/transferir chamados | ✅ | ✅ | ✅ | ❌ | ❌ |
| Finalizar chamados | ✅ | ✅ | ✅ | ❌ | ❌ |
| Arrastar cards Kanban | ✅ | ✅ | ✅ | ❌ | ❌ |
| Acessar painéis Admin | ✅ | ✅ | ❌ | ❌ | ❌ |
| Ver analytics | ✅ | ✅ | ✅ | ❌ | ❌ |
| Exportar Excel | ✅ | ✅ | ✅ | ❌ | ❌ |
| Gerenciar usuários | ✅ | ✅ | ❌ | ❌ | ❌ |
| Monitoramento de ativos | ✅ | ✅ | ✅ | ❌ | ❌ |
Documentação mantida por Talys Matheus Cordeiro Silva (Tcordeiro)