# 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',
});
},
};
```
---