Aplicação SaaS (React)¶
Visão Geral¶
Este exemplo demonstra a implementação do Complyr em uma aplicação SaaS moderna com React, incluindo autenticação de usuários, área do cliente e sincronização de preferências de consentimento no backend.
Ideal para dashboards, plataformas web, áreas de membros e aplicações que exigem login de usuários.
Características: - ✅ Identificação de usuários após login - ✅ Sincronização cross-device via email hash - ✅ Preferências persistidas no backend - ✅ Banner condicional (não exibe para usuários com preferências) - ✅ Gestão de cookies na conta do usuário - ✅ Google Tag Manager integrado - ✅ User ID tracking no GA4
Tecnologias: - React 18+ - React Router v6 - localStorage para cache local - API REST para backend - Google Tag Manager
Tempo estimado: 30 minutos
Nível: Intermediário 🟡
Pré-requisitos¶
1. Complyr¶
- ✅ Workspace criado e ativo
- ✅ Template de consentimento publicado
- ✅ Workspace ID disponível
2. Aplicação SaaS¶
- ✅ Sistema de autenticação implementado (login/registro)
- ✅ API backend para salvar preferências de usuário
- ✅ Banco de dados para armazenar consentimentos por usuário
3. Google Tag Manager (Opcional)¶
- ✅ Container GTM configurado
- ✅ GA4 Measurement ID
Arquitetura da Solução¶
flowchart TD
A[Usuário Visita App] --> B{Está Logado?}
B -->|Não| C[Banner Complyr Aparece]
B -->|Sim| D{Tem Preferências Salvas?}
C --> E[Usuário Define Consentimento]
E --> F[Salvo em localStorage]
D -->|Não| C
D -->|Sim| G[Carregar Preferências do Backend]
G --> H[Aplicar Consentimento Automaticamente]
H --> I[Banner NÃO Aparece]
F --> J[Usuário Faz Login]
J --> K[Sincronizar com Backend]
K --> L[identify email no Complyr]
L --> M[Preferências Salvas no DB] Passo 1: Instalação Base¶
1.1. Adicionar Script Complyr¶
No arquivo public/index.html ou componente principal, adicione o script Complyr:
<!-- public/index.html -->
<!DOCTYPE html>
<html lang="pt-BR">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Minha Aplicação SaaS</title>
</head>
<body>
<noscript>Você precisa habilitar JavaScript para executar esta aplicação.</noscript>
<div id="root"></div>
<!-- ✅ Complyr Script - Adicionar antes do </body> -->
<script
src="https://app.complyr.com.br/tag/js"
data-workspace-id="SEU_WORKSPACE_ID"
data-complyr-script
async
defer>
</script>
<!-- Google Tag Manager (Opcional) -->
<script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
})(window,document,'script','dataLayer','GTM-XXXXXX');</script>
<!-- End Google Tag Manager -->
</body>
</html>
Passo 2: Criar Hook Customizado useComplyr¶
Crie um hook React para gerenciar o Complyr:
// src/hooks/useComplyr.ts
import { useEffect, useState } from 'react';
interface ConsentPurposes {
essential: boolean;
analytics: boolean;
marketing: boolean;
personalization: boolean;
third_party: boolean;
}
interface ConsentData {
workspaceId: string;
consentId: string;
status: 'NONE' | 'GRANTED' | 'PARTIAL' | 'DENIED' | 'REVOKED' | 'EXPIRED';
purposes: Record<string, { granted: boolean; grantedAt: string | null }>;
createdAt: string;
expiresAt: string;
}
export function useComplyr() {
const [isLoaded, setIsLoaded] = useState(false);
const [consent, setConsent] = useState<ConsentData | null>(null);
useEffect(() => {
// Listener para quando Complyr carregar
const handleComplyrLoaded = () => {
setIsLoaded(true);
loadConsent();
};
// Listener para quando consentimento for atualizado
const handleConsentUpdated = (event: CustomEvent<ConsentPurposes>) => {
console.log('Consentimento atualizado:', event.detail);
loadConsent();
};
document.addEventListener('complyr:loaded', handleComplyrLoaded as EventListener);
document.addEventListener('consent-updated', handleConsentUpdated as EventListener);
// Se já carregou, atualizar estado
if (window.complyr) {
setIsLoaded(true);
loadConsent();
}
return () => {
document.removeEventListener('complyr:loaded', handleComplyrLoaded as EventListener);
document.removeEventListener('consent-updated', handleConsentUpdated as EventListener);
};
}, []);
const loadConsent = () => {
try {
const stored = localStorage.getItem('complyr_consent');
if (stored) {
setConsent(JSON.parse(stored));
}
} catch (error) {
console.error('Erro ao carregar consentimento:', error);
}
};
const identify = (email: string) => {
if (window.complyr) {
window.complyr.identify('email', email);
}
};
const openPreferences = () => {
if (window.complyr) {
window.complyr.openPreferences();
}
};
const revokeConsent = (reason: string) => {
if (window.complyr) {
window.complyr.revokeConsent(reason);
}
};
return {
isLoaded,
consent,
identify,
openPreferences,
revokeConsent,
};
}
// Tipos para window.complyr
declare global {
interface Window {
complyr?: {
identify: (type: string, value: string) => void;
openPreferences: () => void;
revokeConsent: (reason: string) => void;
loadPolicy: (type: string, policyId: string | null) => void;
acceptPolicy: (type: string, policyId: string | null) => void;
};
}
}
Passo 3: Sincronização com Backend¶
3.1. Criar API Service¶
// src/services/api.ts
import axios from 'axios';
const api = axios.create({
baseURL: process.env.REACT_APP_API_URL || 'http://localhost:5000',
});
// Interceptor para adicionar token JWT
api.interceptors.request.use((config) => {
const token = localStorage.getItem('auth_token');
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
});
export interface SaveConsentPreferencesDto {
analytics: boolean;
marketing: boolean;
personalization: boolean;
third_party: boolean;
}
export interface UserConsentPreferences {
userId: string;
analytics: boolean;
marketing: boolean;
personalization: boolean;
third_party: boolean;
createdAt: string;
updatedAt: string;
}
// Salvar preferências de consentimento no backend
export async function saveUserConsentPreferences(
preferences: SaveConsentPreferencesDto
): Promise<UserConsentPreferences> {
const response = await api.post('/user/consent-preferences', preferences);
return response.data;
}
// Carregar preferências de consentimento do backend
export async function getUserConsentPreferences(): Promise<UserConsentPreferences | null> {
try {
const response = await api.get('/user/consent-preferences');
return response.data;
} catch (error) {
if (axios.isAxiosError(error) && error.response?.status === 404) {
return null; // Usuário ainda não tem preferências salvas
}
throw error;
}
}
// Revogar consentimento
export async function revokeUserConsent(reason: string): Promise<void> {
await api.post('/user/revoke-consent', { reason });
}
export default api;
3.2. Implementar Endpoint no Backend (Exemplo NestJS)¶
// backend/src/user/consent-preferences.controller.ts
import { Controller, Get, Post, Body, UseGuards } from '@nestjs/common';
import { JwtAuthGuard } from '../auth/jwt-auth.guard';
import { CurrentUser } from '../auth/current-user.decorator';
interface ConsentPreferencesDto {
analytics: boolean;
marketing: boolean;
personalization: boolean;
third_party: boolean;
}
@Controller('user')
@UseGuards(JwtAuthGuard)
export class ConsentPreferencesController {
constructor(private readonly consentService: ConsentPreferencesService) {}
@Post('consent-preferences')
async savePreferences(
@CurrentUser() user: User,
@Body() dto: ConsentPreferencesDto,
) {
return this.consentService.savePreferences(user.id, dto);
}
@Get('consent-preferences')
async getPreferences(@CurrentUser() user: User) {
const preferences = await this.consentService.getPreferences(user.id);
if (!preferences) {
throw new NotFoundException('Preferences not found');
}
return preferences;
}
@Post('revoke-consent')
async revokeConsent(
@CurrentUser() user: User,
@Body('reason') reason: string,
) {
return this.consentService.revokeConsent(user.id, reason);
}
}
Tabela no Banco de Dados (PostgreSQL):
CREATE TABLE user_consent_preferences (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
analytics BOOLEAN DEFAULT false,
marketing BOOLEAN DEFAULT false,
personalization BOOLEAN DEFAULT false,
third_party BOOLEAN DEFAULT false,
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW(),
UNIQUE(user_id)
);
CREATE INDEX idx_user_consent_user_id ON user_consent_preferences(user_id);
Passo 4: Context Provider para Autenticação¶
// src/contexts/AuthContext.tsx
import React, { createContext, useContext, useState, useEffect } from 'react';
import { useComplyr } from '../hooks/useComplyr';
import { getUserConsentPreferences } from '../services/api';
interface User {
id: string;
email: string;
name: string;
}
interface AuthContextData {
user: User | null;
loading: boolean;
login: (email: string, password: string) => Promise<void>;
logout: () => void;
}
const AuthContext = createContext<AuthContextData>({} as AuthContextData);
export function AuthProvider({ children }: { children: React.ReactNode }) {
const [user, setUser] = useState<User | null>(null);
const [loading, setLoading] = useState(true);
const { identify } = useComplyr();
useEffect(() => {
// Carregar usuário do localStorage
const storedUser = localStorage.getItem('user');
if (storedUser) {
const parsedUser = JSON.parse(storedUser);
setUser(parsedUser);
// Identificar no Complyr
identify(parsedUser.email);
// Carregar preferências do backend
loadUserPreferences();
}
setLoading(false);
}, []);
const loadUserPreferences = async () => {
try {
const preferences = await getUserConsentPreferences();
if (preferences) {
// Aplicar preferências automaticamente
// O Complyr vai ler do localStorage ou do backend
console.log('Preferências carregadas:', preferences);
}
} catch (error) {
console.error('Erro ao carregar preferências:', error);
}
};
const login = async (email: string, password: string) => {
// Sua lógica de login aqui
const response = await fetch('/api/auth/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email, password }),
});
const data = await response.json();
if (data.success) {
const userData = data.user;
setUser(userData);
localStorage.setItem('user', JSON.stringify(userData));
localStorage.setItem('auth_token', data.token);
// ✅ IMPORTANTE: Identificar no Complyr após login
identify(userData.email);
// Carregar preferências do backend
await loadUserPreferences();
}
};
const logout = () => {
setUser(null);
localStorage.removeItem('user');
localStorage.removeItem('auth_token');
localStorage.removeItem('complyr_consent');
};
return (
<AuthContext.Provider value={{ user, loading, login, logout }}>
{children}
</AuthContext.Provider>
);
}
export function useAuth() {
return useContext(AuthContext);
}
Passo 5: Componente de Configurações de Privacidade¶
Crie uma página para o usuário gerenciar suas preferências:
// src/pages/PrivacySettings.tsx
import React, { useState, useEffect } from 'react';
import { useComplyr } from '../hooks/useComplyr';
import { saveUserConsentPreferences, getUserConsentPreferences, revokeUserConsent } from '../services/api';
export function PrivacySettings() {
const { consent, openPreferences, isLoaded } = useComplyr();
const [preferences, setPreferences] = useState({
analytics: false,
marketing: false,
personalization: false,
third_party: false,
});
const [loading, setLoading] = useState(true);
const [saving, setSaving] = useState(false);
useEffect(() => {
loadPreferences();
}, []);
const loadPreferences = async () => {
try {
const userPrefs = await getUserConsentPreferences();
if (userPrefs) {
setPreferences({
analytics: userPrefs.analytics,
marketing: userPrefs.marketing,
personalization: userPrefs.personalization,
third_party: userPrefs.third_party,
});
} else if (consent) {
// Fallback: usar localStorage
setPreferences({
analytics: consent.purposes.analytics?.granted || false,
marketing: consent.purposes.marketing?.granted || false,
personalization: consent.purposes.personalization?.granted || false,
third_party: consent.purposes.third_party?.granted || false,
});
}
} catch (error) {
console.error('Erro ao carregar preferências:', error);
} finally {
setLoading(false);
}
};
const handleSave = async () => {
setSaving(true);
try {
await saveUserConsentPreferences(preferences);
alert('Preferências salvas com sucesso!');
} catch (error) {
console.error('Erro ao salvar preferências:', error);
alert('Erro ao salvar preferências. Tente novamente.');
} finally {
setSaving(false);
}
};
const handleRevoke = async () => {
if (window.confirm('Deseja revogar todos os seus consentimentos?')) {
try {
await revokeUserConsent('User requested data deletion');
setPreferences({
analytics: false,
marketing: false,
personalization: false,
third_party: false,
});
alert('Consentimentos revogados com sucesso!');
} catch (error) {
console.error('Erro ao revogar consentimento:', error);
alert('Erro ao revogar consentimento. Tente novamente.');
}
}
};
if (loading) {
return <div>Carregando...</div>;
}
return (
<div className="privacy-settings">
<h1>Configurações de Privacidade</h1>
<p>Gerencie suas preferências de cookies e dados pessoais.</p>
<div className="consent-options">
{/* Essencial - sempre ativo */}
<div className="consent-item">
<div className="consent-header">
<h3>🔒 Cookies Essenciais</h3>
<span className="badge">Sempre Ativo</span>
</div>
<p>
Necessários para o funcionamento básico da aplicação (autenticação, sessão, segurança).
Não podem ser desativados.
</p>
</div>
{/* Analytics */}
<div className="consent-item">
<div className="consent-header">
<h3>📊 Analytics</h3>
<label className="toggle">
<input
type="checkbox"
checked={preferences.analytics}
onChange={(e) => setPreferences({ ...preferences, analytics: e.target.checked })}
/>
<span className="slider"></span>
</label>
</div>
<p>
Nos ajudam a entender como você usa a aplicação para melhorar a experiência.
Google Analytics, Hotjar, etc.
</p>
</div>
{/* Marketing */}
<div className="consent-item">
<div className="consent-header">
<h3>📢 Marketing</h3>
<label className="toggle">
<input
type="checkbox"
checked={preferences.marketing}
onChange={(e) => setPreferences({ ...preferences, marketing: e.target.checked })}
/>
<span className="slider"></span>
</label>
</div>
<p>
Usados para exibir anúncios relevantes e medir a eficácia de campanhas.
Facebook Pixel, Google Ads, etc.
</p>
</div>
{/* Personalization */}
<div className="consent-item">
<div className="consent-header">
<h3>🎨 Personalização</h3>
<label className="toggle">
<input
type="checkbox"
checked={preferences.personalization}
onChange={(e) => setPreferences({ ...preferences, personalization: e.target.checked })}
/>
<span className="slider"></span>
</label>
</div>
<p>
Permitem personalizar a interface e conteúdo baseado nas suas preferências.
</p>
</div>
{/* Third Party */}
<div className="consent-item">
<div className="consent-header">
<h3>🔗 Terceiros</h3>
<label className="toggle">
<input
type="checkbox"
checked={preferences.third_party}
onChange={(e) => setPreferences({ ...preferences, third_party: e.target.checked })}
/>
<span className="slider"></span>
</label>
</div>
<p>
Serviços de terceiros como chat, vídeos incorporados, mapas, etc.
</p>
</div>
</div>
{/* Botões de ação */}
<div className="actions">
<button
className="btn btn-primary"
onClick={handleSave}
disabled={saving}
>
{saving ? 'Salvando...' : 'Salvar Preferências'}
</button>
<button
className="btn btn-secondary"
onClick={openPreferences}
disabled={!isLoaded}
>
Abrir Banner Complyr
</button>
<button
className="btn btn-danger"
onClick={handleRevoke}
>
Revogar Todos os Consentimentos
</button>
</div>
{/* Informações adicionais */}
<div className="info-section">
<h3>Seus Direitos (LGPD)</h3>
<ul>
<li>Confirmação de processamento de dados</li>
<li>Acesso aos seus dados</li>
<li>Correção de dados incompletos ou desatualizados</li>
<li>Anonimização, bloqueio ou eliminação</li>
<li>Portabilidade dos dados</li>
<li>Informações sobre compartilhamento</li>
<li>Revogação de consentimento</li>
</ul>
<p>
Para exercer seus direitos, entre em contato: <a href="mailto:privacidade@suaapp.com">privacidade@suaapp.com</a>
</p>
</div>
</div>
);
}
CSS para Toggle Switches:
/* src/styles/privacy-settings.css */
.privacy-settings {
max-width: 800px;
margin: 0 auto;
padding: 40px 20px;
}
.consent-options {
display: flex;
flex-direction: column;
gap: 20px;
margin: 30px 0;
}
.consent-item {
background: #f9f9f9;
border: 1px solid #e0e0e0;
border-radius: 8px;
padding: 20px;
}
.consent-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 10px;
}
.consent-header h3 {
margin: 0;
font-size: 18px;
}
.badge {
background: #4CAF50;
color: white;
padding: 4px 12px;
border-radius: 12px;
font-size: 12px;
font-weight: 600;
}
/* Toggle Switch */
.toggle {
position: relative;
display: inline-block;
width: 50px;
height: 24px;
}
.toggle input {
opacity: 0;
width: 0;
height: 0;
}
.slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #ccc;
transition: 0.3s;
border-radius: 24px;
}
.slider:before {
position: absolute;
content: "";
height: 18px;
width: 18px;
left: 3px;
bottom: 3px;
background-color: white;
transition: 0.3s;
border-radius: 50%;
}
input:checked + .slider {
background-color: #4CAF50;
}
input:checked + .slider:before {
transform: translateX(26px);
}
/* Botões */
.actions {
display: flex;
gap: 12px;
margin-top: 30px;
}
.btn {
padding: 12px 24px;
border: none;
border-radius: 6px;
font-size: 14px;
font-weight: 600;
cursor: pointer;
transition: all 0.3s;
}
.btn-primary {
background: #4CAF50;
color: white;
}
.btn-primary:hover:not(:disabled) {
background: #45a049;
}
.btn-secondary {
background: #2196F3;
color: white;
}
.btn-secondary:hover:not(:disabled) {
background: #0b7dda;
}
.btn-danger {
background: #f44336;
color: white;
}
.btn-danger:hover {
background: #da190b;
}
.btn:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.info-section {
margin-top: 40px;
padding: 20px;
background: #fff3cd;
border-left: 4px solid #ffc107;
border-radius: 4px;
}
.info-section h3 {
margin-top: 0;
}
.info-section ul {
margin: 10px 0;
padding-left: 20px;
}
.info-section li {
margin: 8px 0;
}
Passo 6: Integração com Google Tag Manager¶
6.1. Rastrear Login¶
// src/pages/Login.tsx
import { useAuth } from '../contexts/AuthContext';
export function Login() {
const { login } = useAuth();
const handleLogin = async (email: string, password: string) => {
await login(email, password);
// Rastrear login no GTM
if (window.dataLayer) {
window.dataLayer.push({
event: 'login',
method: 'email',
user_id: email, // Será hasheado pelo Complyr
});
}
};
// ...resto do componente
}
6.2. Rastrear Eventos de Produto (se aplicável)¶
// src/components/Product.tsx
import { useComplyr } from '../hooks/useComplyr';
export function Product({ product }) {
const { consent } = useComplyr();
const trackProductView = () => {
if (consent?.purposes.analytics?.granted && window.dataLayer) {
window.dataLayer.push({
event: 'view_item',
ecommerce: {
items: [{
item_id: product.id,
item_name: product.name,
price: product.price,
}]
}
});
}
};
useEffect(() => {
trackProductView();
}, [product]);
// ...resto do componente
}
Passo 7: Banner Condicional¶
Evite exibir banner para usuários logados com preferências salvas:
// src/App.tsx
import { useEffect } from 'react';
import { useAuth } from './contexts/AuthContext';
import { getUserConsentPreferences } from './services/api';
export function App() {
const { user } = useAuth();
useEffect(() => {
checkUserPreferences();
}, [user]);
const checkUserPreferences = async () => {
if (user) {
// Usuário logado - verificar se tem preferências
const preferences = await getUserConsentPreferences();
if (preferences) {
// Usuário tem preferências salvas
// Aplicar automaticamente sem exibir banner
applyPreferencesAutomatically(preferences);
}
// Se não tiver preferências, banner aparecerá normalmente
}
};
const applyPreferencesAutomatically = (preferences: any) => {
// Simular aceite/rejeição baseado nas preferências salvas
const consentData = {
analytics: preferences.analytics,
marketing: preferences.marketing,
personalization: preferences.personalization,
third_party: preferences.third_party,
};
// Disparar evento consent-updated
document.dispatchEvent(
new CustomEvent('consent-updated', { detail: consentData })
);
// Atualizar dataLayer para GTM
if (window.dataLayer) {
window.dataLayer.push({
event: 'consent-updated',
consent_analytics: preferences.analytics,
consent_marketing: preferences.marketing,
consent_personalization: preferences.personalization,
consent_third_party: preferences.third_party,
});
}
};
return (
// ...seu app
);
}
Exemplo Completo - App.tsx¶
// src/App.tsx
import React from 'react';
import { BrowserRouter, Routes, Route, Navigate } from 'react-router-dom';
import { AuthProvider, useAuth } from './contexts/AuthContext';
import { Login } from './pages/Login';
import { Register } from './pages/Register';
import { Dashboard } from './pages/Dashboard';
import { PrivacySettings } from './pages/PrivacySettings';
function PrivateRoute({ children }: { children: React.ReactNode }) {
const { user, loading } = useAuth();
if (loading) {
return <div>Carregando...</div>;
}
return user ? <>{children}</> : <Navigate to="/login" />;
}
function App() {
return (
<AuthProvider>
<BrowserRouter>
<Routes>
{/* Rotas públicas */}
<Route path="/login" element={<Login />} />
<Route path="/register" element={<Register />} />
{/* Rotas privadas */}
<Route
path="/dashboard"
element={
<PrivateRoute>
<Dashboard />
</PrivateRoute>
}
/>
<Route
path="/settings/privacy"
element={
<PrivateRoute>
<PrivacySettings />
</PrivateRoute>
}
/>
{/* Redirect padrão */}
<Route path="/" element={<Navigate to="/dashboard" />} />
</Routes>
</BrowserRouter>
</AuthProvider>
);
}
export default App;
Testes¶
1. Testar Identificação de Usuário¶
// Console do navegador após login
console.log(localStorage.getItem('complyr_consent'));
// Deve conter emailHash
// Verificar se identify foi chamado
// DevTools → Network → Filtrar por "complyr"
// Deve haver uma requisição POST com hash do email
2. Testar Sincronização Cross-Device¶
- Dispositivo 1 (Desktop):
- Faça login
- Aceite apenas Analytics
-
Salve preferências
-
Dispositivo 2 (Mobile):
- Faça login com mesmo usuário
- Preferências devem ser carregadas automaticamente
- Banner NÃO deve aparecer
3. Testar Revogação¶
- Vá em Configurações de Privacidade
- Clique em "Revogar Todos os Consentimentos"
- Verifique:
- Preferências zeradas no backend
- localStorage atualizado
- Analytics/Marketing desabilitados
Problemas Comuns¶
Problema 1: Banner aparece para usuário logado¶
Causa: Preferências não sendo carregadas do backend.
Solução:
// Verificar se endpoint está retornando dados
const prefs = await getUserConsentPreferences();
console.log('Preferências carregadas:', prefs);
// Se null, usuário não tem preferências salvas ainda
// Banner deve aparecer normalmente
Problema 2: identify() não funciona¶
Causa: Script Complyr não carregou ou email inválido.
Solução:
// Aguardar carregamento do script
document.addEventListener('complyr:loaded', () => {
if (window.complyr) {
window.complyr.identify('email', user.email);
}
});
Problema 3: Preferências não sincronizam¶
Causa: Token JWT ausente ou inválido.
Solução:
// Verificar se token está sendo enviado
api.interceptors.request.use((config) => {
const token = localStorage.getItem('auth_token');
console.log('Token JWT:', token);
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
});
Checklist de Validação¶
- Script Complyr instalado no index.html
- Hook
useComplyrimplementado - Context
AuthProviderconfigurado - Endpoint backend
/user/consent-preferencescriado - Tabela
user_consent_preferencescriada no DB - Função
identify()chamada após login - Página de Configurações de Privacidade criada
- Toggle switches funcionam corretamente
- Salvar preferências persiste no backend
- Preferências carregadas automaticamente após login
- Banner NÃO aparece para usuários com preferências
- Revogação de consentimento funciona
- GTM integrado (se aplicável)
- User ID rastreado no GA4
- Testes em múltiplos dispositivos OK
Próximos Passos¶
- React SPA Avançado - Hooks, Context, TypeScript
- Google Tag Manager - Rastreamento avançado
- API JavaScript - Métodos disponíveis
- LGPD Compliance - Conformidade legal
Conclusão¶
Parabéns! 🎉 Você implementou uma solução completa de consentimento para aplicação SaaS com:
- ✅ Identificação de usuários
- ✅ Sincronização cross-device
- ✅ Preferências persistidas
- ✅ Gestão na conta do usuário
- ✅ Banner condicional
Agora seus usuários têm controle total sobre seus dados e você está em conformidade com LGPD!
Suporte: contato@complyr.com.br