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>
|
|||
|
|
)
|
|||
|
|
}
|