feat: Ajout de filtres avancés dans gestion productions
- Filtre par Déclarée FTS (Toutes/Oui/Non) - Filtre par Type de prod (Tous/Spectacle vivant/Audiovisuelle/Phonographique/Administratif) - Filtre par Année (années disponibles dans les données) - Filtre par Mois (janvier à décembre) - Compteur de filtres actifs dans le badge - Réinitialisation de tous les filtres en un clic - Layout responsive en grille 4 colonnes pour les filtres
This commit is contained in:
parent
c87c11eb10
commit
d81a12de6e
1 changed files with 150 additions and 6 deletions
|
|
@ -456,6 +456,12 @@ export default function GestionProductionsPage() {
|
|||
const [isCreateModalOpen, setIsCreateModalOpen] = useState(false);
|
||||
const [sortColumn, setSortColumn] = useState<"name" | "reference" | "declaration_date" | null>("declaration_date");
|
||||
const [sortDirection, setSortDirection] = useState<"asc" | "desc">("desc");
|
||||
|
||||
// Nouveaux filtres
|
||||
const [filterDeclaredFts, setFilterDeclaredFts] = useState<"all" | "yes" | "no">("all");
|
||||
const [filterProdType, setFilterProdType] = useState<ProductionType | "all">("all");
|
||||
const [filterYear, setFilterYear] = useState<string>("all");
|
||||
const [filterMonth, setFilterMonth] = useState<string>("all");
|
||||
|
||||
const { data: staffCheck, isLoading: isLoadingStaff } = useStaffCheck();
|
||||
const { data: organizations = [], isLoading: isLoadingOrgs } = useOrganizations();
|
||||
|
|
@ -476,10 +482,22 @@ export default function GestionProductionsPage() {
|
|||
}
|
||||
};
|
||||
|
||||
// Générer les années disponibles
|
||||
const availableYears = useMemo(() => {
|
||||
const years = new Set<string>();
|
||||
productions.forEach((p) => {
|
||||
if (p.declaration_date) {
|
||||
years.add(new Date(p.declaration_date).getFullYear().toString());
|
||||
}
|
||||
});
|
||||
return Array.from(years).sort((a, b) => b.localeCompare(a)); // Plus récent en premier
|
||||
}, [productions]);
|
||||
|
||||
// Filtrage et tri
|
||||
const filteredProductions = useMemo(() => {
|
||||
let result = productions;
|
||||
|
||||
// Filtre par recherche
|
||||
if (searchQuery.trim()) {
|
||||
const query = searchQuery.toLowerCase();
|
||||
result = result.filter(
|
||||
|
|
@ -489,6 +507,41 @@ export default function GestionProductionsPage() {
|
|||
);
|
||||
}
|
||||
|
||||
// Filtre par organisation
|
||||
// (déjà géré par l'API dans useProductions)
|
||||
|
||||
// Filtre par Déclarée FTS
|
||||
if (filterDeclaredFts !== "all") {
|
||||
result = result.filter((p) => {
|
||||
if (filterDeclaredFts === "yes") return p.declared_fts === true;
|
||||
if (filterDeclaredFts === "no") return p.declared_fts === false || p.declared_fts === null;
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
// Filtre par Type de prod
|
||||
if (filterProdType !== "all") {
|
||||
result = result.filter((p) => p.prod_type === filterProdType);
|
||||
}
|
||||
|
||||
// Filtre par année
|
||||
if (filterYear !== "all") {
|
||||
result = result.filter((p) => {
|
||||
if (!p.declaration_date) return false;
|
||||
const year = new Date(p.declaration_date).getFullYear().toString();
|
||||
return year === filterYear;
|
||||
});
|
||||
}
|
||||
|
||||
// Filtre par mois
|
||||
if (filterMonth !== "all") {
|
||||
result = result.filter((p) => {
|
||||
if (!p.declaration_date) return false;
|
||||
const month = (new Date(p.declaration_date).getMonth() + 1).toString().padStart(2, "0");
|
||||
return month === filterMonth;
|
||||
});
|
||||
}
|
||||
|
||||
// Tri
|
||||
if (sortColumn) {
|
||||
result = [...result].sort((a, b) => {
|
||||
|
|
@ -513,7 +566,7 @@ export default function GestionProductionsPage() {
|
|||
}
|
||||
|
||||
return result;
|
||||
}, [productions, searchQuery, sortColumn, sortDirection]);
|
||||
}, [productions, searchQuery, sortColumn, sortDirection, filterDeclaredFts, filterProdType, filterYear, filterMonth]);
|
||||
|
||||
// Vérification staff
|
||||
if (isLoadingStaff) {
|
||||
|
|
@ -533,7 +586,14 @@ export default function GestionProductionsPage() {
|
|||
);
|
||||
}
|
||||
|
||||
const hasActiveFilters = selectedOrgId || searchQuery;
|
||||
const hasActiveFilters = selectedOrgId || searchQuery || filterDeclaredFts !== "all" || filterProdType !== "all" || filterYear !== "all" || filterMonth !== "all";
|
||||
const activeFiltersCount = [
|
||||
selectedOrgId,
|
||||
filterDeclaredFts !== "all",
|
||||
filterProdType !== "all",
|
||||
filterYear !== "all",
|
||||
filterMonth !== "all"
|
||||
].filter(Boolean).length;
|
||||
|
||||
return (
|
||||
<div className="space-y-5 p-6">
|
||||
|
|
@ -587,9 +647,9 @@ export default function GestionProductionsPage() {
|
|||
>
|
||||
<Filter className="w-4 h-4" />
|
||||
Filtres
|
||||
{hasActiveFilters && (
|
||||
{activeFiltersCount > 0 && (
|
||||
<span className="px-2 py-0.5 bg-indigo-600 text-white text-xs rounded-full">
|
||||
{selectedOrgId ? 1 : 0}
|
||||
{activeFiltersCount}
|
||||
</span>
|
||||
)}
|
||||
</button>
|
||||
|
|
@ -600,6 +660,10 @@ export default function GestionProductionsPage() {
|
|||
onClick={() => {
|
||||
setSelectedOrgId("");
|
||||
setSearchQuery("");
|
||||
setFilterDeclaredFts("all");
|
||||
setFilterProdType("all");
|
||||
setFilterYear("all");
|
||||
setFilterMonth("all");
|
||||
}}
|
||||
className="inline-flex items-center gap-1 text-sm text-slate-600 hover:text-slate-900 transition-colors"
|
||||
>
|
||||
|
|
@ -611,7 +675,8 @@ export default function GestionProductionsPage() {
|
|||
|
||||
{showFilters && (
|
||||
<div className="mt-4 pt-4 border-t">
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4">
|
||||
{/* Organisation */}
|
||||
<div className="space-y-2">
|
||||
<label className="text-sm font-medium text-slate-700">
|
||||
Organisation
|
||||
|
|
@ -621,7 +686,7 @@ export default function GestionProductionsPage() {
|
|||
onChange={(e) => setSelectedOrgId(e.target.value)}
|
||||
className="w-full px-3 py-2 border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-transparent"
|
||||
>
|
||||
<option value="">Toutes les organisations</option>
|
||||
<option value="">Toutes</option>
|
||||
{organizations.map((org) => (
|
||||
<option key={org.id} value={org.id}>
|
||||
{org.name}
|
||||
|
|
@ -629,6 +694,85 @@ export default function GestionProductionsPage() {
|
|||
))}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
{/* Déclarée FTS */}
|
||||
<div className="space-y-2">
|
||||
<label className="text-sm font-medium text-slate-700">
|
||||
Déclarée FTS
|
||||
</label>
|
||||
<select
|
||||
value={filterDeclaredFts}
|
||||
onChange={(e) => setFilterDeclaredFts(e.target.value as "all" | "yes" | "no")}
|
||||
className="w-full px-3 py-2 border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-transparent"
|
||||
>
|
||||
<option value="all">Toutes</option>
|
||||
<option value="yes">Oui</option>
|
||||
<option value="no">Non</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
{/* Type de prod */}
|
||||
<div className="space-y-2">
|
||||
<label className="text-sm font-medium text-slate-700">
|
||||
Type de prod
|
||||
</label>
|
||||
<select
|
||||
value={filterProdType}
|
||||
onChange={(e) => setFilterProdType(e.target.value as ProductionType | "all")}
|
||||
className="w-full px-3 py-2 border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-transparent"
|
||||
>
|
||||
<option value="all">Tous</option>
|
||||
<option value="Spectacle vivant">Spectacle vivant</option>
|
||||
<option value="Production audiovisuelle">Production audiovisuelle</option>
|
||||
<option value="Production phonographique">Production phonographique</option>
|
||||
<option value="Administratif">Administratif</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
{/* Année */}
|
||||
<div className="space-y-2">
|
||||
<label className="text-sm font-medium text-slate-700">
|
||||
Année
|
||||
</label>
|
||||
<select
|
||||
value={filterYear}
|
||||
onChange={(e) => setFilterYear(e.target.value)}
|
||||
className="w-full px-3 py-2 border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-transparent"
|
||||
>
|
||||
<option value="all">Toutes</option>
|
||||
{availableYears.map((year) => (
|
||||
<option key={year} value={year}>
|
||||
{year}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
{/* Mois */}
|
||||
<div className="space-y-2">
|
||||
<label className="text-sm font-medium text-slate-700">
|
||||
Mois
|
||||
</label>
|
||||
<select
|
||||
value={filterMonth}
|
||||
onChange={(e) => setFilterMonth(e.target.value)}
|
||||
className="w-full px-3 py-2 border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-transparent"
|
||||
>
|
||||
<option value="all">Tous</option>
|
||||
<option value="01">Janvier</option>
|
||||
<option value="02">Février</option>
|
||||
<option value="03">Mars</option>
|
||||
<option value="04">Avril</option>
|
||||
<option value="05">Mai</option>
|
||||
<option value="06">Juin</option>
|
||||
<option value="07">Juillet</option>
|
||||
<option value="08">Août</option>
|
||||
<option value="09">Septembre</option>
|
||||
<option value="10">Octobre</option>
|
||||
<option value="11">Novembre</option>
|
||||
<option value="12">Décembre</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
|
|
|||
Loading…
Reference in a new issue