/** * NutritionLabel, visão rápida no estilo Apple Privacy Nutrition Labels * ===================================================================== * Skill: lgpd-compliance (Parte 3) * Cole em: components/transparencia/NutritionLabel.tsx * * Renderiza grid 3×N com buckets: * - Dados Não Vinculados a Você (anônimos) * - Dados Vinculados a Você (PII) * - Dados Sensíveis LGPD (art. 11) * * Cada bucket lista as categorias que o app coleta. Se vazio, mostra * checkmark verde "Nada coletado aqui", diferencial competitivo. */ 'use client'; import { CheckCircle2, ChevronRight } from 'lucide-react'; import Link from 'next/link'; // ========================================================================= // Tipos (espelham o schema do inventario.yaml) // ========================================================================= type SensibilidadeNivel = 'baixa' | 'media' | 'alta' | 'critica' | 'variavel' | 'alta_inferencia'; type CategoriaItem = { nome_publico: string; finalidade: string[]; base_legal_lgpd: string; }; type Categoria = { id: string; nome_publico: string; sensibilidade: SensibilidadeNivel; pii?: boolean; aplicavel?: boolean; itens: CategoriaItem[]; declaracao_publica?: string; }; type Inventario = { meta: { versao: string; data_atualizacao: string }; categorias: Categoria[]; }; // ========================================================================= // Classificação por bucket // ========================================================================= function classificarBucket(c: Categoria): 'nao_vinculado' | 'vinculado' | 'sensivel' | null { // Sensíveis (art. 11) sempre no bucket próprio if (c.id === 'sensiveis') return c.aplicavel === false ? 'sensivel' : 'sensivel'; // Identificáveis = vinculado if (c.pii === true) return 'vinculado'; // Anônimos = não vinculado return 'nao_vinculado'; } const ICONES_POR_CATEGORIA: Record = { identidade: '👤', autenticacao: '🔑', financeiros: '💳', identificadores_tecnicos: '🖥️', geolocalizacao: '📍', midia_arquivos: '🖼️', ugc: '✍️', comportamentais: '📊', sensiveis: '⚠️', inferidos: '🧮', metadados_comunicacao: '💬', terceiros: '👥', }; const CORES_SENSIBILIDADE: Record = { baixa: 'text-emerald-600 dark:text-emerald-400', media: 'text-amber-600 dark:text-amber-400', alta: 'text-orange-600 dark:text-orange-400', critica: 'text-red-600 dark:text-red-400', variavel: 'text-zinc-500', alta_inferencia: 'text-orange-600 dark:text-orange-400', }; // ========================================================================= // Componente // ========================================================================= export function NutritionLabel({ inventario }: { inventario: Inventario }) { const buckets = { sensivel: [] as Categoria[], vinculado: [] as Categoria[], nao_vinculado: [] as Categoria[], }; for (const c of inventario.categorias) { const b = classificarBucket(c); if (b) buckets[b].push(c); } return (
); } // ========================================================================= // Subcomponente: Bucket // ========================================================================= function Bucket({ titulo, descricao, categorias, corHeader, }: { titulo: string; descricao: string; categorias: Categoria[]; corHeader: string; }) { const aplicaveis = categorias.filter((c) => c.aplicavel !== false && c.itens.length > 0); const naoAplicaveis = categorias.filter((c) => c.aplicavel === false || c.itens.length === 0); return (

{titulo}

{descricao}

{aplicaveis.length === 0 && naoAplicaveis.every((c) => c.aplicavel === false) && (

Nada coletado aqui

Não tratamos nenhum dado dessa categoria.

)} {aplicaveis.map((c) => ( ))} {naoAplicaveis.length > 0 && aplicaveis.length > 0 && (

Não coletados:

{naoAplicaveis.map((c) => (
{c.nome_publico}
))}
)}
); } // ========================================================================= // Subcomponente: CategoriaRow // ========================================================================= function CategoriaRow({ categoria }: { categoria: Categoria }) { const icone = ICONES_POR_CATEGORIA[categoria.id] ?? '📦'; const corSens = CORES_SENSIBILIDADE[categoria.sensibilidade]; return ( {icone}

{categoria.nome_publico}

{categoria.itens.length} item{categoria.itens.length !== 1 ? 's' : ''} ·{' '} sensibilidade {categoria.sensibilidade}

); }