rcnn/web/src/coaster/CoasterListPanel.tsx
2026-04-25 23:15:46 +02:00

107 lines
3.7 KiB
TypeScript

import { useState, useEffect } from 'react'
import { listCoasters, deleteCoaster } from '../api/coaster'
import type { SavedCoaster } from '../types/api'
import styles from './CoasterListPanel.module.css'
interface Props {
challengeId: string
currentUsername: string | undefined
onLoad: (coaster: SavedCoaster) => void
refreshKey: number
/** Render as a top-bar dropdown instead of a sidebar panel */
menuMode?: boolean
}
export function CoasterListPanel({ challengeId, currentUsername, onLoad, refreshKey, menuMode }: Props) {
const [open, setOpen] = useState(false)
const [coasters, setCoasters] = useState<SavedCoaster[]>([])
useEffect(() => {
listCoasters(challengeId).then(setCoasters).catch(console.error)
}, [challengeId, refreshKey])
async function handleDelete(id: string) {
await deleteCoaster(id)
setCoasters(prev => prev.filter(c => c.id !== id))
}
if (menuMode) {
return (
<div className={styles.menuWrapper}>
<button className={styles.menuToggle} onClick={() => setOpen(o => !o)}>
Coasters ({coasters.length})
<span className={`${styles.toggleArrow}${open ? ` ${styles.open}` : ''}`}></span>
</button>
{open && (
<div className={styles.menuDropdown}>
{coasters.length === 0 ? (
<p className={styles.empty}>No coasters yet.</p>
) : (
coasters.map(c => {
const isOwn = c.creator_username === currentUsername
return (
<div key={c.id} className={styles.row}>
<div className={styles.rowInfo}>
<span className={styles.rowName}>{c.name || 'Unnamed coaster'}</span>
<span className={`${styles.username}${isOwn ? ` ${styles.you}` : ''}`}>
@{c.creator_username}
</span>
</div>
<button className={styles.loadBtn} onClick={() => { onLoad(c); setOpen(false) }}>
Load
</button>
{isOwn && (
<button className={styles.deleteBtn} onClick={() => handleDelete(c.id)}>
Del
</button>
)}
</div>
)
})
)}
</div>
)}
</div>
)
}
return (
<div className={styles.panel}>
<button className={styles.toggle} onClick={() => setOpen(o => !o)}>
<span>Coasters ({coasters.length})</span>
<span className={`${styles.toggleArrow}${open ? ` ${styles.open}` : ''}`}></span>
</button>
{open && (
<div className={styles.body}>
{coasters.length === 0 ? (
<p className={styles.empty}>No coasters yet.</p>
) : (
coasters.map(c => {
const isOwn = c.creator_username === currentUsername
return (
<div key={c.id} className={styles.row}>
<div className={styles.rowInfo}>
<span className={styles.rowName}>{c.name || 'Unnamed coaster'}</span>
<span className={`${styles.username}${isOwn ? ` ${styles.you}` : ''}`}>
@{c.creator_username}
</span>
</div>
<button className={styles.loadBtn} onClick={() => onLoad(c)}>
Load
</button>
{isOwn && (
<button className={styles.deleteBtn} onClick={() => handleDelete(c.id)}>
Del
</button>
)}
</div>
)
})
)}
</div>
)}
</div>
)
}