fix(filtros): corrigir extração de selos e melhorar UX do filtro

- Corrigir lógica de extração de selos para usar códigos exatos
- Filtro agora exige todos os selos selecionados (AND em vez de OR)
- Botão Aplicar volta: seleção não dispara filtro automaticamente
- Layout dos controles unificado em barra com fundo
This commit is contained in:
Frederico Castro
2025-12-15 13:04:06 -03:00
parent c294d4cc77
commit 3254374486
4 changed files with 89 additions and 49 deletions

View File

@@ -48,22 +48,30 @@
.controls {
margin: 1.5rem 0;
background: linear-gradient(165deg, rgba(15, 23, 42, 0.6), rgba(30, 41, 59, 0.4));
border: 1px solid var(--stroke);
border-radius: 12px;
padding: 0.75rem 1rem;
display: flex;
gap: 1.5rem;
flex-wrap: wrap;
align-items: center;
gap: 1rem;
flex-wrap: wrap;
}
.controls label {
.control-group {
display: flex;
align-items: center;
gap: 0.5rem;
font-size: 0.9rem;
}
.control-group-label {
font-size: 0.8rem;
color: var(--muted);
white-space: nowrap;
}
.controls select {
padding: 0.5rem 1rem;
padding: 0.5rem 0.75rem;
background: rgba(255,255,255,0.06);
border: 1px solid var(--stroke);
border-radius: 8px;
@@ -81,23 +89,25 @@
display: flex;
gap: 0.5rem;
align-items: center;
flex: 1;
}
.search-box input {
padding: 0.55rem 0.8rem;
padding: 0.5rem 0.75rem;
background: rgba(255,255,255,0.06);
border: 1px solid var(--stroke);
border-radius: 8px;
color: var(--text);
min-width: 240px;
flex: 1;
}
.search-box input:focus {
outline: 1px solid var(--accent-2);
outline: none;
border-color: var(--accent-2);
}
.search-box button {
padding: 0.55rem 1rem;
padding: 0.5rem 0.9rem;
background: var(--accent);
border: 1px solid var(--accent);
color: white;
@@ -120,17 +130,17 @@
display: flex;
align-items: center;
gap: 0.5rem;
flex-wrap: wrap;
}
.pagination button {
padding: 0.6rem 0.9rem;
border-radius: 8px;
padding: 0.5rem 0.75rem;
border-radius: 6px;
border: 1px solid var(--stroke);
background: rgba(255,255,255,0.06);
color: var(--text);
cursor: pointer;
transition: all 150ms ease;
font-size: 0.9rem;
}
.pagination button:hover:not(:disabled) {
@@ -145,6 +155,20 @@
.page-info {
color: var(--muted);
font-weight: 500;
font-size: 0.9rem;
padding: 0 0.5rem;
white-space: nowrap;
}
@media (max-width: 900px) {
.controls {
gap: 0.75rem;
}
.search-box {
min-width: 100%;
order: 10;
}
}
.ranking-list {

View File

@@ -182,16 +182,16 @@ function App() {
<Header total={total} />
<div className="controls">
<label>
Limite de consultores:
<div className="control-group">
<span className="control-group-label">Exibir:</span>
<select value={pageSize} onChange={(e) => { setPageSize(Number(e.target.value)); setPage(1); }}>
<option value={10}>Top 10</option>
<option value={50}>Top 50</option>
<option value={100}>Top 100</option>
<option value={200}>Top 200</option>
<option value={500}>Top 500</option>
<option value={10}>10</option>
<option value={50}>50</option>
<option value={100}>100</option>
<option value={200}>200</option>
<option value={500}>500</option>
</select>
</label>
</div>
<FiltroSelos
selecionados={filtroSelos}
@@ -201,23 +201,21 @@ function App() {
<form className="search-box" onSubmit={handleSubmitBuscar}>
<input
type="text"
placeholder="Digite o nome para localizar"
placeholder="Buscar por nome..."
value={busca}
onChange={(e) => setBusca(e.target.value)}
/>
<button type="submit" disabled={buscando || busca.length < 3}>
{buscando ? 'Buscando...' : 'Buscar'}
{buscando ? '...' : 'Buscar'}
</button>
</form>
<div className="pagination">
<button onClick={() => setPage(1)} disabled={page <= 1}>« Primeira</button>
<button onClick={() => setPage((p) => Math.max(1, p - 1))} disabled={page <= 1}> Anterior</button>
<span className="page-info">
Página {page} de {totalPages || '?'}
</span>
<button onClick={() => setPage((p) => (totalPages ? Math.min(totalPages, p + 1) : p + 1))} disabled={totalPages && page >= totalPages}>Próxima </button>
<button onClick={() => totalPages && setPage(totalPages)} disabled={totalPages && page >= totalPages}>Última »</button>
<button onClick={() => setPage(1)} disabled={page <= 1}>«</button>
<button onClick={() => setPage((p) => Math.max(1, p - 1))} disabled={page <= 1}></button>
<span className="page-info">{page} / {totalPages || '?'}</span>
<button onClick={() => setPage((p) => (totalPages ? Math.min(totalPages, p + 1) : p + 1))} disabled={totalPages && page >= totalPages}></button>
<button onClick={() => totalPages && setPage(totalPages)} disabled={totalPages && page >= totalPages}>»</button>
</div>
</div>

View File

@@ -36,8 +36,15 @@ const SELOS_CONFIG = {
function FiltroSelos({ selecionados, onChange }) {
const [aberto, setAberto] = useState(false);
const [selosTemp, setSelosTemp] = useState([]);
const ref = useRef(null);
useEffect(() => {
if (aberto) {
setSelosTemp([...selecionados]);
}
}, [aberto, selecionados]);
useEffect(() => {
const handleClickOutside = (e) => {
if (ref.current && !ref.current.contains(e.target)) {
@@ -49,18 +56,27 @@ function FiltroSelos({ selecionados, onChange }) {
}, []);
const toggleSelo = (codigo) => {
if (selecionados.includes(codigo)) {
onChange(selecionados.filter((s) => s !== codigo));
if (selosTemp.includes(codigo)) {
setSelosTemp(selosTemp.filter((s) => s !== codigo));
} else {
onChange([...selecionados, codigo]);
setSelosTemp([...selosTemp, codigo]);
}
};
const limparTemp = () => {
setSelosTemp([]);
};
const limparFiltros = (e) => {
e.stopPropagation();
onChange([]);
};
const aplicarFiltro = () => {
onChange(selosTemp);
setAberto(false);
};
const totalSelos = Object.values(SELOS_CONFIG).reduce(
(acc, g) => acc + g.selos.length,
0
@@ -90,9 +106,9 @@ function FiltroSelos({ selecionados, onChange }) {
<div className="filtro-selos-dropdown">
<div className="filtro-selos-header">
<span>Selecione os selos para filtrar</span>
{selecionados.length > 0 && (
<button className="filtro-limpar-todos" onClick={limparFiltros}>
Limpar ({selecionados.length})
{selosTemp.length > 0 && (
<button className="filtro-limpar-todos" onClick={limparTemp}>
Limpar ({selosTemp.length})
</button>
)}
</div>
@@ -105,11 +121,11 @@ function FiltroSelos({ selecionados, onChange }) {
{grupo.selos.map((selo) => (
<label
key={selo.codigo}
className={`filtro-selo-item ${selecionados.includes(selo.codigo) ? 'selecionado' : ''}`}
className={`filtro-selo-item ${selosTemp.includes(selo.codigo) ? 'selecionado' : ''}`}
>
<input
type="checkbox"
checked={selecionados.includes(selo.codigo)}
checked={selosTemp.includes(selo.codigo)}
onChange={() => toggleSelo(selo.codigo)}
/>
<span className="selo-icone">{selo.icone}</span>
@@ -123,9 +139,9 @@ function FiltroSelos({ selecionados, onChange }) {
<div className="filtro-selos-footer">
<span className="filtro-info">
{selecionados.length} de {totalSelos} selecionado{selecionados.length !== 1 ? 's' : ''}
{selosTemp.length} de {totalSelos} selecionado{selosTemp.length !== 1 ? 's' : ''}
</span>
<button className="filtro-aplicar" onClick={() => setAberto(false)}>
<button className="filtro-aplicar" onClick={aplicarFiltro}>
Aplicar
</button>
</div>