103 lines
2.9 KiB
React
103 lines
2.9 KiB
React
|
|
import { useState } from 'react'
|
|||
|
|
import { useSubscriptions } from '../context/SubscriptionsContext.jsx'
|
|||
|
|
|
|||
|
|
function formatEUR(n) {
|
|||
|
|
return new Intl.NumberFormat('fr-FR', {
|
|||
|
|
style: 'currency',
|
|||
|
|
currency: 'EUR',
|
|||
|
|
}).format(Number(n) || 0)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function formatDate(iso) {
|
|||
|
|
try {
|
|||
|
|
return new Intl.DateTimeFormat('fr-FR', {
|
|||
|
|
dateStyle: 'long',
|
|||
|
|
}).format(new Date(iso))
|
|||
|
|
} catch {
|
|||
|
|
return iso || '—'
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export default function AbonnementPage() {
|
|||
|
|
const { plans, subscription, subscribe, cancelSubscription, getPlanLabel } =
|
|||
|
|
useSubscriptions()
|
|||
|
|
const [err, setErr] = useState(null)
|
|||
|
|
const [ok, setOk] = useState(null)
|
|||
|
|
|
|||
|
|
function handleSubscribe(planId) {
|
|||
|
|
setErr(null)
|
|||
|
|
setOk(null)
|
|||
|
|
try {
|
|||
|
|
subscribe(planId)
|
|||
|
|
setOk('Abonnement pris en compte (POST /api/subscriptions en local).')
|
|||
|
|
} catch (e) {
|
|||
|
|
setErr(e?.message || 'Impossible de souscrire.')
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return (
|
|||
|
|
<div className="abonnement-page">
|
|||
|
|
<h2 className="page-title">Abonnement</h2>
|
|||
|
|
<p className="page-lead">
|
|||
|
|
Équivalent local de <code>POST /api/subscriptions</code> · stockage{' '}
|
|||
|
|
<code>librairie-subscriptions</code> · pas de paiement réel, c’est une
|
|||
|
|
démo.
|
|||
|
|
</p>
|
|||
|
|
|
|||
|
|
{err ? (
|
|||
|
|
<p className="form-error" role="alert">
|
|||
|
|
{err}
|
|||
|
|
</p>
|
|||
|
|
) : null}
|
|||
|
|
{ok ? <p className="form-notice">{ok}</p> : null}
|
|||
|
|
|
|||
|
|
{subscription ? (
|
|||
|
|
<section className="order-panel abo-active">
|
|||
|
|
<h3 className="panel-title">Abonnement actif</h3>
|
|||
|
|
<p>
|
|||
|
|
Formule : <strong>{getPlanLabel(subscription.planId)}</strong>
|
|||
|
|
</p>
|
|||
|
|
<p className="book-section-lead">
|
|||
|
|
Depuis le {formatDate(subscription.subscribedAt)}
|
|||
|
|
</p>
|
|||
|
|
<button
|
|||
|
|
type="button"
|
|||
|
|
className="btn danger"
|
|||
|
|
onClick={() => {
|
|||
|
|
cancelSubscription()
|
|||
|
|
setOk('Abonnement annulé (local).')
|
|||
|
|
}}
|
|||
|
|
>
|
|||
|
|
Résilier (local)
|
|||
|
|
</button>
|
|||
|
|
</section>
|
|||
|
|
) : null}
|
|||
|
|
|
|||
|
|
<section className="order-panel">
|
|||
|
|
<h3 className="panel-title">Choisir une formule</h3>
|
|||
|
|
<ul className="abo-plans">
|
|||
|
|
{plans.map((p) => (
|
|||
|
|
<li key={p.id} className="abo-plan-card">
|
|||
|
|
<p className="abo-plan-name">{p.label}</p>
|
|||
|
|
<p className="abo-plan-price">{formatEUR(p.price)}</p>
|
|||
|
|
<button
|
|||
|
|
type="button"
|
|||
|
|
className="btn primary small"
|
|||
|
|
onClick={() => handleSubscribe(p.id)}
|
|||
|
|
disabled={Boolean(subscription)}
|
|||
|
|
>
|
|||
|
|
Souscrire (POST)
|
|||
|
|
</button>
|
|||
|
|
</li>
|
|||
|
|
))}
|
|||
|
|
</ul>
|
|||
|
|
{subscription ? (
|
|||
|
|
<p className="book-section-lead">
|
|||
|
|
Pour changer de formule, résilie d’abord puis reprends une offre.
|
|||
|
|
</p>
|
|||
|
|
) : null}
|
|||
|
|
</section>
|
|||
|
|
</div>
|
|||
|
|
)
|
|||
|
|
}
|