# Arquitetura Frontend ## Arquitetura de Componentes ### Organização de Componentes ``` src/ ├── components/ │ ├── ui/ # Shadcn/ui components │ ├── layout/ │ │ ├── Header.tsx │ │ ├── Footer.tsx │ │ └── DashboardLayout.tsx │ ├── whatsapp/ │ │ ├── WhatsAppInstanceCard.tsx │ │ ├── WhatsAppInstanceList.tsx │ │ └── QRCodeModal.tsx │ └── google-calendar/ │ └── GoogleCalendarCard.tsx ``` ### Template de Componente ```typescript // components/whatsapp/WhatsAppInstanceCard.tsx import { Card, CardHeader, CardTitle, CardContent } from '@/components/ui/card'; import { Button } from '@/components/ui/button'; import { Badge } from '@/components/ui/badge'; import { WhatsAppInstance } from '@/types/whatsapp'; interface WhatsAppInstanceCardProps { instance: WhatsAppInstance; onGenerateQRCode: (instanceName: string) => void; onDisconnect: (instanceName: string) => void; isLoading?: boolean; } export function WhatsAppInstanceCard({ instance, onGenerateQRCode, onDisconnect, isLoading = false, }: WhatsAppInstanceCardProps) { const isConnected = instance.status === 'connected'; return ( {instance.instanceName} {isConnected ? 'Conectado' : 'Desconectado'} {!isConnected && ( )} {isConnected && ( )} ); } ``` ## Gerenciamento de Estado ### Estrutura de Estado ```typescript // stores/auth.ts import { createContext, useContext, useState, useEffect } from 'react'; import { User } from '@supabase/supabase-js'; import { supabase } from '@/lib/supabase'; interface AuthContextType { user: User | null; loading: boolean; signOut: () => Promise; } const AuthContext = createContext(undefined); export function AuthProvider({ children }: { children: React.ReactNode }) { const [user, setUser] = useState(null); const [loading, setLoading] = useState(true); useEffect(() => { supabase.auth.getSession().then(({ data: { session } }) => { setUser(session?.user ?? null); setLoading(false); }); const { data: { subscription } } = supabase.auth.onAuthStateChange((_event, session) => { setUser(session?.user ?? null); }); return () => subscription.unsubscribe(); }, []); const signOut = async () => { await supabase.auth.signOut(); setUser(null); }; return ( {children} ); } export function useAuth() { const context = useContext(AuthContext); if (!context) throw new Error('useAuth must be used within AuthProvider'); return context; } ``` ### Padrões de Gerenciamento de Estado - **Global auth state:** React Context (`AuthContext`) para sessão - **Component-local state:** `useState` para UI state - **Server state:** Custom hooks com `fetch` e polling - **Form state:** `useState` ou React Hook Form ## Arquitetura de Roteamento ### Organização de Rotas ``` app/ ├── (auth)/ │ ├── login/page.tsx │ └── reset-password/page.tsx ├── (dashboard)/ │ ├── layout.tsx # Protected layout │ └── dashboard/page.tsx ├── api/ │ ├── whatsapp/ │ └── google-calendar/ └── layout.tsx # Root layout ``` ### Padrão de Rota Protegida ```typescript // app/(dashboard)/layout.tsx 'use client'; import { useAuth } from '@/stores/auth'; import { useRouter } from 'next/navigation'; import { useEffect } from 'react'; import { DashboardLayout } from '@/components/layout/DashboardLayout'; export default function ProtectedLayout({ children }: { children: React.ReactNode }) { const { user, loading } = useAuth(); const router = useRouter(); useEffect(() => { if (!loading && !user) { router.push('/login'); } }, [user, loading, router]); if (loading) return
Loading...
; if (!user) return null; return {children}; } ``` ## Camada de Serviços Frontend ### Setup do Cliente API ```typescript // lib/api-client.ts import { supabase } from '@/lib/supabase'; interface FetchOptions extends RequestInit { requireAuth?: boolean; } export async function apiClient( endpoint: string, options: FetchOptions = {} ): Promise { const { requireAuth = true, ...fetchOptions } = options; const headers: HeadersInit = { 'Content-Type': 'application/json', ...fetchOptions.headers, }; if (requireAuth) { const { data: { session } } = await supabase.auth.getSession(); if (!session) throw new Error('Unauthorized'); headers['Authorization'] = `Bearer ${session.access_token}`; } const res = await fetch(`${process.env.NEXT_PUBLIC_SITE_URL}${endpoint}`, { ...fetchOptions, headers, }); if (!res.ok) { const errorData = await res.json().catch(() => ({ error: { message: 'Unknown error' } })); throw new Error(errorData.error?.message || `Request failed: ${res.status}`); } return res.json(); } ``` ### Exemplo de Serviço ```typescript // services/whatsapp.service.ts import { apiClient } from '@/lib/api-client'; import { WhatsAppInstance } from '@/types/whatsapp'; export const whatsappService = { async getInstances(): Promise { return apiClient('/api/whatsapp/instances'); }, async generateQRCode(instanceName: string): Promise<{ qrCode: string }> { return apiClient<{ qrCode: string }>(`/api/whatsapp/instances/${instanceName}/qrcode`, { method: 'POST', }); }, async disconnectInstance(instanceName: string): Promise<{ success: boolean }> { return apiClient(`/api/whatsapp/instances/${instanceName}/disconnect`, { method: 'POST', }); }, }; ``` ---