import { useCallback, useState } from 'react' import { Link } from 'react-router-dom' import { Loading, ErrorState } from '../../components/PageState.jsx' import { useAsync } from '../../lib/useAsync.js' import { ago, dateTime } from '../../lib/format.js' import { api } from '../../api/client.js' // Player-facing appeals: eligible sanctions the player can appeal, plus the // status of appeals they've already submitted. Mirrors PlayerAccount's // Section layout. const STATUS_STYLE = { pending: { color: '#e0b070', background: 'rgba(224,176,112,0.12)', border: '1px solid rgba(224,176,112,0.4)' }, under_review: { color: '#7fa8d0', background: 'rgba(127,168,208,0.14)', border: '1px solid rgba(127,168,208,0.4)' }, approved: { color: '#7fd0a4', background: 'rgba(95,185,138,0.16)', border: '1px solid rgba(95,185,138,0.4)' }, denied: { color: '#d98b84', background: 'rgba(217,139,132,0.16)', border: '1px solid rgba(217,139,132,0.4)' }, withdrawn: { color: '#9fb0c6', background: 'rgba(127,153,189,0.14)', border: '1px solid var(--line)' }, } const STATUS_LABEL = { pending: 'Pending', under_review: 'Under review', approved: 'Approved', denied: 'Denied', withdrawn: 'Withdrawn', } function fmtDuration(seconds) { if (!seconds) return null if (seconds % 86400 === 0) return `${seconds / 86400}d` if (seconds % 3600 === 0) return `${seconds / 3600}h` if (seconds % 60 === 0) return `${seconds / 60}m` return `${seconds}s` } function Section({ title, children }) { return (

{title}

{children}
) } function EligibleItem({ item, onSubmitted }) { const [open, setOpen] = useState(false) const [text, setText] = useState('') const [busy, setBusy] = useState(false) const [error, setError] = useState('') async function submit() { if (!text.trim()) return setBusy(true) setError('') try { await api.player.submitAppeal({ mod_action_id: item.id, submitted_text: text.trim() }) setText('') setOpen(false) onSubmitted() } catch (err) { setError(err.message || 'Could not submit your appeal.') } finally { setBusy(false) } } const duration = fmtDuration(item.duration_seconds) return (
{item.action_type} {duration && {duration}} {ago(item.created_at)}

{item.reason || 'No reason given.'}

{!open ? (
) : (
{error &&

{error}

}