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
This commit is contained in:
parent
27ac12e0bb
commit
2ae9535009
2 changed files with 34 additions and 23 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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() {
|
|||
</div>
|
||||
)}
|
||||
</div>
|
||||
) : filteredTracks.length === 0 ? (
|
||||
<EmptyState
|
||||
icon={<Music />}
|
||||
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
|
||||
}
|
||||
/>
|
||||
) : (
|
||||
<Card>
|
||||
<CardContent className="p-0">
|
||||
{filteredTracks.length === 0 ? (
|
||||
<div className="p-12 text-center">
|
||||
<Music className="w-16 h-16 text-kodo-secondary mx-auto mb-4" />
|
||||
<p className="text-lg font-semibold text-white mb-2">
|
||||
Aucun titre trouvé
|
||||
</p>
|
||||
<p className="text-base text-kodo-secondary mb-4">
|
||||
{searchTerm
|
||||
? "Essayez avec d'autres termes de recherche"
|
||||
: 'Commencez par uploader votre premier track'}
|
||||
</p>
|
||||
</div>
|
||||
) : (
|
||||
<VirtualizedList
|
||||
<VirtualizedList
|
||||
items={filteredTracks}
|
||||
itemHeight={88}
|
||||
containerHeight={600}
|
||||
className="divide-y divide-white/5"
|
||||
onItemsRendered={handleItemsRendered}
|
||||
renderItem={(track: Track, index: number) => (
|
||||
<div
|
||||
key={track.id}
|
||||
|
|
@ -793,8 +800,7 @@ export default function LibraryPagePremium() {
|
|||
</DropdownMenu>
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</CardContent>
|
||||
</Card>
|
||||
)}
|
||||
|
|
|
|||
Loading…
Reference in a new issue