Arquivo: src/admin/Dashboard.jsx
Linhas: 1915
Limite recomendado: 150-250 linhas por componente
Problema
O Dashboard.jsx concentra toda a logica do painel admin em um unico arquivo: gestao de eventos, contribuintes, tags, usuarios, 3 modais diferentes, 30+ estados, 3 instancias de useForm, sidebar, topbar, notificacoes, tema e autenticacao.
Isso viola os principios de:
- Single Responsibility Principle (SRP): um componente deve ter apenas uma responsabilidade
- Separation of Concerns: logica de negocio misturada com UI
- Composability: impossivel reutilizar partes isoladas
Refatoracao Proposta
Extrair em 12+ componentes e 4+ custom hooks:
src/admin/
├── Dashboard.jsx (~80 linhas - orquestrador)
├── components/
│ ├── AdminSidebar.jsx (~80 linhas)
│ ├── AdminTopbar.jsx (~30 linhas)
│ ├── StatsGrid.jsx (~50 linhas)
│ ├── EventsTable.jsx (~120 linhas)
│ ├── EventTableRow.jsx (~70 linhas)
│ ├── EventFormModal.jsx (~200 linhas)
│ ├── TagsSection.jsx (~80 linhas)
│ ├── TagFormModal.jsx (~80 linhas)
│ ├── ContributorsSection.jsx (~90 linhas)
│ ├── ContributorCard.jsx (~60 linhas)
│ ├── ContributorFormModal.jsx (~150 linhas)
│ ├── UsersManagementSection.jsx (~100 linhas)
│ ├── UserRoleRow.jsx (~70 linhas)
│ ├── Notification.jsx (~20 linhas)
│ └── ImageUpload.jsx (~60 linhas)
├── hooks/
│ ├── useEvents.js (CRUD + estado de eventos)
│ ├── useContributors.js (CRUD + estado de contribuintes)
│ ├── useTags.js (CRUD + estado de tags)
│ ├── useUsersManagement.js (gestao de roles)
│ └── useNotification.js (logica de notificacao)
└── constants/
└── index.js (DAY_NAMES, PAGE_SIZES, ESTADOS_BR)
Exemplo de Implementacao — useEvents.js
import { useState, useCallback, useMemo } from 'react'
import { getEvents, createEvent, updateEvent, deleteEvent, getEventStats, uploadEventImage } from '../../services/eventService'
import { parseDateValue } from '../utils/dateUtils'
export function useEvents({ showNotification }) {
const [eventos, setEventos] = useState([])
const [loading, setLoading] = useState(true)
const [stats, setStats] = useState({ total: 0, noturno: 0, diurno: 0 })
const loadEvents = useCallback(async () => {
try {
setLoading(true)
const [eventsData, statsData] = await Promise.all([getEvents(), getEventStats()])
setEventos(eventsData)
setStats(statsData)
} catch (error) {
showNotification('Erro ao carregar eventos', 'error')
} finally {
setLoading(false)
}
}, [showNotification])
const sortedEvents = useMemo(() => {
const today = new Date()
today.setHours(0, 0, 0, 0)
return [...eventos].sort((a, b) => {
const dateA = parseDateValue(a.data_evento)
const dateB = parseDateValue(b.data_evento)
const isAFuture = dateA && dateA >= today
const isBFuture = dateB && dateB >= today
if (isAFuture && !isBFuture) return -1
if (!isAFuture && isBFuture) return 1
if (isAFuture) return dateA - dateB
return dateB - dateA
})
}, [eventos])
const handleDeleteEvent = useCallback(async (id) => {
if (window.confirm('Tem certeza que deseja excluir este evento?')) {
try {
await deleteEvent(id)
showNotification('Evento excluido com sucesso!')
await loadEvents()
} catch {
showNotification('Erro ao excluir evento', 'error')
}
}
}, [showNotification, loadEvents])
return { eventos, loading, stats, sortedEvents, loadEvents, handleDeleteEvent }
}
Exemplo de Implementacao — EventTableRow.jsx
import { Eye, Edit2, Trash2 } from 'lucide-react'
import { parseDateValue } from '../utils/dateUtils'
import { stripRichText } from '../../utils/richText'
import BgEventos from '../../assets/eventos.png'
function EventTableRow({ evento, permissions, onEdit, onDelete }) {
const eventDate = parseDateValue(evento.data_evento)
const today = new Date()
today.setHours(0, 0, 0, 0)
const isPast = eventDate && eventDate < today
return (
<tr className={isPast ? 'event-row-past' : ''}>
<td data-label="Imagem">
<img src={evento.imagem || BgEventos} alt={evento.nome} className="event-thumbnail" />
</td>
<td data-label="Nome do Evento">
<span className="event-name">{evento.nome}</span>
{evento.descricao && (
<span className="event-desc-preview">{stripRichText(evento.descricao)}</span>
)}
{isPast && <span className="badge badge-encerrado">Encerrado</span>}
</td>
<td data-label="Data">{evento.data_evento}</td>
<td data-label="Horario">{evento.horario}</td>
<td data-label="Periodo">
<span className={`badge badge-${evento.periodo?.toLowerCase()}`}>{evento.periodo}</span>
</td>
<td data-label="Acoes">
<div className="action-buttons">
{!isPast && (
<>
<button className="btn-icon btn-view" onClick={() => window.open(evento.link, '_blank')} title="Ver evento">
<Eye size={16} />
</button>
{permissions.canEditEvents && (
<button className="btn-icon btn-edit" onClick={() => onEdit(evento)} title="Editar">
<Edit2 size={16} />
</button>
)}
</>
)}
{permissions.canDeleteEvents && (
<button className="btn-icon btn-delete" onClick={() => onDelete(evento.id)} title="Excluir">
<Trash2 size={16} />
</button>
)}
</div>
</td>
</tr>
)
}
export default EventTableRow
Arquivo:
src/admin/Dashboard.jsxLinhas: 1915
Limite recomendado: 150-250 linhas por componente
Problema
O
Dashboard.jsxconcentra toda a logica do painel admin em um unico arquivo: gestao de eventos, contribuintes, tags, usuarios, 3 modais diferentes, 30+ estados, 3 instancias deuseForm, sidebar, topbar, notificacoes, tema e autenticacao.Isso viola os principios de:
Refatoracao Proposta
Extrair em 12+ componentes e 4+ custom hooks:
Exemplo de Implementacao —
useEvents.jsExemplo de Implementacao —
EventTableRow.jsx