diff --git a/src/components/common/InfiniteScrollList.tsx b/src/components/common/InfiniteScrollList.tsx index 122db98..b9ab711 100644 --- a/src/components/common/InfiniteScrollList.tsx +++ b/src/components/common/InfiniteScrollList.tsx @@ -12,6 +12,7 @@ interface InfiniteScrollListProps { emptyMessage: string; loadingTitle?: string; loadingMessage?: string; + showItemCount?: boolean; } const InfiniteScrollList = ({ @@ -24,6 +25,7 @@ const InfiniteScrollList = ({ emptyMessage, loadingTitle = "Loading...", loadingMessage = "Please wait while data is being loaded", + showItemCount = true, }: InfiniteScrollListProps) => { const observerRef = useRef(null); @@ -114,7 +116,7 @@ const InfiniteScrollList = ({ {/* Stats */} - {items.length > 0 && ( + {items.length > 0 && showItemCount && (
Showing {items.length} item{items.length !== 1 ? 's' : ''} {hasMore && ` • Scroll down to load more`} diff --git a/src/components/common/SongInfo.tsx b/src/components/common/SongInfo.tsx index 804796f..114634f 100644 --- a/src/components/common/SongInfo.tsx +++ b/src/components/common/SongInfo.tsx @@ -13,7 +13,7 @@ import { useDisabledSongs } from '../../hooks/useDisabledSongs'; import { useSelectSinger } from '../../hooks/useSelectSinger'; import { useToast } from '../../hooks/useToast'; import SelectSinger from './SelectSinger'; -import SongItem from './SongItem'; +import { SongInfoDisplay } from './SongItem'; import type { Song } from '../../types'; interface SongInfoProps { @@ -96,17 +96,15 @@ const SongInfo: React.FC = ({ isOpen, onClose, song }) => {
- {/* Song Information using SongItem component */} + {/* Song Information using SongInfoDisplay component */}
- +
+ +
{/* Action Buttons */} diff --git a/src/components/common/TwoLineDisplay.tsx b/src/components/common/TwoLineDisplay.tsx new file mode 100644 index 0000000..53b6d40 --- /dev/null +++ b/src/components/common/TwoLineDisplay.tsx @@ -0,0 +1,49 @@ +import React from 'react'; +import { IonLabel } from '@ionic/react'; + +interface TwoLineDisplayProps { + primaryText: string; + secondaryText: string; + primaryColor?: string; + secondaryColor?: string; + primarySize?: string; + secondarySize?: string; +} + +export const TwoLineDisplay: React.FC = ({ + primaryText, + secondaryText, + primaryColor = 'black', + secondaryColor = '#6b7280', + primarySize = '1rem', + secondarySize = '0.875rem' +}) => { + return ( + + {/* Primary Text - styled like song title */} +
+ {primaryText} +
+ {/* Secondary Text - styled like artist name */} +
+ {secondaryText} +
+
+ ); +}; \ No newline at end of file diff --git a/src/components/common/index.ts b/src/components/common/index.ts index 8eca193..629a076 100644 --- a/src/components/common/index.ts +++ b/src/components/common/index.ts @@ -1,10 +1,9 @@ export { default as ActionButton } from './ActionButton'; export { default as EmptyState } from './EmptyState'; -export { default as Toast } from './Toast'; export { default as ErrorBoundary } from './ErrorBoundary'; export { default as InfiniteScrollList } from './InfiniteScrollList'; export { default as PageHeader } from './PageHeader'; -export { default as SongItem } from './SongItem'; export { default as PlayerControls } from './PlayerControls'; -export { default as SelectSinger } from './SelectSinger'; -export { default as SongInfo } from './SongInfo'; \ No newline at end of file +export { default as SongItem, SongInfoDisplay, SongActionButtons } from './SongItem'; +export { default as Toast } from './Toast'; +export { TwoLineDisplay } from './TwoLineDisplay'; \ No newline at end of file diff --git a/src/features/Artists/Artists.tsx b/src/features/Artists/Artists.tsx index 472df53..d42baf1 100644 --- a/src/features/Artists/Artists.tsx +++ b/src/features/Artists/Artists.tsx @@ -1,7 +1,7 @@ import React, { useState } from 'react'; -import { IonSearchbar, IonItem, IonLabel, IonModal, IonHeader, IonToolbar, IonTitle, IonButton, IonIcon, IonContent } from '@ionic/react'; +import { IonSearchbar, IonItem, IonModal, IonHeader, IonToolbar, IonTitle, IonButton, IonIcon, IonContent } from '@ionic/react'; import { close, list } from 'ionicons/icons'; -import { InfiniteScrollList, SongItem } from '../../components/common'; +import { InfiniteScrollList, SongItem, TwoLineDisplay } from '../../components/common'; import { useArtists } from '../../hooks'; import { useAppSelector } from '../../redux'; import { selectSongs } from '../../redux'; @@ -42,15 +42,11 @@ const Artists: React.FC = () => { // Render artist item for InfiniteScrollList const renderArtistItem = (artist: string) => ( - handleArtistClick(artist)} detail={false}> - -

- {artist} -

-

- {getSongCountByArtist(artist)} song{getSongCountByArtist(artist) !== 1 ? 's' : ''} -

-
+ handleArtistClick(artist)} detail={false} style={{ '--min-height': '60px' }}> + ); diff --git a/src/features/Singers/Singers.tsx b/src/features/Singers/Singers.tsx index 9497577..f2d533c 100644 --- a/src/features/Singers/Singers.tsx +++ b/src/features/Singers/Singers.tsx @@ -44,17 +44,25 @@ const Singers: React.FC = () => { debugLog('Singers component - singers:', singers); // Render singer item for InfiniteScrollList - const renderSingerItem = (singer: Singer) => ( - + const renderSingerItem = (singer: Singer, index: number) => ( + + {/* Order Number */} +
+
+ {index + 1} +
+
+ + {/* Singer Name */} -

+
{singer.name} -

+
{/* Delete Button (Admin Only) */} {isAdmin && ( -
+
e.stopPropagation()}> handleRemoveSinger(singer)} @@ -71,19 +79,18 @@ const Singers: React.FC = () => { return ( <> -
+
{isAdmin && ( )}
-
+
items={singers} isLoading={singersCount === 0} @@ -94,6 +101,7 @@ const Singers: React.FC = () => { emptyMessage="Singers will appear here when they join the party" loadingTitle="Loading singers..." loadingMessage="Please wait while singers data is being loaded" + showItemCount={false} />
diff --git a/src/features/TopPlayed/Top100.tsx b/src/features/TopPlayed/Top100.tsx index 9a74e6a..4c3b62f 100644 --- a/src/features/TopPlayed/Top100.tsx +++ b/src/features/TopPlayed/Top100.tsx @@ -1,10 +1,10 @@ import React, { useState, useMemo, useCallback } from 'react'; -import { IonItem, IonLabel, IonChip, IonModal, IonHeader, IonToolbar, IonTitle, IonButton, IonIcon, IonContent, IonList } from '@ionic/react'; +import { IonItem, IonChip, IonModal, IonHeader, IonToolbar, IonTitle, IonButton, IonIcon, IonContent, IonList } from '@ionic/react'; import { close, list } from 'ionicons/icons'; import { useTopPlayed } from '../../hooks'; import { useAppSelector } from '../../redux'; import { selectTopPlayed, selectSongsArray } from '../../redux'; -import { InfiniteScrollList, SongItem } from '../../components/common'; +import { InfiniteScrollList, SongItem, TwoLineDisplay } from '../../components/common'; import { filterSongs } from '../../utils/dataProcessing'; import { debugLog } from '../../utils/logger'; import { useSongOperations } from '../../hooks'; @@ -108,20 +108,19 @@ const Top100: React.FC = () => { button onClick={() => handleTopPlayedClick(item)} detail={false} + style={{ '--min-height': '60px' }} > {/* Number */} -
- {index + 1}) +
+
+ {index + 1} +
- -

- {item.title} -

-

- {item.artist} -

-
+ {item.count} plays