Frontend : filtres

This commit is contained in:
2025-12-22 15:53:26 +01:00
parent b0ba1e886c
commit 6c47f0760e
5 changed files with 351 additions and 10 deletions

View File

@@ -0,0 +1,248 @@
"use client"
import { useState, useEffect } from "react"
import type { FilterParams } from "@/lib/types"
interface FilterPanelProps {
filters: FilterParams
onFiltersChange: (filters: FilterParams) => void
availableGenres: string[]
availableMoods: string[]
availableInstruments: string[]
availableKeys: string[]
}
export default function FilterPanel({
filters,
onFiltersChange,
availableGenres,
availableMoods,
availableInstruments,
availableKeys,
}: FilterPanelProps) {
const [localFilters, setLocalFilters] = useState<FilterParams>(filters)
// Update local filters when parent changes
useEffect(() => {
setLocalFilters(filters)
}, [filters])
const handleFilterChange = (key: keyof FilterParams, value: any) => {
const newFilters = { ...localFilters, [key]: value }
setLocalFilters(newFilters)
onFiltersChange(newFilters)
}
const clearFilters = () => {
const emptyFilters: FilterParams = {}
setLocalFilters(emptyFilters)
onFiltersChange(emptyFilters)
}
const hasActiveFilters = Object.keys(localFilters).length > 0
return (
<div className="bg-white rounded-lg shadow p-4 mb-6">
<div className="flex justify-between items-center mb-4">
<h2 className="text-lg font-semibold text-gray-900">Filters</h2>
{hasActiveFilters && (
<button
onClick={clearFilters}
className="text-sm text-blue-600 hover:text-blue-800"
>
Clear all
</button>
)}
</div>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
{/* Genre Filter */}
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
Genre
</label>
<select
value={localFilters.genre || ""}
onChange={(e) => handleFilterChange("genre", e.target.value || undefined)}
className="w-full px-3 py-2 border border-gray-300 rounded-md text-sm focus:outline-none focus:ring-2 focus:ring-blue-500"
>
<option value="">All Genres</option>
{availableGenres.map((genre) => (
<option key={genre} value={genre}>
{genre}
</option>
))}
</select>
</div>
{/* Mood Filter */}
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
Mood
</label>
<select
value={localFilters.mood || ""}
onChange={(e) => handleFilterChange("mood", e.target.value || undefined)}
className="w-full px-3 py-2 border border-gray-300 rounded-md text-sm focus:outline-none focus:ring-2 focus:ring-blue-500"
>
<option value="">All Moods</option>
{availableMoods.map((mood) => (
<option key={mood} value={mood}>
{mood}
</option>
))}
</select>
</div>
{/* Instrument Filter */}
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
Instrument
</label>
<select
value={localFilters.instrument || ""}
onChange={(e) => handleFilterChange("instrument", e.target.value || undefined)}
className="w-full px-3 py-2 border border-gray-300 rounded-md text-sm focus:outline-none focus:ring-2 focus:ring-blue-500"
>
<option value="">All Instruments</option>
{availableInstruments.map((instrument) => (
<option key={instrument} value={instrument}>
{instrument}
</option>
))}
</select>
</div>
{/* Key Filter */}
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
Key
</label>
<select
value={localFilters.key || ""}
onChange={(e) => handleFilterChange("key", e.target.value || undefined)}
className="w-full px-3 py-2 border border-gray-300 rounded-md text-sm focus:outline-none focus:ring-2 focus:ring-blue-500"
>
<option value="">All Keys</option>
{availableKeys.map((key) => (
<option key={key} value={key}>
{key}
</option>
))}
</select>
</div>
{/* Tempo Range Filter */}
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
Tempo
</label>
<select
value={localFilters.tempo_range || ""}
onChange={(e) => handleFilterChange("tempo_range", e.target.value || undefined)}
className="w-full px-3 py-2 border border-gray-300 rounded-md text-sm focus:outline-none focus:ring-2 focus:ring-blue-500"
>
<option value="">All Tempos</option>
<option value="slow">Slow (&lt;100 BPM)</option>
<option value="medium">Medium (100-140 BPM)</option>
<option value="fast">Fast (&gt;140 BPM)</option>
</select>
</div>
{/* Sort By */}
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
Sort By
</label>
<select
value={localFilters.sort_by || "analyzed_at"}
onChange={(e) => handleFilterChange("sort_by", e.target.value as any)}
className="w-full px-3 py-2 border border-gray-300 rounded-md text-sm focus:outline-none focus:ring-2 focus:ring-blue-500"
>
<option value="analyzed_at">Date Added</option>
<option value="filename">Filename</option>
<option value="tempo_bpm">BPM</option>
<option value="duration_seconds">Duration</option>
<option value="energy">Energy</option>
</select>
</div>
{/* Sort Order */}
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
Order
</label>
<select
value={localFilters.sort_desc ? "desc" : "asc"}
onChange={(e) => handleFilterChange("sort_desc", e.target.value === "desc")}
className="w-full px-3 py-2 border border-gray-300 rounded-md text-sm focus:outline-none focus:ring-2 focus:ring-blue-500"
>
<option value="asc">Ascending</option>
<option value="desc">Descending</option>
</select>
</div>
</div>
{/* Active Filters Display */}
{hasActiveFilters && (
<div className="mt-4 flex flex-wrap gap-2">
{localFilters.genre && (
<span className="inline-flex items-center px-2 py-1 rounded text-xs bg-blue-100 text-blue-800">
Genre: {localFilters.genre}
<button
onClick={() => handleFilterChange("genre", undefined)}
className="ml-1 hover:text-blue-900"
>
×
</button>
</span>
)}
{localFilters.mood && (
<span className="inline-flex items-center px-2 py-1 rounded text-xs bg-purple-100 text-purple-800">
Mood: {localFilters.mood}
<button
onClick={() => handleFilterChange("mood", undefined)}
className="ml-1 hover:text-purple-900"
>
×
</button>
</span>
)}
{localFilters.instrument && (
<span className="inline-flex items-center px-2 py-1 rounded text-xs bg-green-100 text-green-800">
Instrument: {localFilters.instrument}
<button
onClick={() => handleFilterChange("instrument", undefined)}
className="ml-1 hover:text-green-900"
>
×
</button>
</span>
)}
{localFilters.key && (
<span className="inline-flex items-center px-2 py-1 rounded text-xs bg-yellow-100 text-yellow-800">
Key: {localFilters.key}
<button
onClick={() => handleFilterChange("key", undefined)}
className="ml-1 hover:text-yellow-900"
>
×
</button>
</span>
)}
{localFilters.tempo_range && (
<span className="inline-flex items-center px-2 py-1 rounded text-xs bg-orange-100 text-orange-800">
Tempo: {localFilters.tempo_range}
<button
onClick={() => handleFilterChange("tempo_range", undefined)}
className="ml-1 hover:text-orange-900"
>
×
</button>
</span>
)}
</div>
)}
</div>
)
}