156 lines
4.2 KiB
React
156 lines
4.2 KiB
React
import { useMemo, useState } from 'react'
|
||
import { Link, useParams } from 'react-router-dom'
|
||
import { useGroups } from '../context/GroupsContext.jsx'
|
||
|
||
function formatEUR(n) {
|
||
return new Intl.NumberFormat('fr-FR', {
|
||
style: 'currency',
|
||
currency: 'EUR',
|
||
maximumFractionDigits: 2,
|
||
}).format(Number(n) || 0)
|
||
}
|
||
|
||
function formatDate(iso) {
|
||
try {
|
||
return new Intl.DateTimeFormat('fr-FR', {
|
||
dateStyle: 'short',
|
||
timeStyle: 'short',
|
||
}).format(new Date(iso))
|
||
} catch {
|
||
return iso || '—'
|
||
}
|
||
}
|
||
|
||
export default function GroupDetailPage() {
|
||
const { groupId } = useParams()
|
||
const {
|
||
groups,
|
||
addGroupOrder,
|
||
getOrdersForGroup,
|
||
groupTotalEuros,
|
||
} = useGroups()
|
||
|
||
const group = useMemo(
|
||
() => groups.find((g) => g.id === String(groupId)),
|
||
[groups, groupId],
|
||
)
|
||
|
||
const lines = group ? getOrdersForGroup(group.id) : []
|
||
const total = group ? groupTotalEuros(group.id) : 0
|
||
|
||
const [contributor, setContributor] = useState('')
|
||
const [amount, setAmount] = useState('')
|
||
const [note, setNote] = useState('')
|
||
const [err, setErr] = useState(null)
|
||
const [ok, setOk] = useState(null)
|
||
|
||
function handleSubmit(e) {
|
||
e.preventDefault()
|
||
setErr(null)
|
||
setOk(null)
|
||
if (!group) return
|
||
try {
|
||
addGroupOrder(group.id, {
|
||
contributor,
|
||
amount,
|
||
note,
|
||
})
|
||
setContributor('')
|
||
setAmount('')
|
||
setNote('')
|
||
setOk('Contribution enregistrée (POST /api/groups/:id/orders en local).')
|
||
} catch (ex) {
|
||
setErr(ex?.message || 'Impossible d’enregistrer.')
|
||
}
|
||
}
|
||
|
||
if (!group) {
|
||
return (
|
||
<div className="groupes-page">
|
||
<p className="empty-state">Groupe introuvable.</p>
|
||
<Link to="/groupes" className="nav-link">
|
||
← Liste des groupes
|
||
</Link>
|
||
</div>
|
||
)
|
||
}
|
||
|
||
return (
|
||
<div className="groupes-page">
|
||
<Link to="/groupes" className="nav-link book-detail-back">
|
||
← Tous les groupes
|
||
</Link>
|
||
<h2 className="page-title">{group.name}</h2>
|
||
<p className="page-lead">
|
||
Pool actuel : <strong>{formatEUR(total)}</strong> · id{' '}
|
||
<code>{group.id.slice(0, 8)}…</code>
|
||
</p>
|
||
|
||
{err ? (
|
||
<p className="form-error" role="alert">
|
||
{err}
|
||
</p>
|
||
) : null}
|
||
{ok ? <p className="form-notice">{ok}</p> : null}
|
||
|
||
<section className="order-panel">
|
||
<h3 className="panel-title">Participer à la commande groupée</h3>
|
||
<form className="review-form" onSubmit={handleSubmit}>
|
||
<label>
|
||
Pseudo / prénom
|
||
<input
|
||
value={contributor}
|
||
onChange={(e) => setContributor(e.target.value)}
|
||
placeholder="Marvin"
|
||
/>
|
||
</label>
|
||
<label>
|
||
Montant (€) — optionnel pour la démo
|
||
<input
|
||
type="number"
|
||
min={0}
|
||
step="0.5"
|
||
value={amount}
|
||
onChange={(e) => setAmount(e.target.value)}
|
||
placeholder="12.5"
|
||
/>
|
||
</label>
|
||
<label>
|
||
Note (ex. titres souhaités)
|
||
<textarea
|
||
rows={2}
|
||
value={note}
|
||
onChange={(e) => setNote(e.target.value)}
|
||
placeholder="2 romans SF…"
|
||
/>
|
||
</label>
|
||
<button type="submit" className="btn primary">
|
||
Envoyer ma participation (POST)
|
||
</button>
|
||
</form>
|
||
</section>
|
||
|
||
<section className="order-panel">
|
||
<h3 className="panel-title">Contributions</h3>
|
||
{lines.length === 0 ? (
|
||
<p className="empty-state">Personne n’a encore participé.</p>
|
||
) : (
|
||
<ul className="reviews-list">
|
||
{lines.map((o) => (
|
||
<li key={o.id} className="reviews-item">
|
||
<p className="reviews-meta">
|
||
<strong>{o.contributor}</strong> · {formatDate(o.createdAt)} ·{' '}
|
||
{formatEUR(o.amountEuros)}
|
||
</p>
|
||
{o.note ? (
|
||
<p className="reviews-text">{o.note}</p>
|
||
) : null}
|
||
</li>
|
||
))}
|
||
</ul>
|
||
)}
|
||
</section>
|
||
</div>
|
||
)
|
||
}
|