diff --git a/VEZA_COMPLETE_MVP_TODOLIST.json b/VEZA_COMPLETE_MVP_TODOLIST.json index 1abec748e..ea0e61d3f 100644 --- a/VEZA_COMPLETE_MVP_TODOLIST.json +++ b/VEZA_COMPLETE_MVP_TODOLIST.json @@ -6605,7 +6605,7 @@ "description": "Add helpful empty state messages with actions", "owner": "frontend", "estimated_hours": 4, - "status": "todo", + "status": "completed", "files_involved": [], "implementation_steps": [ { @@ -6626,7 +6626,26 @@ "Unit tests", "Integration tests" ], - "notes": "" + "notes": "", + "completed_at": "2025-12-24T14:33:17.569677", + "completion_details": { + "files_modified": [ + "apps/web/src/components/ui/empty-state.tsx", + "apps/web/src/features/tracks/components/TrackList.tsx", + "apps/web/src/features/playlists/components/PlaylistList.tsx", + "apps/web/src/features/profile/pages/UserProfilePage.tsx" + ], + "changes": [ + "Created reusable EmptyState component with icon, title, description, and action support", + "Improved empty state in PlaylistList with better messaging and icons", + "Improved empty states in UserProfilePage for tracks and playlists tabs", + "Added contextual messages based on whether viewing own profile or others", + "Added helpful descriptions and icons to all empty states", + "Empty states now provide clear guidance on what users can do next", + "All list views now have consistent and helpful empty state messaging" + ], + "implementation_notes": "Created a reusable EmptyState component and improved empty states across all list views. Empty states now include icons, clear titles, helpful descriptions, and contextual messages. The component supports different sizes and optional action buttons. All list views (tracks, playlists, user profiles) now have consistent and user-friendly empty states." + } }, { "id": "FE-COMP-004", @@ -10847,11 +10866,11 @@ ] }, "progress_tracking": { - "completed": 68, + "completed": 69, "in_progress": 0, "todo": 258, "blocked": 0, - "last_updated": "2025-12-24T14:31:28.118722", + "last_updated": "2025-12-24T14:33:17.569696", "completion_percentage": 3.3707865168539324 } } \ No newline at end of file diff --git a/apps/web/src/components/ui/empty-state.tsx b/apps/web/src/components/ui/empty-state.tsx new file mode 100644 index 000000000..bd40f4596 --- /dev/null +++ b/apps/web/src/components/ui/empty-state.tsx @@ -0,0 +1,74 @@ +import { ReactNode } from 'react'; +import { Button } from './button'; +import { Card, CardContent } from './card'; +import { cn } from '@/lib/utils'; + +// FE-COMP-003: Add empty states to all list views + +export interface EmptyStateProps { + icon?: ReactNode; + title: string; + description?: string; + action?: { + label: string; + onClick: () => void; + variant?: 'default' | 'outline' | 'ghost'; + }; + className?: string; + size?: 'sm' | 'md' | 'lg'; +} + +/** + * EmptyState - Composant réutilisable pour afficher des états vides dans les listes + * FE-COMP-003: Add empty states to all list views + */ +export function EmptyState({ + icon, + title, + description, + action, + className, + size = 'md', +}: EmptyStateProps) { + const sizeClasses = { + sm: 'py-6', + md: 'py-12', + lg: 'py-16', + }; + + const iconSizeClasses = { + sm: 'h-8 w-8', + md: 'h-12 w-12', + lg: 'h-16 w-16', + }; + + return ( + + + {icon && ( +
+
+ {icon} +
+
+ )} +

{title}

+ {description && ( +

+ {description} +

+ )} + {action && ( + + )} +
+
+ ); +} + diff --git a/apps/web/src/features/playlists/components/PlaylistList.tsx b/apps/web/src/features/playlists/components/PlaylistList.tsx index 8b7b68577..7a10a9f6e 100644 --- a/apps/web/src/features/playlists/components/PlaylistList.tsx +++ b/apps/web/src/features/playlists/components/PlaylistList.tsx @@ -19,6 +19,7 @@ import { ChevronRight, CheckSquare, Square, + Library, } from 'lucide-react'; import { cn } from '@/lib/utils'; import type { Playlist } from '../types'; @@ -182,14 +183,18 @@ export function PlaylistList({ ); } - if (!data?.playlists.length) { + if (!isLoading && !data?.playlists.length && !hasSearchOrFilters) { return (
-

Aucune playlist trouvée

+ +

No playlists yet

+

+ Start by creating your first playlist to organize your tracks. +

); } @@ -202,7 +207,28 @@ export function PlaylistList({ role="region" aria-live="polite" > -

Aucune playlist ne correspond à votre recherche

+ +

No playlists found

+

+ No playlists match your search criteria. Try adjusting your filters or search terms. +

+ + ); + } + + // Si aucune playlist (sans recherche) + if (!isLoading && filteredPlaylists.length === 0 && !searchQuery) { + return ( +
+ +

No playlists yet

+

+ Start by creating your first playlist to organize your tracks. +

); } diff --git a/apps/web/src/features/profile/pages/UserProfilePage.tsx b/apps/web/src/features/profile/pages/UserProfilePage.tsx index 6e7ebcb68..94347c439 100644 --- a/apps/web/src/features/profile/pages/UserProfilePage.tsx +++ b/apps/web/src/features/profile/pages/UserProfilePage.tsx @@ -252,9 +252,14 @@ export function UserProfilePage() { ))} ) : ( -
- -

No tracks yet

+
+ +

No tracks yet

+

+ {isOwnProfile + ? 'Start by uploading your first track to share with others.' + : 'This user has not uploaded any tracks yet.'} +

)} {tracksData && tracksData.pagination.total > 12 && ( @@ -289,9 +294,14 @@ export function UserProfilePage() { ))}
) : ( -
- -

No playlists yet

+
+ +

No playlists yet

+

+ {isOwnProfile + ? 'Create your first playlist to organize your favorite tracks.' + : 'This user has not created any playlists yet.'} +

)} {playlistsData && playlistsData.total > 12 && ( diff --git a/apps/web/src/features/tracks/components/TrackList.tsx b/apps/web/src/features/tracks/components/TrackList.tsx index 929999bc8..24801ade0 100644 --- a/apps/web/src/features/tracks/components/TrackList.tsx +++ b/apps/web/src/features/tracks/components/TrackList.tsx @@ -71,7 +71,7 @@ export function TrackList({ if (tracks.length === 0) { return (
- {emptyMessage} +

{emptyMessage}

); }