import React, { useEffect, useState } from 'react'; import { api } from '../stores/api'; import { useStore } from '../stores/store'; import PageHeader from '../components/PageHeader'; import { dateHeureReunionIso, datesReunion, dateLocale, labelDateReunion } from '../utils/datesReunion'; const TYPES_JETONS = [['desir', 'Désir'], ['24h', '24 heures'], ['1mois', '1 mois'], ['2mois', '2 mois'], ['3mois', '3 mois'], ['6mois', '6 mois'], ['9mois', '9 mois'], ['1an', '1 an'], ['multiple', 'Multiple']]; const INPUT = 'w-full p-3 bg-gray-50 rounded-xl border-0 focus:ring-2 focus:ring-aa-blue'; export default function ReunionHebdomadaire({ contexte = false }) { const { estExecutif, groupe } = useStore(); const choixDatesChrono = datesReunion(groupe, { joursAvant: 365, joursApres: 180 }); const choixDates = [...choixDatesChrono].reverse(); const prochaineDate = choixDatesChrono.find((d) => d >= dateLocale()) || choixDatesChrono[choixDatesChrono.length - 1] || dateLocale(); const [membres, setMembres] = useState([]); const [animateurDefautId, setAnimateurDefautId] = useState(''); const [catalogue, setCatalogue] = useState([]); const [rencontres, setRencontres] = useState([]); const [ouverteId, setOuverteId] = useState(null); const [action, setAction] = useState(''); const [msg, setMsg] = useState(''); const [error, setError] = useState(''); const [loading, setLoading] = useState(false); const [planifier, setPlanifier] = useState(false); const [plan, setPlan] = useState({ date: prochaineDate, animateur_id: '', theme: '', annonces: '', note: '' }); const [collecte, setCollecte] = useState({ montant: '', detenteur_id: '' }); const [vente, setVente] = useState({ type: 'litterature', litterature_id: '', type_jeton: 'desir', quantite: '1', montant: '', vendu_par_id: '' }); const [jeton, setJeton] = useState({ type_jeton: '', membre_id: '', annees: '' }); const [rapport, setRapport] = useState({ animateur_id: '', theme: '', annonces: '', note: '' }); const charger = async () => { try { const [m, l, r] = await Promise.all([ api.get('/membres/actifs'), api.get('/litterature/'), api.get('/reunions/rencontres/details?limite=80'), ]); let animateurId = ''; if (estExecutif) { try { const affectations = await api.get('/postes/affectations/groupe'); const affectationAnimateur = affectations.find((a) => (a.poste_nom || '').toLowerCase().includes('animateur')); animateurId = affectationAnimateur?.membre_id || ''; } catch { animateurId = ''; } } setMembres(m); setCatalogue(l); setRencontres(r); setAnimateurDefautId(animateurId); if (m.length > 0) { const fallbackAnimateur = animateurId || m[0].id; setPlan((f) => ({ ...f, animateur_id: f.animateur_id || fallbackAnimateur })); setRapport((f) => ({ ...f, animateur_id: f.animateur_id || fallbackAnimateur })); setCollecte((f) => ({ ...f, detenteur_id: f.detenteur_id || m[0].id })); setVente((f) => ({ ...f, vendu_par_id: f.vendu_par_id || m[0].id })); } } catch (e) { setError(e.message); } }; useEffect(() => { charger(); }, []); const flash = (texte) => { setMsg(texte); setTimeout(() => setMsg(''), 2500); }; const dateRencontre = (r) => r.date_reunion?.slice(0, 10); const rencontreOuverte = rencontres.find((r) => r.id === ouverteId); const ouvrir = (id) => { const prochaine = ouverteId === id ? null : id; setOuverteId(prochaine); setAction(''); const r = rencontres.find((x) => x.id === id); const animateur = membres.find((m) => m.prenom === r?.animateur_prenom); setRapport({ animateur_id: animateur?.id || animateurDefautId || membres[0]?.id || '', theme: r?.theme || '', annonces: (r?.annonces || []).join('\n'), note: r?.note || '', }); }; const choisirLitterature = (id) => { const item = catalogue.find((c) => c.id === id); const quantite = parseInt(vente.quantite || '1'); setVente({ ...vente, litterature_id: id, montant: item?.prix_coutant ? (item.prix_coutant * quantite).toFixed(2) : vente.montant, }); }; const changerQuantite = (valeur) => { const item = catalogue.find((c) => c.id === vente.litterature_id); const qte = parseInt(valeur || '0'); setVente({ ...vente, quantite: valeur, montant: vente.type === 'litterature' && item?.prix_coutant && qte > 0 ? (item.prix_coutant * qte).toFixed(2) : vente.montant, }); }; const creerRencontre = async () => { setLoading(true); setError(''); try { await api.post('/reunions/', { type: 'reguliere', date_reunion: dateHeureReunionIso(plan.date, groupe), animateur_id: plan.animateur_id || animateurDefautId || null, theme: plan.theme.trim() || 'Rencontre hebdomadaire', annonces: plan.annonces.split('\n').map((a) => a.trim()).filter(Boolean) || null, note: plan.note.trim() || null, }); setPlanifier(false); setPlan({ date: prochaineDate, animateur_id: animateurDefautId, theme: '', annonces: '', note: '' }); flash('Rencontre planifiée'); await charger(); } catch (e) { setError(e.message); } setLoading(false); }; const enregistrerCollecte = async () => { if (!rencontreOuverte || !collecte.montant || !collecte.detenteur_id) { setError('Montant et détenteur requis'); return; } setLoading(true); setError(''); try { await api.post('/collectes/', { reunion_id: rencontreOuverte.id, date_collecte: dateRencontre(rencontreOuverte), montant: parseFloat(collecte.montant), detenteur_id: collecte.detenteur_id, }); setCollecte({ ...collecte, montant: '' }); flash('Collecte saisie'); await charger(); } catch (e) { setError(e.message); } setLoading(false); }; const enregistrerVente = async () => { if (!rencontreOuverte || !vente.montant || !vente.vendu_par_id) { setError('Montant et vendeur requis'); return; } const quantite = parseInt(vente.quantite || '0'); if (quantite <= 0) { setError('Quantité invalide'); return; } setLoading(true); setError(''); try { if (vente.type === 'litterature') { if (!vente.litterature_id) { setError('Sélectionnez un titre'); setLoading(false); return; } await api.post('/ventes-litterature/', { reunion_id: rencontreOuverte.id, litterature_id: vente.litterature_id, quantite, montant: parseFloat(vente.montant), date_vente: dateRencontre(rencontreOuverte), vendu_par_id: vente.vendu_par_id, }); } else { await api.post('/jetons/ventes', { reunion_id: rencontreOuverte.id, type_jeton: vente.type_jeton, quantite, montant: parseFloat(vente.montant), date_vente: dateRencontre(rencontreOuverte), vendu_par_id: vente.vendu_par_id, }); } setVente({ ...vente, litterature_id: '', quantite: '1', montant: '' }); flash('Vente saisie'); await charger(); } catch (e) { setError(e.message); } setLoading(false); }; const enregistrerJeton = async () => { if (!rencontreOuverte || !jeton.type_jeton) { setError('Sélectionnez un type de jeton'); return; } if (jeton.type_jeton !== 'desir' && !jeton.membre_id) { setError('Sélectionnez un membre'); return; } if (jeton.type_jeton === 'multiple' && (!jeton.annees || parseInt(jeton.annees) < 2)) { setError("Nombre d'années requis"); return; } setLoading(true); setError(''); try { const body = { reunion_id: rencontreOuverte.id, type_jeton: jeton.type_jeton, date_remise: dateRencontre(rencontreOuverte), }; if (jeton.membre_id) body.membre_id = jeton.membre_id; if (jeton.type_jeton === 'multiple') body.annees = parseInt(jeton.annees); await api.post('/jetons/', body); setJeton({ type_jeton: '', membre_id: '', annees: '' }); flash('Jeton/gâteau comptabilisé'); await charger(); } catch (e) { setError(e.message); } setLoading(false); }; const enregistrerRapport = async () => { if (!rencontreOuverte) return; setLoading(true); setError(''); try { await api.patch(`/reunions/${rencontreOuverte.id}`, { type: 'reguliere', date_reunion: rencontreOuverte.date_reunion, animateur_id: rapport.animateur_id || null, theme: rapport.theme.trim() || 'Rencontre hebdomadaire', annonces: rapport.annonces.split('\n').map((a) => a.trim()).filter(Boolean) || null, note: rapport.note.trim() || null, }); flash('Rapport enregistré'); await charger(); } catch (e) { setError(e.message); } setLoading(false); }; const telechargerRapport = async (rencontre) => { setLoading(true); setError(''); try { const token = api.getToken(); const res = await fetch(`/api/reunions/${rencontre.id}/rapport.pdf`, { headers: { Authorization: `Bearer ${token}` }, }); if (!res.ok) throw new Error('Erreur de génération du rapport PDF'); const blob = await res.blob(); const a = document.createElement('a'); a.href = URL.createObjectURL(blob); a.download = `rapport_rencontre_${dateRencontre(rencontre)}.pdf`; a.click(); URL.revokeObjectURL(a.href); } catch (e) { setError(e.message); } finally { setLoading(false); } }; const heure = (dateIso) => (dateIso ? new Date(dateIso).toLocaleTimeString('fr-CA', { hour: '2-digit', minute: '2-digit' }) : '—'); return (
Collecte déjà saisie pour cette rencontre.
: ( <>Animateur : {rencontre.animateur_prenom || '—'}
{rencontre.collecte &&Collecte : {rencontre.collecte.montant.toFixed(2)} $ ({rencontre.collecte.statut}) détenue par {rencontre.collecte.detenteur_prenom || '—'}
} {rencontre.ventes_litterature.map((v) =>Vente littérature : {v.titre} x{v.quantite}, {v.montant.toFixed(2)} $ par {v.vendu_par_prenom || '—'}
)} {rencontre.ventes_jetons.map((v) =>Vente jeton : {v.type_jeton} x{v.quantite}, {v.montant.toFixed(2)} $ par {v.vendu_par_prenom || '—'}
)} {rencontre.jetons.map((j) =>Jeton/gâteau : {j.membre_prenom || 'Anonyme'} — {j.type_jeton}{j.annees ? ` (${j.annees} ans)` : ''}
)}