- Sistema FOT (Formatura Operations Tracking): * Tela de Gestão FOT (/cursos) com tabela Excel-style * Modal CourseForm com 10 campos (FOT, Empresa, Instituição, etc) * Validação de FOT (5 dígitos numéricos) * Edição de turmas ao clicar na linha * Integração com API backend (empresas, níveis educacionais, universidades) - Dashboard renovado (/painel): * Tabela com 8 colunas (FOT, Data, Curso, Instituição, Ano, Empresa, Tipo, Status) * Filtros avançados: FOT (busca numérica), Data, Tipo de Evento * Removidos filtros de Estado e Cidade * Página de detalhes com tabela vertical (12 informações) * Botão Aprovar redireciona para modal de equipe - Sistema de Aprovação Dupla (/aprovacao): * 2 tabelas separadas por abas (Usuários Normais e Profissionais) * Coluna Universidade renomeada para Empresa * Coluna Função nos profissionais * Workflow de aprovação com atribuição de equipe - Cadastro Profissional (/cadastro-profissional): * Formulário específico para fotógrafos * Dropdown de Função Profissional da API * Tratamento de erro quando backend offline - Modal de Criar Evento: * Tipo de Evento como primeiro campo * Nome do Evento (Opcional) como segundo campo - Componentes novos: * EventTable.tsx - Tabela de eventos com ordenação * EventFiltersBar.tsx - Filtros avançados (3 filtros) * CourseForm.tsx - Formulário FOT completo * ProfessionalForm.tsx - Cadastro profissional - API Service: * Integração com backend Go * Endpoints: /api/empresas, /api/funcoes, /api/niveis-educacionais, /api/universidades, /graduation-years - Documentação: * README.md principal atualizado * frontend/README.md atualizado * Documentação completa de componentes e features
154 lines
5.2 KiB
TypeScript
154 lines
5.2 KiB
TypeScript
import React from "react";
|
|
import { Calendar, Hash, Filter, X } from "lucide-react";
|
|
|
|
export interface EventFilters {
|
|
date: string;
|
|
fotId: string;
|
|
type: string;
|
|
}
|
|
|
|
interface EventFiltersBarProps {
|
|
filters: EventFilters;
|
|
onFilterChange: (filters: EventFilters) => void;
|
|
availableTypes: string[];
|
|
}
|
|
|
|
export const EventFiltersBar: React.FC<EventFiltersBarProps> = ({
|
|
filters,
|
|
onFilterChange,
|
|
availableTypes,
|
|
}) => {
|
|
const handleReset = () => {
|
|
onFilterChange({
|
|
date: "",
|
|
fotId: "",
|
|
type: "",
|
|
});
|
|
};
|
|
|
|
const hasActiveFilters = Object.values(filters).some((value) => value !== "");
|
|
|
|
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-3 gap-3">
|
|
{/* Filtro por FOT */}
|
|
<div className="flex flex-col">
|
|
<label className="text-xs font-medium text-gray-600 mb-1 flex items-center gap-1">
|
|
<Hash size={14} className="text-brand-gold" />
|
|
FOT
|
|
</label>
|
|
<input
|
|
type="text"
|
|
value={filters.fotId}
|
|
onChange={(e) => {
|
|
const value = e.target.value.replace(/\D/g, '').slice(0, 5);
|
|
onFilterChange({ ...filters, fotId: value });
|
|
}}
|
|
placeholder="Buscar FOT..."
|
|
maxLength={5}
|
|
className="px-3 py-2 border border-gray-300 rounded text-sm focus:outline-none focus:border-brand-gold transition-colors"
|
|
/>
|
|
</div>
|
|
|
|
{/* 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 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.fotId && (
|
|
<span className="inline-flex items-center gap-1 px-2 py-1 bg-brand-gold/10 text-brand-gold text-xs rounded">
|
|
FOT: {filters.fotId}
|
|
<button
|
|
onClick={() => onFilterChange({ ...filters, fotId: "" })}
|
|
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>
|
|
);
|
|
};
|