Signed-off-by: Matt Bruce <mbrucedogs@gmail.com>

This commit is contained in:
Matt Bruce 2025-07-18 14:31:51 -05:00
parent d4e5c4d5ae
commit e9a8682b31
12 changed files with 62 additions and 84 deletions

View File

@ -1,17 +1,23 @@
import React, { useState, useEffect } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { useLocation } from 'react-router-dom';
import { IonApp, IonHeader, IonToolbar, IonTitle, IonContent, IonMenuButton, IonIcon } from '@ionic/react';
import { logOut } from 'ionicons/icons';
import { selectControllerName } from '../../redux/authSlice';
import { logout } from '../../redux/authSlice';
import { ActionButton } from '../common';
import Navigation from '../Navigation/Navigation';
import { getPageTitle } from '../../utils/routeUtils';
import type { LayoutProps } from '../../types';
const Layout: React.FC<LayoutProps> = ({ children }) => {
const controllerName = useSelector(selectControllerName);
const dispatch = useDispatch();
const location = useLocation();
const [isLargeScreen, setIsLargeScreen] = useState(false);
// Get the current page title
const currentPageTitle = getPageTitle(location.pathname);
// Check screen size for responsive layout
useEffect(() => {
@ -51,12 +57,7 @@ const Layout: React.FC<LayoutProps> = ({ children }) => {
<IonTitle>
<div className="flex items-center">
<span>Sings A Lot</span>
{controllerName && (
<span className="ml-4 text-sm text-gray-500">
: {controllerName}
</span>
)}
<span>{currentPageTitle}</span>
</div>
</IonTitle>

View File

@ -1,7 +1,7 @@
import React, { useState } from 'react';
import { IonSearchbar, IonList, IonItem, IonLabel, IonModal, IonHeader, IonToolbar, IonTitle, IonButton, IonIcon, IonContent } from '@ionic/react';
import { close, add, heart, heartOutline, list } from 'ionicons/icons';
import { InfiniteScrollList, PageHeader } from '../../components/common';
import { InfiniteScrollList } from '../../components/common';
import { useArtists } from '../../hooks';
import { useAppSelector } from '../../redux';
import { selectSongs } from '../../redux';
@ -56,10 +56,6 @@ const Artists: React.FC = () => {
return (
<>
<PageHeader
title="Artists"
/>
<div className="max-w-4xl mx-auto p-6">
<div className="mb-6">
{/* Search Input */}

View File

@ -1,5 +1,5 @@
import React from 'react';
import { InfiniteScrollList, PageHeader, SongItem } from '../../components/common';
import { InfiniteScrollList, SongItem } from '../../components/common';
import { useFavorites } from '../../hooks';
import { useAppSelector } from '../../redux';
import { selectFavorites } from '../../redux';
@ -23,11 +23,6 @@ const Favorites: React.FC = () => {
return (
<>
<PageHeader
title="Favorites"
subtitle={`${favoritesCount} items loaded`}
/>
<InfiniteScrollList<Song>
items={favoritesItems}
isLoading={favoritesCount === 0}

View File

@ -1,7 +1,7 @@
import React from 'react';
import { IonChip, IonIcon } from '@ionic/react';
import { time } from 'ionicons/icons';
import { InfiniteScrollList, PageHeader, SongItem } from '../../components/common';
import { InfiniteScrollList, SongItem } from '../../components/common';
import { useHistory } from '../../hooks';
import { useAppSelector } from '../../redux';
import { selectHistory } from '../../redux';
@ -39,11 +39,6 @@ const History: React.FC = () => {
return (
<>
<PageHeader
title="Recently Played"
subtitle={`${historyCount} items loaded`}
/>
<InfiniteScrollList<Song>
items={historyItems}
isLoading={historyCount === 0}

View File

@ -1,5 +1,5 @@
import React from 'react';
import { InfiniteScrollList, PageHeader, SongItem } from '../../components/common';
import { InfiniteScrollList, SongItem } from '../../components/common';
import { useNewSongs } from '../../hooks';
import { useAppSelector } from '../../redux';
import { selectNewSongs } from '../../redux';
@ -23,11 +23,6 @@ const NewSongs: React.FC = () => {
return (
<>
<PageHeader
title="New Songs"
subtitle={`${newSongsCount} items loaded`}
/>
<InfiniteScrollList<Song>
items={newSongsItems}
isLoading={newSongsCount === 0}

View File

@ -1,7 +1,7 @@
import React, { useState, useEffect } from 'react';
import { IonButton, IonIcon, IonReorderGroup, IonReorder, IonItem, IonLabel, IonItemSliding, IonItemOptions, IonItemOption } from '@ionic/react';
import { trash, reorderThreeOutline, reorderTwoOutline, playCircle } from 'ionicons/icons';
import { PageHeader, ActionButton } from '../../components/common';
import { ActionButton } from '../../components/common';
import { useQueue } from '../../hooks';
import { useAppSelector } from '../../redux';
import { selectQueue, selectPlayerState, selectIsAdmin, selectControllerName } from '../../redux';
@ -219,22 +219,19 @@ const Queue: React.FC = () => {
return (
<>
<PageHeader
title="Queue"
subtitle={`${queueStats.totalSongs} song${queueStats.totalSongs !== 1 ? 's' : ''} in queue`}
action={
isAdmin && (
<IonButton
onClick={toggleQueueMode}
fill="outline"
size="small"
className="flex items-center gap-2"
>
<IonIcon icon={queueMode === 'delete' ? reorderThreeOutline : trash} />
</IonButton>
)
}
/>
<div className="flex justify-between items-center mb-4">
{isAdmin && (
<IonButton
onClick={toggleQueueMode}
fill="outline"
size="small"
className="flex items-center gap-2"
>
<IonIcon icon={queueMode === 'delete' ? reorderThreeOutline : trash} />
</IonButton>
)}
</div>
<div className="max-w-4xl mx-auto p-6">

View File

@ -1,6 +1,6 @@
import React from 'react';
import { IonSearchbar } from '@ionic/react';
import { InfiniteScrollList, PageHeader, SongItem } from '../../components/common';
import { InfiniteScrollList, SongItem } from '../../components/common';
import { useSearch } from '../../hooks';
import { useAppSelector } from '../../redux';
import { selectIsAdmin, selectSongs } from '../../redux';
@ -39,10 +39,6 @@ const Search: React.FC = () => {
return (
<div className="max-w-4xl mx-auto p-6">
<PageHeader
title="Search Songs"
/>
<div className="mb-6">
{/* Search Input */}
<IonSearchbar

View File

@ -1,6 +1,6 @@
import React, { useState, useEffect } from 'react';
import { IonToggle, IonItem, IonLabel, IonList } from '@ionic/react';
import { PageHeader } from '../../components/common';
import { } from '../../components/common';
import { useAppSelector } from '../../redux';
import { selectControllerName } from '../../redux';
import { settingsService } from '../../firebase/services';
@ -62,10 +62,9 @@ const Settings: React.FC = () => {
return (
<>
<PageHeader
title="Settings"
subtitle="Configure player behavior"
/>
<div className="text-sm text-gray-500 text-center mb-4">
Configure player behavior
</div>
<div className="max-w-4xl mx-auto p-6">
<IonList>

View File

@ -1,7 +1,7 @@
import React, { useState } from 'react';
import { IonItem, IonLabel, IonIcon, IonModal, IonHeader, IonToolbar, IonTitle, IonButton, IonContent, IonInput, IonLabel as IonInputLabel } from '@ionic/react';
import { trash, add, close } from 'ionicons/icons';
import { InfiniteScrollList, PageHeader, ActionButton } from '../../components/common';
import { InfiniteScrollList, ActionButton } from '../../components/common';
import { useSingers } from '../../hooks';
import { useAppSelector } from '../../redux';
import { selectSingers } from '../../redux';
@ -70,21 +70,17 @@ const Singers: React.FC = () => {
return (
<>
<PageHeader
title="Singers"
subtitle={`${singersCount} singers in the party`}
action={
isAdmin && (
<IonButton
fill="clear"
onClick={handleOpenAddModal}
className="text-primary"
>
<IonIcon icon={add} slot="icon-only" />
</IonButton>
)
}
/>
<div className="flex justify-between items-center mb-4">
{isAdmin && (
<IonButton
fill="clear"
onClick={handleOpenAddModal}
className="text-primary"
>
<IonIcon icon={add} slot="icon-only" />
</IonButton>
)}
</div>
<div className="max-w-4xl mx-auto p-6">
<InfiniteScrollList<Singer>

View File

@ -1,7 +1,7 @@
import React, { useState, useMemo, useCallback } from 'react';
import { IonItem, IonLabel, IonModal, IonHeader, IonToolbar, IonTitle, IonButton, IonIcon, IonChip, IonContent, IonList, IonAccordionGroup, IonAccordion } from '@ionic/react';
import { close, list } from 'ionicons/icons';
import { InfiniteScrollList, PageHeader, SongItem } from '../../components/common';
import { InfiniteScrollList, SongItem } from '../../components/common';
import { useSongLists } from '../../hooks';
import { useAppSelector } from '../../redux';
import { selectSongList } from '../../redux';
@ -73,11 +73,6 @@ const SongLists: React.FC = () => {
return (
<>
<PageHeader
title="Song Lists"
subtitle={`${songListCount} items loaded`}
/>
<div className="max-w-4xl mx-auto p-6">
<InfiniteScrollList<SongList>
items={songLists}

View File

@ -4,7 +4,7 @@ import { close, list } from 'ionicons/icons';
import { useTopPlayed } from '../../hooks';
import { useAppSelector } from '../../redux';
import { selectTopPlayed, selectSongsArray } from '../../redux';
import { InfiniteScrollList, PageHeader, SongItem } from '../../components/common';
import { InfiniteScrollList, SongItem } from '../../components/common';
import { filterSongs } from '../../utils/dataProcessing';
import { useSongOperations } from '../../hooks';
import { useToast } from '../../hooks';
@ -97,11 +97,6 @@ const Top100: React.FC = () => {
return (
<>
<PageHeader
title="Top 100 Played"
subtitle={`${displayCount} items loaded`}
/>
<InfiniteScrollList<TopPlayed>
items={displayItems}
isLoading={isLoading}

18
src/utils/routeUtils.ts Normal file
View File

@ -0,0 +1,18 @@
// Route to title mapping for dynamic navigation
export const getPageTitle = (pathname: string): string => {
const routeMap: { [key: string]: string } = {
'/': 'Search Songs',
'/search': 'Search Songs',
'/queue': 'Queue',
'/singers': 'Singers',
'/artists': 'Artists',
'/top-played': 'Top 100 Played',
'/favorites': 'Favorites',
'/history': 'History',
'/new-songs': 'New Songs',
'/song-lists': 'Song Lists',
'/settings': 'Settings'
};
return routeMap[pathname] || 'Search Songs';
};