Files
aggios.app/front-end-dash.aggios.app/components/layout/README.md
2025-12-17 13:36:23 -03:00

295 lines
7.8 KiB
Markdown

# Componentes de Layout Padrão
Este diretório contém componentes reutilizáveis para manter um design system consistente em todas as páginas de listagem do dashboard.
## Componentes Disponíveis
### 1. **PageHeader**
Header padrão com título, descrição e botão de ação.
```tsx
import PageHeader from '@/components/layout/PageHeader';
<PageHeader
title="Agências"
description="Gerencie seus parceiros e acompanhe o desempenho."
actionLabel="Nova Agência"
onAction={() => setModalOpen(true)}
/>
```
### 2. **SearchBar**
Barra de busca padrão com ícone de lupa.
```tsx
import SearchBar from '@/components/layout/SearchBar';
<SearchBar
value={searchTerm}
onChange={setSearchTerm}
placeholder="Buscar por nome, email..."
/>
```
### 3. **StatusFilter**
Dropdown de filtro de status com Headless UI.
```tsx
import StatusFilter from '@/components/layout/StatusFilter';
const STATUS_OPTIONS = [
{ id: 'all', name: 'Todos os Status' },
{ id: 'active', name: 'Ativos' },
{ id: 'inactive', name: 'Inativos' },
];
<StatusFilter
options={STATUS_OPTIONS}
selected={selectedStatus}
onChange={setSelectedStatus}
/>
```
### 4. **EmptyState**
Estado vazio padrão com ícone, título e descrição.
```tsx
import EmptyState from '@/components/layout/EmptyState';
import { BuildingOfficeIcon } from '@heroicons/react/24/outline';
<EmptyState
icon={<BuildingOfficeIcon className="w-8 h-8 text-zinc-400" />}
title="Nenhuma agência encontrada"
description="Não encontramos resultados para os filtros selecionados."
actionLabel="Limpar todos os filtros"
onAction={clearFilters}
/>
```
### 5. **LoadingState**
Estado de carregamento com spinner.
```tsx
import LoadingState from '@/components/layout/LoadingState';
{loading && <LoadingState />}
```
### 6. **StatusBadge**
Badge de status ativo/inativo com toggle opcional.
```tsx
import StatusBadge from '@/components/layout/StatusBadge';
<StatusBadge
active={item.is_active}
onClick={() => toggleStatus(item.id, item.is_active)}
activeLabel="Ativo"
inactiveLabel="Inativo"
/>
```
### 7. **Pagination** ⭐ NEW
Paginação funcional com navegação e indicador de páginas.
```tsx
import Pagination from '@/components/layout/Pagination';
const [currentPage, setCurrentPage] = useState(1);
const itemsPerPage = 10;
const totalItems = filteredItems.length;
const totalPages = Math.ceil(totalItems / itemsPerPage);
const paginatedItems = filteredItems.slice(
(currentPage - 1) * itemsPerPage,
currentPage * itemsPerPage
);
<Pagination
currentPage={currentPage}
totalPages={totalPages}
totalItems={totalItems}
itemsPerPage={itemsPerPage}
onPageChange={setCurrentPage}
/>
```
### 8. **ConfirmDialog** ⭐ NEW
Modal de confirmação profissional (substitui `confirm()`).
```tsx
import { useState } from 'react';
import ConfirmDialog from '@/components/layout/ConfirmDialog';
const [confirmOpen, setConfirmOpen] = useState(false);
const [itemToDelete, setItemToDelete] = useState<string | null>(null);
const handleDeleteClick = (id: string) => {
setItemToDelete(id);
setConfirmOpen(true);
};
const handleConfirmDelete = () => {
if (itemToDelete) {
// Executar exclusão
deleteItem(itemToDelete);
}
};
<ConfirmDialog
isOpen={confirmOpen}
onClose={() => setConfirmOpen(false)}
onConfirm={handleConfirmDelete}
title="Excluir Item"
message="Tem certeza que deseja excluir este item? Esta ação não pode ser desfeita."
confirmText="Excluir"
cancelText="Cancelar"
variant="danger"
/>
```
### 9. **ToastContext & useToast** ⭐ NEW
Sistema de notificações toast (substitui `alert()`).
**Setup no layout:**
```tsx
import { ToastProvider } from '@/components/layout/ToastContext';
<ToastProvider>
{children}
</ToastProvider>
```
**Uso nas páginas:**
```tsx
import { useToast } from '@/components/layout/ToastContext';
const toast = useToast();
// Sucesso
toast.success('Item criado!', 'O item foi criado com sucesso.');
// Erro
toast.error('Erro ao excluir', 'Não foi possível excluir o item.');
// Info
toast.info('Informação', 'Ação concluída.');
```
## Exemplo de Uso Completo
```tsx
"use client";
import { useState, useEffect } from 'react';
import PageHeader from '@/components/layout/PageHeader';
import SearchBar from '@/components/layout/SearchBar';
import StatusFilter from '@/components/layout/StatusFilter';
import LoadingState from '@/components/layout/LoadingState';
import EmptyState from '@/components/layout/EmptyState';
import StatusBadge from '@/components/layout/StatusBadge';
import TableFooter from '@/components/layout/TableFooter';
import { BuildingOfficeIcon } from '@heroicons/react/24/outline';
const STATUS_OPTIONS = [
{ id: 'all', name: 'Todos os Status' },
{ id: 'active', name: 'Ativos' },
{ id: 'inactive', name: 'Inativos' },
];
export default function MyListPage() {
const [items, setItems] = useState([]);
const [loading, setLoading] = useState(true);
const [searchTerm, setSearchTerm] = useState('');
const [selectedStatus, setSelectedStatus] = useState(STATUS_OPTIONS[0]);
const filteredItems = items.filter(item => {
const matchesSearch = item.name.toLowerCase().includes(searchTerm.toLowerCase());
const matchesStatus =
selectedStatus.id === 'all' ? true :
selectedStatus.id === 'active' ? item.is_active :
!item.is_active;
return matchesSearch && matchesStatus;
});
return (
<div className="p-6 max-w-[1600px] mx-auto space-y-6">
<PageHeader
title="Minha Lista"
description="Descrição da página"
actionLabel="Novo Item"
onAction={() => {}}
/>
<div className="flex flex-col lg:flex-row gap-4 items-center justify-between">
<SearchBar
value={searchTerm}
onChange={setSearchTerm}
placeholder="Buscar..."
/>
<StatusFilter
options={STATUS_OPTIONS}
selected={selectedStatus}
onChange={setSelectedStatus}
/>
</div>
{loading ? (
<LoadingState />
) : filteredItems.length === 0 ? (
<EmptyState
icon={<BuildingOfficeIcon className="w-8 h-8 text-zinc-400" />}
title="Nenhum item encontrado"
description="Tente ajustar os filtros."
/>
) : (
<div className="bg-white dark:bg-zinc-900 rounded-xl border border-zinc-200 dark:border-zinc-800 overflow-hidden">
<table className="w-full">
{/* Sua tabela aqui */}
</table>
<TableFooter count={filteredItems.length} />
</div>
)}
</div>
);
}
```
## Design System
### Cores
```css
--gradient: linear-gradient(135deg, #ff3a05, #ff0080)
--brand-color: #ff0080
```
### Classes Tailwind Padrão
**Container principal:**
```
p-6 max-w-[1600px] mx-auto space-y-6
```
**Tabela:**
```
bg-white dark:bg-zinc-900 rounded-xl border border-zinc-200 dark:border-zinc-800
```
**Header da tabela:**
```
bg-zinc-50/50 dark:bg-zinc-800/50 border-b border-zinc-200 dark:border-zinc-800
```
**Linha hover:**
```
hover:bg-zinc-50 dark:hover:bg-zinc-800/50 transition-colors
```
## Benefícios
**Consistência visual** - Todas as páginas seguem o mesmo padrão
**Manutenção fácil** - Altere um componente, atualiza em todas as páginas
**Desenvolvimento rápido** - Reutilize componentes prontos
**Design system** - Cores e estilos centralizados
**Acessibilidade** - Componentes já otimizados