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.
87 lines
4.6 KiB
JavaScript
87 lines
4.6 KiB
JavaScript
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>
|
|
);
|
|
}
|