singsalot/src/features/Singers/Singers.tsx

158 lines
4.6 KiB
TypeScript

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, ActionButton } from '../../components/common';
import { useSingers } from '../../hooks';
import { useAppSelector } from '../../redux';
import { selectSingers } from '../../redux';
import type { Singer } from '../../types';
const Singers: React.FC = () => {
const {
singers,
isAdmin,
handleRemoveSinger,
handleAddSinger,
} = useSingers();
const [showAddModal, setShowAddModal] = useState(false);
const [newSingerName, setNewSingerName] = useState('');
const singersData = useAppSelector(selectSingers);
const handleOpenAddModal = () => {
setShowAddModal(true);
setNewSingerName('');
};
const handleCloseAddModal = () => {
setShowAddModal(false);
setNewSingerName('');
};
const handleSubmitAddSinger = async () => {
if (newSingerName.trim()) {
await handleAddSinger(newSingerName);
handleCloseAddModal();
}
};
const singersCount = Object.keys(singersData).length;
// Debug logging
console.log('Singers component - singers count:', singersCount);
console.log('Singers component - singers:', singers);
// Render singer item for InfiniteScrollList
const renderSingerItem = (singer: Singer) => (
<IonItem detail={false}>
<IonLabel>
<h3 className="text-sm font-medium text-gray-900">
{singer.name}
</h3>
</IonLabel>
{/* Delete Button (Admin Only) */}
{isAdmin && (
<div slot="end" className="flex items-center gap-2 ml-2">
<div onClick={(e) => e.stopPropagation()}>
<ActionButton
onClick={() => handleRemoveSinger(singer)}
variant="danger"
size="sm"
>
<IonIcon icon={trash} />
</ActionButton>
</div>
</div>
)}
</IonItem>
);
return (
<>
<div className="flex justify-end items-center mb-4 pr-4 right-button-container">
{isAdmin && (
<IonButton
fill="clear"
onClick={handleOpenAddModal}
className="text-primary"
>
<IonIcon icon={add} slot="icon-only" size="large" />
</IonButton>
)}
</div>
<div className="max-w-4xl mx-auto p-6">
<InfiniteScrollList<Singer>
items={singers}
isLoading={singersCount === 0}
hasMore={false}
onLoadMore={() => {}}
renderItem={renderSingerItem}
emptyTitle="No singers yet"
emptyMessage="Singers will appear here when they join the party"
loadingTitle="Loading singers..."
loadingMessage="Please wait while singers data is being loaded"
/>
</div>
{/* Add Singer Modal */}
<IonModal
isOpen={showAddModal}
onDidDismiss={handleCloseAddModal}
breakpoints={[0, 0.5, 0.8]}
initialBreakpoint={0.8}
>
<IonHeader>
<IonToolbar>
<IonTitle>Add New Singer</IonTitle>
<IonButton slot="end" fill="clear" onClick={handleCloseAddModal}>
<IonIcon icon={close} />
</IonButton>
</IonToolbar>
</IonHeader>
<IonContent className="ion-padding">
<div>
<div style={{ marginBottom: '2rem' }}>
<IonInputLabel className="bold-label">
Singer Name
</IonInputLabel>
<IonInput
className="visible-input"
value={newSingerName}
onIonInput={(e) => setNewSingerName(e.detail.value || '')}
placeholder="Enter singer name"
clearInput={true}
onKeyPress={(e) => {
if (e.key === 'Enter') {
handleSubmitAddSinger();
}
}}
/>
</div>
<div className="flex gap-2">
<IonButton
expand="block"
onClick={handleSubmitAddSinger}
disabled={!newSingerName.trim()}
>
Add Singer
</IonButton>
<IonButton
expand="block"
fill="outline"
onClick={handleCloseAddModal}
>
Cancel
</IonButton>
</div>
</div>
</IonContent>
</IonModal>
</>
);
};
export default Singers;