Refactoriser les pages d'assemblées
Extrait les utilitaires et composants UI propres aux assemblées pour alléger la page principale sans changer le comportement. Centralise l'usage de estExecutif depuis le store dans les pages Assemblées, Rencontres, Événements et Décisions afin d'éviter les règles d'accès dupliquées côté frontend.
This commit is contained in:
parent
69309acd13
commit
a8afaffc0e
6 changed files with 123 additions and 156 deletions
87
frontend/src/features/assemblees/assembleeUi.jsx
Normal file
87
frontend/src/features/assemblees/assembleeUi.jsx
Normal file
|
|
@ -0,0 +1,87 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { labelDateReunion } from '../../utils/datesReunion';
|
||||||
|
|
||||||
|
export const INPUT = 'w-full p-3 bg-gray-50 rounded-xl border-0 focus:ring-2 focus:ring-aa-blue';
|
||||||
|
|
||||||
|
const STATUTS = {
|
||||||
|
proposee: ['À seconder', 'bg-yellow-100 text-yellow-700'],
|
||||||
|
secondee: ['Secondée', 'bg-blue-100 text-blue-700'],
|
||||||
|
rejetee: ['Rejetée', 'bg-red-100 text-red-700'],
|
||||||
|
};
|
||||||
|
|
||||||
|
export function boutonAction(actif) {
|
||||||
|
return `p-3 rounded-xl text-sm font-bold ${actif ? 'bg-aa-blue text-white' : 'bg-gray-50 text-gray-700'}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function Card({ titre, children }) {
|
||||||
|
return <div className="bg-white rounded-2xl p-4 shadow-sm space-y-3"><h2 className="font-bold text-gray-800">{titre}</h2>{children}</div>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function Champ({ label, children }) {
|
||||||
|
return <label className="block"><span className="text-xs text-gray-400 block mb-1">{label}</span>{children}</label>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function SelectDate({ value, onChange, dates }) {
|
||||||
|
if (!dates.length) {
|
||||||
|
return <div className="rounded-xl bg-gray-50 p-3 text-sm text-gray-500">Configurez le jour de réunion dans les paramètres du groupe.</div>;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<select value={value} onChange={(e) => onChange(e.target.value)} className={INPUT}>
|
||||||
|
{dates.map((d) => <option key={d} value={d}>{labelDateReunion(d)}</option>)}
|
||||||
|
</select>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function MembreSelect({ label, value, onChange, membres }) {
|
||||||
|
return (
|
||||||
|
<select value={value} onChange={(e) => onChange(e.target.value)} className={INPUT}>
|
||||||
|
<option value="">{label}</option>
|
||||||
|
{membres.map((m) => <option key={m.id} value={m.id}>{m.prenom}</option>)}
|
||||||
|
</select>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function DecisionItem({ p, membres, estExecutif, form, setForm, seconderPour, setSeconderPour, seconder, voter }) {
|
||||||
|
const [label, cls] = STATUTS[p.statut] || STATUTS.proposee;
|
||||||
|
const motif = (form.etant_donne_que || '').trim();
|
||||||
|
const decision = (form.groupe_a_decide_de || '').trim();
|
||||||
|
return (
|
||||||
|
<div className="rounded-xl bg-gray-50 p-3 space-y-2">
|
||||||
|
<div className="flex items-start justify-between gap-2">
|
||||||
|
<div className="min-w-0">
|
||||||
|
<p className="font-bold text-gray-800">{p.sujet}</p>
|
||||||
|
{p.description && <p className="text-sm text-gray-500">{p.description}</p>}
|
||||||
|
<p className="text-xs text-gray-400 mt-1">Proposée par {p.proposeur_prenom}{p.secondeur_prenom ? ` · Secondée par ${p.secondeur_prenom}` : ''}</p>
|
||||||
|
</div>
|
||||||
|
<span className={`text-xs px-2 py-1 rounded-full font-medium whitespace-nowrap ${cls}`}>{label}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{p.statut === 'proposee' && (
|
||||||
|
<div className="space-y-2">
|
||||||
|
{estExecutif && <textarea value={form.etant_donne_que || ''} onChange={(e) => setForm('etant_donne_que', e.target.value)} placeholder="ÉTANT DONNÉ QUE..." rows={2} className={`${INPUT} resize-none text-sm`} />}
|
||||||
|
<div className="grid grid-cols-2 gap-2">
|
||||||
|
{estExecutif && <button onClick={() => voter(p.id, 'rejetee')} disabled={!motif} className="p-2 bg-red-50 text-red-700 rounded-xl text-sm font-bold disabled:opacity-40">Rejeter</button>}
|
||||||
|
<button onClick={() => seconder(p.id)} className="p-2 bg-blue-50 text-blue-700 rounded-xl text-sm font-bold">Je seconde</button>
|
||||||
|
</div>
|
||||||
|
{estExecutif && (
|
||||||
|
<div className="grid grid-cols-[1fr_auto] gap-2">
|
||||||
|
<MembreSelect label="Seconder pour" value={seconderPour} onChange={setSeconderPour} membres={membres} />
|
||||||
|
<button onClick={() => seconder(p.id, seconderPour)} disabled={!seconderPour} className="px-4 bg-blue-600 text-white rounded-xl text-sm font-bold disabled:opacity-40">OK</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{p.statut === 'secondee' && estExecutif && (
|
||||||
|
<div className="space-y-2">
|
||||||
|
<textarea value={form.etant_donne_que || ''} onChange={(e) => setForm('etant_donne_que', e.target.value)} placeholder="ÉTANT DONNÉ QUE..." rows={2} className={`${INPUT} resize-none text-sm`} />
|
||||||
|
<textarea value={form.groupe_a_decide_de || ''} onChange={(e) => setForm('groupe_a_decide_de', e.target.value)} placeholder="LE GROUPE A DÉCIDÉ DE..." rows={2} className={`${INPUT} resize-none text-sm`} />
|
||||||
|
<div className="grid grid-cols-2 gap-2">
|
||||||
|
<button onClick={() => voter(p.id, 'rejetee')} disabled={!motif} className="p-2 bg-red-50 text-red-700 rounded-xl text-sm font-bold disabled:opacity-40">Rejeter</button>
|
||||||
|
<button onClick={() => voter(p.id, 'adoptee')} disabled={!motif || !decision} className="p-2 bg-green-50 text-green-700 rounded-xl text-sm font-bold disabled:opacity-40">Transformer</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
29
frontend/src/features/assemblees/assembleeUtils.js
Normal file
29
frontend/src/features/assemblees/assembleeUtils.js
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
export function textePvAuto(pvPrecedent, propositions, resolutionsJour, postesVacants) {
|
||||||
|
return [
|
||||||
|
"Ouverture de l'assemblée.",
|
||||||
|
'',
|
||||||
|
pvPrecedent ? `Lecture du procès-verbal précédent (${new Date(pvPrecedent.date_reunion).toLocaleDateString('fr-CA')}).` : 'Lecture du procès-verbal précédent.',
|
||||||
|
'',
|
||||||
|
'Réflexions en cours:',
|
||||||
|
...propositions.map((p) => `- ${p.sujet} (${p.proposeur_prenom} / ${p.secondeur_prenom || 'à confirmer'})`),
|
||||||
|
'',
|
||||||
|
'Décisions prises pendant cette assemblée:',
|
||||||
|
...resolutionsJour.map((r) => `- ${r.numero_resolution || ''} ${r.sujet}`.trim()),
|
||||||
|
'',
|
||||||
|
'Postes disponibles:',
|
||||||
|
...postesVacants.map((p) => `- ${p.nom}`),
|
||||||
|
].join('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
export function moisPrecedent(dateStr) {
|
||||||
|
const d = new Date(`${dateStr}T12:00:00`);
|
||||||
|
d.setMonth(d.getMonth() - 1);
|
||||||
|
const annee = d.getFullYear();
|
||||||
|
const mois = d.getMonth() + 1;
|
||||||
|
const noms = ['', 'Janvier', 'Février', 'Mars', 'Avril', 'Mai', 'Juin', 'Juillet', 'Août', 'Septembre', 'Octobre', 'Novembre', 'Décembre'];
|
||||||
|
return { annee, mois, libelle: `${noms[mois]} ${annee}` };
|
||||||
|
}
|
||||||
|
|
||||||
|
export function heure(dateIso) {
|
||||||
|
return dateIso ? new Date(dateIso).toLocaleTimeString('fr-CA', { hour: '2-digit', minute: '2-digit' }) : '--:--';
|
||||||
|
}
|
||||||
|
|
@ -3,18 +3,13 @@ import { api } from '../stores/api';
|
||||||
import { useStore } from '../stores/store';
|
import { useStore } from '../stores/store';
|
||||||
import PageHeader from '../components/PageHeader';
|
import PageHeader from '../components/PageHeader';
|
||||||
import { dateHeureReunionIso, datesAssembleesAffaires, dateLocale, labelDateReunion } from '../utils/datesReunion';
|
import { dateHeureReunionIso, datesAssembleesAffaires, dateLocale, labelDateReunion } from '../utils/datesReunion';
|
||||||
|
import { heure, moisPrecedent, textePvAuto } from '../features/assemblees/assembleeUtils';
|
||||||
|
import { boutonAction, Card, Champ, DecisionItem, INPUT, MembreSelect, SelectDate } from '../features/assemblees/assembleeUi';
|
||||||
|
|
||||||
const INPUT = 'w-full p-3 bg-gray-50 rounded-xl border-0 focus:ring-2 focus:ring-aa-blue';
|
|
||||||
const METHODES = [['VIREMENT', 'Virement'], ['CHEQUE', 'Chèque']];
|
const METHODES = [['VIREMENT', 'Virement'], ['CHEQUE', 'Chèque']];
|
||||||
const STATUTS = {
|
|
||||||
proposee: ['À seconder', 'bg-yellow-100 text-yellow-700'],
|
|
||||||
secondee: ['Secondée', 'bg-blue-100 text-blue-700'],
|
|
||||||
rejetee: ['Rejetée', 'bg-red-100 text-red-700'],
|
|
||||||
};
|
|
||||||
|
|
||||||
export default function AssembleeAffaires({ contexte = false }) {
|
export default function AssembleeAffaires({ contexte = false }) {
|
||||||
const { postes, groupe } = useStore();
|
const { estExecutif, groupe } = useStore();
|
||||||
const estExecutif = postes.some((p) => ['Sysadmin', 'RSG', 'RSG Substitut(e)', 'Trésorier(ière)', 'Secrétaire'].includes(p));
|
|
||||||
const choixDatesChrono = datesAssembleesAffaires(groupe);
|
const choixDatesChrono = datesAssembleesAffaires(groupe);
|
||||||
const choixDates = [...choixDatesChrono].reverse();
|
const choixDates = [...choixDatesChrono].reverse();
|
||||||
const prochaineDate = choixDatesChrono.find((d) => d >= dateLocale()) || choixDatesChrono[choixDatesChrono.length - 1] || dateLocale();
|
const prochaineDate = choixDatesChrono.find((d) => d >= dateLocale()) || choixDatesChrono[choixDatesChrono.length - 1] || dateLocale();
|
||||||
|
|
@ -513,140 +508,3 @@ export default function AssembleeAffaires({ contexte = false }) {
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function RapportAssemblee({ date, reunion, pvJour, pvPrecedent, propositions, resolutionsJour, postesVacants }) {
|
|
||||||
const contenuAuto = textePvAuto(pvPrecedent, propositions, resolutionsJour, postesVacants);
|
|
||||||
return (
|
|
||||||
<div className="bg-white rounded-2xl p-4 shadow-sm print:shadow-none print:rounded-none print:p-0">
|
|
||||||
<h2 className="text-xl font-bold text-gray-900">Rapport d'assemblée d'affaires</h2>
|
|
||||||
<p className="text-sm text-gray-500">{labelDateReunion(date)}</p>
|
|
||||||
<Section titre="Présences">
|
|
||||||
{reunion?.presences?.length ? reunion.presences.filter((p) => p.present).map((p) => <li key={p.membre_id}>{p.membre_prenom}</li>) : <li>Présences à cocher sur papier</li>}
|
|
||||||
</Section>
|
|
||||||
<Section titre="Réflexions en cours">
|
|
||||||
{propositions.length ? propositions.map((p) => <li key={p.id}>{p.sujet} ({p.proposeur_prenom} / {p.secondeur_prenom || 'à confirmer'})</li>) : <li>Aucune proposition secondée non résolue</li>}
|
|
||||||
</Section>
|
|
||||||
<Section titre="Décisions prises">
|
|
||||||
{resolutionsJour.length ? resolutionsJour.map((r) => <li key={r.id}>{r.numero_resolution} - {r.sujet}</li>) : <li>Aucune décision enregistrée pour cette date</li>}
|
|
||||||
</Section>
|
|
||||||
<Section titre="Postes disponibles">
|
|
||||||
{postesVacants.length ? postesVacants.map((p) => <li key={p.id}>{p.nom}</li>) : <li>Aucun poste vacant</li>}
|
|
||||||
</Section>
|
|
||||||
<div className="mt-4">
|
|
||||||
<h3 className="font-bold text-gray-800">Procès-verbal</h3>
|
|
||||||
<p className="text-sm whitespace-pre-wrap mt-1">{pvJour?.contenu || contenuAuto}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function textePvAuto(pvPrecedent, propositions, resolutionsJour, postesVacants) {
|
|
||||||
return [
|
|
||||||
"Ouverture de l'assemblée.",
|
|
||||||
'',
|
|
||||||
pvPrecedent ? `Lecture du procès-verbal précédent (${new Date(pvPrecedent.date_reunion).toLocaleDateString('fr-CA')}).` : 'Lecture du procès-verbal précédent.',
|
|
||||||
'',
|
|
||||||
'Réflexions en cours:',
|
|
||||||
...propositions.map((p) => `- ${p.sujet} (${p.proposeur_prenom} / ${p.secondeur_prenom || 'à confirmer'})`),
|
|
||||||
'',
|
|
||||||
'Décisions prises pendant cette assemblée:',
|
|
||||||
...resolutionsJour.map((r) => `- ${r.numero_resolution || ''} ${r.sujet}`.trim()),
|
|
||||||
'',
|
|
||||||
'Postes disponibles:',
|
|
||||||
...postesVacants.map((p) => `- ${p.nom}`),
|
|
||||||
].join('\n');
|
|
||||||
}
|
|
||||||
|
|
||||||
function moisPrecedent(dateStr) {
|
|
||||||
const d = new Date(`${dateStr}T12:00:00`);
|
|
||||||
d.setMonth(d.getMonth() - 1);
|
|
||||||
const annee = d.getFullYear();
|
|
||||||
const mois = d.getMonth() + 1;
|
|
||||||
const noms = ['', 'Janvier', 'Février', 'Mars', 'Avril', 'Mai', 'Juin', 'Juillet', 'Août', 'Septembre', 'Octobre', 'Novembre', 'Décembre'];
|
|
||||||
return { annee, mois, libelle: `${noms[mois]} ${annee}` };
|
|
||||||
}
|
|
||||||
|
|
||||||
function DecisionItem({ p, membres, estExecutif, form, setForm, seconderPour, setSeconderPour, seconder, voter }) {
|
|
||||||
const [label, cls] = STATUTS[p.statut] || STATUTS.proposee;
|
|
||||||
const motif = (form.etant_donne_que || '').trim();
|
|
||||||
const decision = (form.groupe_a_decide_de || '').trim();
|
|
||||||
return (
|
|
||||||
<div className="rounded-xl bg-gray-50 p-3 space-y-2">
|
|
||||||
<div className="flex items-start justify-between gap-2">
|
|
||||||
<div className="min-w-0">
|
|
||||||
<p className="font-bold text-gray-800">{p.sujet}</p>
|
|
||||||
{p.description && <p className="text-sm text-gray-500">{p.description}</p>}
|
|
||||||
<p className="text-xs text-gray-400 mt-1">Proposée par {p.proposeur_prenom}{p.secondeur_prenom ? ` · Secondée par ${p.secondeur_prenom}` : ''}</p>
|
|
||||||
</div>
|
|
||||||
<span className={`text-xs px-2 py-1 rounded-full font-medium whitespace-nowrap ${cls}`}>{label}</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{p.statut === 'proposee' && (
|
|
||||||
<div className="space-y-2">
|
|
||||||
{estExecutif && <textarea value={form.etant_donne_que || ''} onChange={(e) => setForm('etant_donne_que', e.target.value)} placeholder="ÉTANT DONNÉ QUE..." rows={2} className={`${INPUT} resize-none text-sm`} />}
|
|
||||||
<div className="grid grid-cols-2 gap-2">
|
|
||||||
{estExecutif && <button onClick={() => voter(p.id, 'rejetee')} disabled={!motif} className="p-2 bg-red-50 text-red-700 rounded-xl text-sm font-bold disabled:opacity-40">Rejeter</button>}
|
|
||||||
<button onClick={() => seconder(p.id)} className="p-2 bg-blue-50 text-blue-700 rounded-xl text-sm font-bold">Je seconde</button>
|
|
||||||
</div>
|
|
||||||
{estExecutif && (
|
|
||||||
<div className="grid grid-cols-[1fr_auto] gap-2">
|
|
||||||
<MembreSelect label="Seconder pour" value={seconderPour} onChange={setSeconderPour} membres={membres} />
|
|
||||||
<button onClick={() => seconder(p.id, seconderPour)} disabled={!seconderPour} className="px-4 bg-blue-600 text-white rounded-xl text-sm font-bold disabled:opacity-40">OK</button>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{p.statut === 'secondee' && estExecutif && (
|
|
||||||
<div className="space-y-2">
|
|
||||||
<textarea value={form.etant_donne_que || ''} onChange={(e) => setForm('etant_donne_que', e.target.value)} placeholder="ÉTANT DONNÉ QUE..." rows={2} className={`${INPUT} resize-none text-sm`} />
|
|
||||||
<textarea value={form.groupe_a_decide_de || ''} onChange={(e) => setForm('groupe_a_decide_de', e.target.value)} placeholder="LE GROUPE A DÉCIDÉ DE..." rows={2} className={`${INPUT} resize-none text-sm`} />
|
|
||||||
<div className="grid grid-cols-2 gap-2">
|
|
||||||
<button onClick={() => voter(p.id, 'rejetee')} disabled={!motif} className="p-2 bg-red-50 text-red-700 rounded-xl text-sm font-bold disabled:opacity-40">Rejeter</button>
|
|
||||||
<button onClick={() => voter(p.id, 'adoptee')} disabled={!motif || !decision} className="p-2 bg-green-50 text-green-700 rounded-xl text-sm font-bold disabled:opacity-40">Transformer</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function boutonAction(actif) {
|
|
||||||
return `p-3 rounded-xl text-sm font-bold ${actif ? 'bg-aa-blue text-white' : 'bg-gray-50 text-gray-700'}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
function heure(dateIso) {
|
|
||||||
return dateIso ? new Date(dateIso).toLocaleTimeString('fr-CA', { hour: '2-digit', minute: '2-digit' }) : '--:--';
|
|
||||||
}
|
|
||||||
|
|
||||||
function Card({ titre, children }) {
|
|
||||||
return <div className="bg-white rounded-2xl p-4 shadow-sm space-y-3"><h2 className="font-bold text-gray-800">{titre}</h2>{children}</div>;
|
|
||||||
}
|
|
||||||
|
|
||||||
function Champ({ label, children }) {
|
|
||||||
return <label className="block"><span className="text-xs text-gray-400 block mb-1">{label}</span>{children}</label>;
|
|
||||||
}
|
|
||||||
|
|
||||||
function Section({ titre, children }) {
|
|
||||||
return <div className="mt-4"><h3 className="font-bold text-gray-800">{titre}</h3><ul className="list-disc ml-5 text-sm mt-1 space-y-1">{children}</ul></div>;
|
|
||||||
}
|
|
||||||
|
|
||||||
function SelectDate({ value, onChange, dates }) {
|
|
||||||
if (!dates.length) {
|
|
||||||
return <div className="rounded-xl bg-gray-50 p-3 text-sm text-gray-500">Configurez le jour de réunion dans les paramètres du groupe.</div>;
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<select value={value} onChange={(e) => onChange(e.target.value)} className={INPUT}>
|
|
||||||
{dates.map((d) => <option key={d} value={d}>{labelDateReunion(d)}</option>)}
|
|
||||||
</select>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function MembreSelect({ label, value, onChange, membres }) {
|
|
||||||
return (
|
|
||||||
<select value={value} onChange={(e) => onChange(e.target.value)} className={INPUT}>
|
|
||||||
<option value="">{label}</option>
|
|
||||||
{membres.map((m) => <option key={m.id} value={m.id}>{m.prenom}</option>)}
|
|
||||||
</select>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ const TYPES = {
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function Decisions() {
|
export default function Decisions() {
|
||||||
const { postes, estExecutif } = useStore();
|
const { estExecutif } = useStore();
|
||||||
const [propositions, setPropositions] = useState([]);
|
const [propositions, setPropositions] = useState([]);
|
||||||
const [resolutions, setResolutions] = useState([]);
|
const [resolutions, setResolutions] = useState([]);
|
||||||
const [membres, setMembres] = useState([]);
|
const [membres, setMembres] = useState([]);
|
||||||
|
|
@ -34,9 +34,7 @@ export default function Decisions() {
|
||||||
const [seconderPour, setSeconderPour] = useState({});
|
const [seconderPour, setSeconderPour] = useState({});
|
||||||
const [decisionOuverte, setDecisionOuverte] = useState(null);
|
const [decisionOuverte, setDecisionOuverte] = useState(null);
|
||||||
|
|
||||||
const peutExecutif = estExecutif || postes.some((p) =>
|
const peutExecutif = estExecutif;
|
||||||
['RSG', 'RSG Substitut(e)', 'Trésorier(ière)', 'Secrétaire'].includes(p)
|
|
||||||
);
|
|
||||||
|
|
||||||
const charger = async () => {
|
const charger = async () => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ const TYPE_STYLE = {
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function Evenements() {
|
export default function Evenements() {
|
||||||
const { postes } = useStore();
|
const { estExecutif } = useStore();
|
||||||
const [evenements, setEvenements] = useState([]);
|
const [evenements, setEvenements] = useState([]);
|
||||||
const [filtres, setFiltres] = useState(['anniversaire', 'adhoc']);
|
const [filtres, setFiltres] = useState(['anniversaire', 'adhoc']);
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
|
|
@ -23,10 +23,6 @@ export default function Evenements() {
|
||||||
const [description, setDescription] = useState('');
|
const [description, setDescription] = useState('');
|
||||||
const [lieu, setLieu] = useState('');
|
const [lieu, setLieu] = useState('');
|
||||||
|
|
||||||
const estExecutif = postes.some((p) =>
|
|
||||||
['RSG', 'RSG Substitut(e)', 'Trésorier(ière)', 'Secrétaire'].includes(p)
|
|
||||||
);
|
|
||||||
|
|
||||||
const charger = async () => {
|
const charger = async () => {
|
||||||
try {
|
try {
|
||||||
const data = await api.get('/evenements/?jours=365&jours_avant=365');
|
const data = await api.get('/evenements/?jours=365&jours_avant=365');
|
||||||
|
|
|
||||||
|
|
@ -8,8 +8,7 @@ const TYPES_JETONS = [['desir', 'Désir'], ['24h', '24 heures'], ['1mois', '1 mo
|
||||||
const INPUT = 'w-full p-3 bg-gray-50 rounded-xl border-0 focus:ring-2 focus:ring-aa-blue';
|
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 }) {
|
export default function ReunionHebdomadaire({ contexte = false }) {
|
||||||
const { postes, groupe } = useStore();
|
const { estExecutif, groupe } = useStore();
|
||||||
const estExecutif = postes.some((p) => ['Sysadmin', 'RSG', 'RSG Substitut(e)', 'Trésorier(ière)', 'Secrétaire'].includes(p));
|
|
||||||
const choixDatesChrono = datesReunion(groupe, { joursAvant: 365, joursApres: 180 });
|
const choixDatesChrono = datesReunion(groupe, { joursAvant: 365, joursApres: 180 });
|
||||||
const choixDates = [...choixDatesChrono].reverse();
|
const choixDates = [...choixDatesChrono].reverse();
|
||||||
const prochaineDate = choixDatesChrono.find((d) => d >= dateLocale()) || choixDatesChrono[choixDatesChrono.length - 1] || dateLocale();
|
const prochaineDate = choixDatesChrono.find((d) => d >= dateLocale()) || choixDatesChrono[choixDatesChrono.length - 1] || dateLocale();
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue