[FIX] Filtrage des partitions #2 Il faut un champ filtre #3

This commit is contained in:
NADAL Jean-Baptiste
2026-02-26 10:52:21 +01:00
parent 38bfe62eec
commit 2d585aa834

View File

@@ -2,11 +2,12 @@
import { onMount } from 'svelte'; import { onMount } from 'svelte';
import { apiService, type Score } from '$lib/api'; import { apiService, type Score } from '$lib/api';
let scores: Score[] = []; let scores = $state<Score[]>([]);
let loading = true; let loading = $state(true);
let error = ''; let error = $state('');
let sortBy: 'id' | 'name' | 'compositor' = 'id'; let sortBy: 'id' | 'name' | 'compositor' = $state('id');
let sortOrder: 'asc' | 'desc' = 'asc'; let sortOrder: 'asc' | 'desc' = $state('desc');
let filter: string = $state('');
onMount(async () => { onMount(async () => {
try { try {
@@ -28,16 +29,37 @@
} }
} }
$: sortedScores = [...scores].sort((a, b) => { function removeAccents(str: string): string {
let aVal = a[sortBy] || ''; return str.normalize('NFD').replace(/[\u0300-\u036f]/g, '');
let bVal = b[sortBy] || ''; }
let cmp: number;
if (sortBy === 'id') { let filteredScores = $derived.by(() => {
cmp = parseInt(a.id) - parseInt(b.id); let result = [...scores];
} else {
cmp = aVal.localeCompare(bVal); // Filter
if (filter.trim()) {
const f = removeAccents(filter.toLowerCase());
result = result.filter(s =>
removeAccents(s.id.toLowerCase()).includes(f) ||
removeAccents(s.name.toLowerCase()).includes(f) ||
removeAccents(s.compositor || '').toLowerCase().includes(f)
);
} }
return sortOrder === 'asc' ? cmp : -cmp;
// Sort
result.sort((a, b) => {
let aVal = a[sortBy] || '';
let bVal = b[sortBy] || '';
let cmp: number;
if (sortBy === 'id') {
cmp = parseInt(a.id) - parseInt(b.id);
} else {
cmp = String(aVal).localeCompare(String(bVal));
}
return sortOrder === 'asc' ? cmp : -cmp;
});
return result;
}); });
</script> </script>
@@ -48,7 +70,7 @@
<div class="container mx-auto px-4 py-8"> <div class="container mx-auto px-4 py-8">
<div class="mb-6"> <div class="mb-6">
<h1 class="text-3xl font-bold text-ohmj-primary">Répertoire</h1> <h1 class="text-3xl font-bold text-ohmj-primary">Répertoire</h1>
<p class="text-gray-600 mt-2">{scores.length} partitions disponibles</p> <p class="text-gray-600 mt-2">{scores.length} partition{scores.length !== 1 ? 's' : ''} disponible{scores.length !== 1 ? 's' : ''}</p>
</div> </div>
{#if loading} {#if loading}
@@ -60,32 +82,76 @@
{error} {error}
</div> </div>
{:else} {:else}
<!-- Filter & Sort Bar -->
<div class="bg-white rounded-lg shadow-sm p-4 mb-4">
<div class="flex flex-col md:flex-row gap-4">
<!-- Search Input -->
<div class="flex-1">
<div class="relative">
<svg class="absolute left-3 top-1/2 -translate-y-1/2 w-5 h-5 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
</svg>
<input
type="text"
bind:value={filter}
placeholder="Rechercher par n°, nom ou compositeur..."
class="w-full pl-10 pr-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-ohmj-primary focus:border-ohmj-primary transition-colors"
/>
</div>
</div>
<!-- Sort Controls -->
<div class="flex items-center gap-2">
<span class="text-sm text-gray-500 whitespace-nowrap">Trier par :</span>
<div class="flex rounded-lg border border-gray-300 overflow-hidden">
<button
type="button"
onclick={() => sortScores('id')}
class="px-3 py-2 text-sm font-medium transition-colors {sortBy === 'id' ? 'bg-ohmj-primary text-white' : 'bg-white text-gray-700 hover:bg-gray-50'}"
>
{sortBy === 'id' ? (sortOrder === 'asc' ? '↑' : '↓') : ''}
</button>
<button
type="button"
onclick={() => sortScores('name')}
class="px-3 py-2 text-sm font-medium transition-colors {sortBy === 'name' ? 'bg-ohmj-primary text-white' : 'bg-white text-gray-700 hover:bg-gray-50'}"
>
Nom {sortBy === 'name' ? (sortOrder === 'asc' ? '↑' : '↓') : ''}
</button>
<button
type="button"
onclick={() => sortScores('compositor')}
class="px-3 py-2 text-sm font-medium transition-colors {sortBy === 'compositor' ? 'bg-ohmj-primary text-white' : 'bg-white text-gray-700 hover:bg-gray-50'}"
>
Compositeur {sortBy === 'compositor' ? (sortOrder === 'asc' ? '↑' : '↓') : ''}
</button>
</div>
</div>
</div>
{#if filter}
<p class="text-sm text-gray-500 mt-2">{filteredScores.length} résultat{filteredScores.length !== 1 ? 's' : ''} pour "{filter}"</p>
{/if}
</div>
<!-- Results Table -->
<div class="bg-white rounded-lg shadow overflow-hidden"> <div class="bg-white rounded-lg shadow overflow-hidden">
<table class="min-w-full"> <table class="min-w-full">
<thead class="bg-gray-50"> <thead class="bg-gray-50">
<tr> <tr>
<th <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider w-20">
class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider cursor-pointer hover:bg-gray-100 w-20"
onclick={() => sortScores('id')}
>
{sortBy === 'id' ? (sortOrder === 'asc' ? '↑' : '↓') : ''}
</th> </th>
<th <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider cursor-pointer hover:bg-gray-100" Nom
onclick={() => sortScores('name')}
>
Nom {sortBy === 'name' ? (sortOrder === 'asc' ? '↑' : '↓') : ''}
</th> </th>
<th <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider cursor-pointer hover:bg-gray-100" Compositeur
onclick={() => sortScores('compositor')}
>
Compositeur {sortBy === 'compositor' ? (sortOrder === 'asc' ? '↑' : '↓') : ''}
</th> </th>
</tr> </tr>
</thead> </thead>
<tbody class="divide-y divide-gray-200"> <tbody class="divide-y divide-gray-200">
{#each sortedScores as score} {#each filteredScores as score}
<tr class="hover:bg-ohmj-light transition-colors cursor-pointer" onclick={() => window.location.href = '/scores/' + score.id}> <tr class="hover:bg-ohmj-light transition-colors cursor-pointer" onclick={() => window.location.href = '/scores/' + score.id}>
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900"> <td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">
{score.id} {score.id}
@@ -100,6 +166,12 @@
{/each} {/each}
</tbody> </tbody>
</table> </table>
{#if filteredScores.length === 0}
<div class="text-center py-12 text-gray-500">
Aucune partition ne correspond à votre recherche
</div>
{/if}
</div> </div>
{/if} {/if}
</div> </div>