Developed by: Talys Matheus Cordeiro Silva (Tcordeiro) — Professional Portfolio Showcase
- Architecture Overview
- Data Flow
- Security Model (RBAC + RLS)
- Core Modules
- Mobile & PWA Implementation
- Real-time & Performance
- Edge Functions
- Permission Matrix
The system follows a serverless-first architecture leveraging Supabase as the primary Backend-as-a-Service (BaaS) layer:
┌─────────────────────────────────────────────────────────────┐
│ Client Layer (React 18 + TypeScript) │
│ Vite · Tailwind · shadcn/ui · React Query │
└──────────────────────────┬──────────────────────────────────┘
│
┌──────────────┼──────────────┐
│ │ │
┌──────▼──────┐ ┌─────▼──────┐ ┌────▼───────┐
│ REST API │ │ Realtime │ │ WebSocket │
│ (PostgREST) │ │ (LISTEN/ │ │ (Telemetry) │
└──────┬──────┘ │ NOTIFY) │ └────┬───────┘
│ └─────┬──────┘ │
┌──────▼──────────────▼─────────────▼──────┐
│ Supabase Platform │
│ PostgreSQL · Auth · Storage · Edge Fn │
└───────────────────────────────────────────┘
User → New Ticket Form → Sector/Topic → Structured JSONB
↓
PostgreSQL RLS
↓
+----------------+----------------+
| |
Realtime Notify Edge Function
(UI Push Update) (send-notification)
| |
Kanban Board Push + Webhook Dispatch
auto-updates (group email + external)
Dynamic form data is persisted in a dados_formulario (JSONB) column, enabling:
// Priority 1: Structured JSONB from dynamic form
const placa = dados_formulario?.placa
|| dados_formulario?.placaVeiculo
// Priority 2: Intelligent regex on legacy description bullets
|| extractFromBullets(descricao, 'Placa')
// Normalized for search (removes accents, hyphens, spaces)
|| normalized(rawText)| Role | Code | Access Level |
|---|---|---|
| Admin | admin |
Full — all data, all users |
| Developer | dev |
Full + technical panels |
| Operator | operador |
Own tickets + assigned tickets |
| User | usuario |
Own submissions only |
| Viewer | visualizar_kanban |
Read-only Kanban access |
All tables enforce RLS policies that evaluate the authenticated JWT to determine row visibility:
- Users see only their own tickets unless assigned as operator.
- Operators see tickets in their sector or directly assigned to them.
- Admins bypass all restrictions with full read/write access.
- Viewers access Kanban read-only — chat and drag-and-drop are locked in the UI.
To resolve user display names without exposing private data (email/phone), an Edge Function bypasses RLS using the service role key, returning only { id, nome } — never full profile data.
The ticket system supports two independent Kanban boards with separate access controls:
- Standard Kanban — for general support operators
- Operational Kanban — for logistics/freight operators (
kanban_acesso = 'OPERACIONAL')
Cards display dynamically extracted fields:
| Field | Source |
|---|---|
| License Plate | dados_formulario.placa or bullet extraction |
| CT-e / Freight Note | dados_formulario.cte or bullet extraction |
| Client | dados_formulario.cliente or bullet extraction |
| SLA Status | Computed from created_at vs 3h threshold |
| Responsible | Assigned operator name with avatar |
Replaced the legacy manual "Urgency" field with an automatic time-based metric:
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 // 3h
);
if (finalizado_at) return 'No Prazo';
return new Date() > prazoLimite ? 'Em Atraso' : 'No Prazo';
};Displayed as a colored badge (green/red) on all Kanban cards and in the reporting dashboard.
Real-time hardware telemetry via a hybrid approach:
- PostgreSQL: Persistent asset metadata (specs, location, ownership)
- WebSocket Server: Live CPU/RAM/Disk usage pushed from local Node.js agents
- UI: Folder-organized tree view with online/offline counters per sector
KPI metrics computed via server-side PostgreSQL queries and displayed via Recharts:
- Daily evolution chart (created vs finalized tickets)
- SLA compliance donut chart (On Time / Delayed distribution)
- Demand per sector/branch bar charts
- Most active users per branch
- MTTR (Mean Time To Resolution) calculation
Materials and IT assets tracking with:
- Room-based organization (server rooms, storage, offices)
- Real-time quantity tracking
- Excel export with user-stamped filename (
InventarioSala_YYYY-MM-DD_<user>.xlsx)
Request → Network?
Yes → Serve + Cache update
No → Serve from Cache
No Cache → Fallback page
{
"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);No
100vworw-screen— uses100%withoverflow: hiddento prevent iOS bounce scroll bleed.
// Custom hook: useSwipeGesture
// - Touch start: record X position if within 40px of left edge
// - Touch move: calculate delta, update CSS transform for visual feedback
// - Touch end: if delta > 80px, open sidebar; else snap back
// Passive listeners for 60fps scrolling performance- Horizontal scroll container with
min-width: 800pxcolumns scroll-snap-type: x mandatoryfor fluid column navigation on touch- Drag-and-drop remains functional on touch (hello-pangea/dnd touch support)
Chat subscriptions are stabilized using useRef to prevent loop caused by object identity changes:
// Problematic: unstable reference causes useEffect to re-run
useEffect(() => { subscribe(user) }, [user])
// Stable: extract primitive, use ref for non-primitive
const userIdRef = useRef(user?.id)
useEffect(() => { subscribe(userIdRef.current) }, [user?.id])Realtime updates debounced at 500ms to prevent request storms from rapid consecutive DB events:
const debouncedRefetch = useCallback(
debounce(() => refetchChamados(), 500), []
);
channel.on('postgres_changes', { event: '*', table: 'chamados' }, debouncedRefetch);Critical operations (message send, attachment upload, ticket transfer) include 15-second safety timeouts to unblock the UI if the server fails to respond.
Deployed on Deno runtime, invoked via supabase.functions.invoke():
| Function | Purpose |
|---|---|
send-notification |
Dispatches push notifications + external webhook with group email |
admin-create-user |
Creates users with service role — supports all profile types including dev |
get-public-profile |
Resolves user display name safely without exposing PII |
All functions require a valid JWT in the Authorization header (verify_jwt = true).
Documentation maintained by Talys Matheus Cordeiro Silva (Tcordeiro)
