From 2ae9535009a54889bcd1ec8a9d973b78a67ed774 Mon Sep 17 00:00:00 2001 From: senke Date: Fri, 16 Jan 2026 12:44:19 +0100 Subject: [PATCH] edge-cases: improve empty state handling in LibraryPage (Edge 1.1, 1.3) - Replaced custom empty state div with EmptyState component - Added EmptyState import from @/components/ui/empty-state - Improved empty state UI with icon, title, description, and action button - Different messages for empty list vs empty search results - Added upload action button when no tracks (not in search mode) - Fixed pre-existing errors: added useInfiniteQuery import, fixed tracksData reference - Edge 1.4: Marked as complete (EmptyState component already exists) - Edge 1.1 & 1.3: In progress - improved empty state handling --- EXHAUSTIVE_TODO_LIST.md | 13 ++++-- .../features/library/pages/LibraryPage.tsx | 44 +++++++++++-------- 2 files changed, 34 insertions(+), 23 deletions(-) diff --git a/EXHAUSTIVE_TODO_LIST.md b/EXHAUSTIVE_TODO_LIST.md index b44151dd9..894342aae 100644 --- a/EXHAUSTIVE_TODO_LIST.md +++ b/EXHAUSTIVE_TODO_LIST.md @@ -4590,12 +4590,17 @@ If any task fails: - **Validation**: Empty search shows helpful message - **Rollback**: Restore original empty state -- [ ] **Edge 1.4**: Create reusable EmptyState component +- [x] **Edge 1.4**: Create reusable EmptyState component - **Scope**: `apps/web/src/components/ui/EmptyState.tsx` (create) - Reusable empty state component - - **Dependencies**: None + - **Dependencies**: None ✅ - **Risk**: LOW 🔒 - - **Validation**: Component works, reusable - - **Rollback**: Delete component + - **Validation**: ✅ Component already exists at `apps/web/src/components/ui/empty-state.tsx`: + - Fully functional EmptyState component with icon, title, description, action support + - Supports size variants (sm, md, lg) + - Uses Kodo design system (Card, Button components) + - Has test file: `empty-state.test.tsx` + - **Result**: Reusable EmptyState component available for use + - **Rollback**: N/A (already exists) - [ ] **Edge 1.5**: Use EmptyState component everywhere - **Scope**: All empty states - Use EmptyState component diff --git a/apps/web/src/features/library/pages/LibraryPage.tsx b/apps/web/src/features/library/pages/LibraryPage.tsx index 88f37d3b2..85a25e6d5 100644 --- a/apps/web/src/features/library/pages/LibraryPage.tsx +++ b/apps/web/src/features/library/pages/LibraryPage.tsx @@ -1,7 +1,7 @@ import { useState, useEffect, useRef } from 'react'; import { useSearchParams } from 'react-router-dom'; import { useDebounce } from '@/hooks/useDebounce'; -import { useQuery, useQueryClient } from '@tanstack/react-query'; +import { useQuery, useQueryClient, useInfiniteQuery } from '@tanstack/react-query'; import { usePlaylists, useAddTrackToPlaylist, @@ -57,6 +57,7 @@ import { Sidebar } from '@/components/ui/Sidebar'; import { BulkModeBanner } from '@/components/BulkModeBanner'; import { Tooltip } from '@/components/ui/tooltip'; import { AdvancedFilters } from '@/components/AdvancedFilters'; +import { EmptyState } from '@/components/ui/empty-state'; import { logger } from '@/utils/logger'; import { parseApiError } from '@/utils/apiErrorHandler'; import { cn } from '@/lib/utils'; @@ -135,7 +136,8 @@ export default function LibraryPagePremium() { // Action 2.2.1.1: Remove client-side filtering - backend handles all filters // Backend returns filtered results, no need for client-side filtering - const filteredTracks: Track[] = tracksData?.tracks || []; + const filteredTracks: Track[] = + tracksInfiniteData?.pages.flatMap((page) => page.tracks || []) || []; // Action 2.4.1.4: Use debounced search term for page reset to fix race condition // Reset infinite query when search term changes (handled by query key) @@ -688,28 +690,33 @@ export default function LibraryPagePremium() { )} + ) : filteredTracks.length === 0 ? ( + } + title={searchTerm ? 'Aucun résultat trouvé' : 'Aucune piste disponible'} + description={ + searchTerm + ? "Essayez avec d'autres termes de recherche" + : 'Commencez par uploader votre premier track' + } + action={ + !searchTerm + ? { + label: 'Uploader un track', + onClick: () => setIsUploadModalOpen(true), + variant: 'default', + } + : undefined + } + /> ) : ( - {filteredTracks.length === 0 ? ( -
- -

- Aucun titre trouvé -

-

- {searchTerm - ? "Essayez avec d'autres termes de recherche" - : 'Commencez par uploader votre premier track'} -

-
- ) : ( - (
)} - /> - )} + />
)}