- Adiciona coluna de Cidade com ordenação independente na tabela de eventos - Implementa ordenação clicável em todas as colunas (nome, tipo, data, horário, cidade, local, status) - Remove dependência de estado para filtro de cidade - Adiciona exibição de curso/turma no modal de detalhes do evento - Torna campo curso/turma obrigatório no formulário de solicitação de evento - Remove campo de imagem de capa do formulário - Corrige importação de getActiveCoursesByInstitutionId no Dashboard
224 lines
8.1 KiB
TypeScript
224 lines
8.1 KiB
TypeScript
import React from 'react';
|
|
import { Calendar, MapPin, Clock, Filter, X } from 'lucide-react';
|
|
|
|
export interface EventFilters {
|
|
date: string;
|
|
city: string;
|
|
state: string;
|
|
timeRange: string;
|
|
type: string;
|
|
}
|
|
|
|
interface EventFiltersBarProps {
|
|
filters: EventFilters;
|
|
onFilterChange: (filters: EventFilters) => void;
|
|
availableCities: string[];
|
|
availableStates: string[];
|
|
availableTypes: string[];
|
|
}
|
|
|
|
export const EventFiltersBar: React.FC<EventFiltersBarProps> = ({
|
|
filters,
|
|
onFilterChange,
|
|
availableCities,
|
|
availableStates,
|
|
availableTypes,
|
|
}) => {
|
|
const handleReset = () => {
|
|
onFilterChange({
|
|
date: '',
|
|
city: '',
|
|
state: '',
|
|
timeRange: '',
|
|
type: '',
|
|
});
|
|
};
|
|
|
|
const hasActiveFilters = Object.values(filters).some(value => value !== '');
|
|
|
|
const timeRanges = [
|
|
{ value: '', label: 'Todos os horários' },
|
|
{ value: 'morning', label: 'Manhã (06:00 - 12:00)' },
|
|
{ value: 'afternoon', label: 'Tarde (12:00 - 18:00)' },
|
|
{ value: 'evening', label: 'Noite (18:00 - 23:59)' },
|
|
];
|
|
|
|
return (
|
|
<div className="bg-white rounded-lg border border-gray-200 p-4 shadow-sm">
|
|
<div className="flex items-center justify-between mb-4">
|
|
<div className="flex items-center gap-2">
|
|
<Filter size={18} className="text-brand-gold" />
|
|
<h3 className="font-semibold text-gray-800">Filtros Avançados</h3>
|
|
</div>
|
|
{hasActiveFilters && (
|
|
<button
|
|
onClick={handleReset}
|
|
className="flex items-center gap-1 text-sm text-gray-600 hover:text-brand-gold transition-colors"
|
|
>
|
|
<X size={16} />
|
|
Limpar filtros
|
|
</button>
|
|
)}
|
|
</div>
|
|
|
|
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-5 gap-3">
|
|
{/* Filtro por Data */}
|
|
<div className="flex flex-col">
|
|
<label className="text-xs font-medium text-gray-600 mb-1 flex items-center gap-1">
|
|
<Calendar size={14} className="text-brand-gold" />
|
|
Data
|
|
</label>
|
|
<input
|
|
type="date"
|
|
value={filters.date}
|
|
onChange={(e) => onFilterChange({ ...filters, date: e.target.value })}
|
|
className="px-3 py-2 border border-gray-300 rounded text-sm focus:outline-none focus:border-brand-gold transition-colors"
|
|
/>
|
|
</div>
|
|
|
|
{/* Filtro por Estado */}
|
|
<div className="flex flex-col">
|
|
<label className="text-xs font-medium text-gray-600 mb-1 flex items-center gap-1">
|
|
<MapPin size={14} className="text-brand-gold" />
|
|
Estado
|
|
</label>
|
|
<select
|
|
value={filters.state}
|
|
onChange={(e) => onFilterChange({ ...filters, state: e.target.value })}
|
|
className="px-3 py-2 border border-gray-300 rounded text-sm focus:outline-none focus:border-brand-gold transition-colors bg-white"
|
|
>
|
|
<option value="">Todos os estados</option>
|
|
{availableStates.map((state) => (
|
|
<option key={state} value={state}>
|
|
{state}
|
|
</option>
|
|
))}
|
|
</select>
|
|
</div>
|
|
|
|
{/* Filtro por Cidade */}
|
|
<div className="flex flex-col">
|
|
<label className="text-xs font-medium text-gray-600 mb-1 flex items-center gap-1">
|
|
<MapPin size={14} className="text-brand-gold" />
|
|
Cidade
|
|
</label>
|
|
<select
|
|
value={filters.city}
|
|
onChange={(e) => onFilterChange({ ...filters, city: e.target.value })}
|
|
className="px-3 py-2 border border-gray-300 rounded text-sm focus:outline-none focus:border-brand-gold transition-colors bg-white"
|
|
>
|
|
<option value="">Todas as cidades</option>
|
|
{availableCities.map((city) => (
|
|
<option key={city} value={city}>
|
|
{city}
|
|
</option>
|
|
))}
|
|
</select>
|
|
</div>
|
|
|
|
{/* Filtro por Horário */}
|
|
<div className="flex flex-col">
|
|
<label className="text-xs font-medium text-gray-600 mb-1 flex items-center gap-1">
|
|
<Clock size={14} className="text-brand-gold" />
|
|
Horário
|
|
</label>
|
|
<select
|
|
value={filters.timeRange}
|
|
onChange={(e) => onFilterChange({ ...filters, timeRange: e.target.value })}
|
|
className="px-3 py-2 border border-gray-300 rounded text-sm focus:outline-none focus:border-brand-gold transition-colors bg-white"
|
|
>
|
|
{timeRanges.map((range) => (
|
|
<option key={range.value} value={range.value}>
|
|
{range.label}
|
|
</option>
|
|
))}
|
|
</select>
|
|
</div>
|
|
|
|
{/* Filtro por Tipo */}
|
|
<div className="flex flex-col">
|
|
<label className="text-xs font-medium text-gray-600 mb-1 flex items-center gap-1">
|
|
<Filter size={14} className="text-brand-gold" />
|
|
Tipo de Evento
|
|
</label>
|
|
<select
|
|
value={filters.type}
|
|
onChange={(e) => onFilterChange({ ...filters, type: e.target.value })}
|
|
className="px-3 py-2 border border-gray-300 rounded text-sm focus:outline-none focus:border-brand-gold transition-colors bg-white"
|
|
>
|
|
<option value="">Todos os tipos</option>
|
|
{availableTypes.map((type) => (
|
|
<option key={type} value={type}>
|
|
{type}
|
|
</option>
|
|
))}
|
|
</select>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Active Filters Display */}
|
|
{hasActiveFilters && (
|
|
<div className="mt-3 pt-3 border-t border-gray-100">
|
|
<div className="flex flex-wrap gap-2">
|
|
<span className="text-xs text-gray-500">Filtros ativos:</span>
|
|
{filters.date && (
|
|
<span className="inline-flex items-center gap-1 px-2 py-1 bg-brand-gold/10 text-brand-gold text-xs rounded">
|
|
Data: {new Date(filters.date + 'T00:00:00').toLocaleDateString('pt-BR')}
|
|
<button
|
|
onClick={() => onFilterChange({ ...filters, date: '' })}
|
|
className="hover:text-brand-black"
|
|
>
|
|
<X size={12} />
|
|
</button>
|
|
</span>
|
|
)}
|
|
{filters.state && (
|
|
<span className="inline-flex items-center gap-1 px-2 py-1 bg-brand-gold/10 text-brand-gold text-xs rounded">
|
|
Estado: {filters.state}
|
|
<button
|
|
onClick={() => onFilterChange({ ...filters, state: '', city: '' })}
|
|
className="hover:text-brand-black"
|
|
>
|
|
<X size={12} />
|
|
</button>
|
|
</span>
|
|
)}
|
|
{filters.city && (
|
|
<span className="inline-flex items-center gap-1 px-2 py-1 bg-brand-gold/10 text-brand-gold text-xs rounded">
|
|
Cidade: {filters.city}
|
|
<button
|
|
onClick={() => onFilterChange({ ...filters, city: '' })}
|
|
className="hover:text-brand-black"
|
|
>
|
|
<X size={12} />
|
|
</button>
|
|
</span>
|
|
)}
|
|
{filters.timeRange && (
|
|
<span className="inline-flex items-center gap-1 px-2 py-1 bg-brand-gold/10 text-brand-gold text-xs rounded">
|
|
{timeRanges.find(r => r.value === filters.timeRange)?.label}
|
|
<button
|
|
onClick={() => onFilterChange({ ...filters, timeRange: '' })}
|
|
className="hover:text-brand-black"
|
|
>
|
|
<X size={12} />
|
|
</button>
|
|
</span>
|
|
)}
|
|
{filters.type && (
|
|
<span className="inline-flex items-center gap-1 px-2 py-1 bg-brand-gold/10 text-brand-gold text-xs rounded">
|
|
Tipo: {filters.type}
|
|
<button
|
|
onClick={() => onFilterChange({ ...filters, type: '' })}
|
|
className="hover:text-brand-black"
|
|
>
|
|
<X size={12} />
|
|
</button>
|
|
</span>
|
|
)}
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
};
|