Améliorations
This commit is contained in:
1 parent
8d104f2058
commit
bab7a79b63
4 files changed
+147
-5
No files matched your search
@@ -48,6 +48,17 @@ export default function InvChart({ rows, remboursements, reinvestissements, plat
|
||||
const currentYear = String(new Date().getFullYear());
|
||||
const cutoffStr = platYear && platYear !== currentYear ? `${platYear}-12-31` : todayStr;
|
||||
|
||||
// Coupure appliquée aux MONTANTS cumulés (formule "Capital investi" unifiée,
|
||||
// cf. project_invchart_capital_encours) : bornée au 31/12/{platYear} pour une
|
||||
// année passée révolue, ILLIMITÉE sinon — exactement comme les totaux KPI
|
||||
// (Plateformes.jsx / Investissements.jsx, cutoff = selectedYear ? ... : null).
|
||||
// Sans ça, un remboursement déjà saisi avec une date de règlement future
|
||||
// (ex. Olivier enregistre le remboursement final dès que la plateforme
|
||||
// l'annonce, avec la date de règlement communiquée, avant qu'elle soit
|
||||
// atteinte) est compté par le KPI mais pas encore par le graphique, qui
|
||||
// affiche alors un capital investi supérieur au KPI de exactement ce montant.
|
||||
const dataCutoffStr = platYear && platYear !== currentYear ? `${platYear}-12-31` : null;
|
||||
|
||||
/* ── 1. Courbe capital en cours = investissements − capital remboursé ── */
|
||||
const allPoints = useMemo(() => {
|
||||
if (!rows?.length) return [];
|
||||
@@ -61,7 +72,7 @@ export default function InvChart({ rows, remboursements, reinvestissements, plat
|
||||
// +montant_investi à chaque date de souscription
|
||||
for (const r of rows) {
|
||||
const d = r.date_souscription?.slice(0, 10);
|
||||
if (!d || d > cutoffStr) continue;
|
||||
if (!d || (dataCutoffStr && d > dataCutoffStr)) continue;
|
||||
deltas[d] = (deltas[d] || 0) + (r.montant_investi ?? 0);
|
||||
}
|
||||
|
||||
@@ -70,7 +81,7 @@ export default function InvChart({ rows, remboursements, reinvestissements, plat
|
||||
for (const rv of reinvestissements) {
|
||||
if (!rowIds.has(rv.investissement_id)) continue;
|
||||
const d = rv.date_reinvestissement?.slice(0, 10);
|
||||
if (!d || !rv.montant || d > cutoffStr) continue;
|
||||
if (!d || !rv.montant || (dataCutoffStr && d > dataCutoffStr)) continue;
|
||||
deltas[d] = (deltas[d] || 0) + rv.montant;
|
||||
}
|
||||
}
|
||||
@@ -80,18 +91,33 @@ export default function InvChart({ rows, remboursements, reinvestissements, plat
|
||||
for (const rb of remboursements) {
|
||||
if (!rowIds.has(rb.investissement_id)) continue;
|
||||
const d = rb.date_remb?.slice(0, 10);
|
||||
if (!d || !rb.capital || d > cutoffStr) continue;
|
||||
if (!d || !rb.capital || (dataCutoffStr && d > dataCutoffStr)) continue;
|
||||
deltas[d] = (deltas[d] || 0) - rb.capital;
|
||||
}
|
||||
}
|
||||
|
||||
// Tri chronologique + cumul
|
||||
let cum = 0;
|
||||
return Object.keys(deltas).sort().map(date => {
|
||||
const cumulated = Object.keys(deltas).sort().map(date => {
|
||||
cum += deltas[date];
|
||||
return { date, value: Math.max(0, cum) };
|
||||
});
|
||||
}, [rows, remboursements, reinvestissements, cutoffStr]);
|
||||
|
||||
// Un point dont la date dépasse aujourd'hui (remboursement/réinvestissement
|
||||
// saisi par avance, cf. dataCutoffStr) est regroupé sur la date du jour pour
|
||||
// l'affichage : la courbe ne va jamais visuellement au-delà d'aujourd'hui,
|
||||
// mais la valeur cumulée (déjà correcte, alignée sur le KPI) est conservée.
|
||||
const clamped = [];
|
||||
for (const p of cumulated) {
|
||||
const d = p.date > todayStr ? todayStr : p.date;
|
||||
if (clamped.length && clamped[clamped.length - 1].date === d) {
|
||||
clamped[clamped.length - 1] = { date: d, value: p.value };
|
||||
} else {
|
||||
clamped.push({ date: d, value: p.value });
|
||||
}
|
||||
}
|
||||
return clamped;
|
||||
}, [rows, remboursements, reinvestissements, dataCutoffStr, todayStr]);
|
||||
|
||||
const currentValue = allPoints.length ? allPoints[allPoints.length - 1].value : 0;
|
||||
|
||||
|
||||
@@ -35,6 +35,7 @@ function SectionHeader({ title, description }) {
|
||||
const DEFAULT = {
|
||||
appName: 'Crowdlending Tracker',
|
||||
appUrl: '',
|
||||
environnement: 'developpement',
|
||||
mcpUrl: '',
|
||||
allowRegistration: true,
|
||||
minPasswordLength: 8,
|
||||
@@ -42,6 +43,12 @@ const DEFAULT = {
|
||||
documentsQuotaCount: 500,
|
||||
};
|
||||
|
||||
const ENVIRONNEMENTS = [
|
||||
{ value: 'production', label: 'Production' },
|
||||
{ value: 'qualification', label: 'Qualification' },
|
||||
{ value: 'developpement', label: 'Développement' },
|
||||
];
|
||||
|
||||
export default function GeneralSection() {
|
||||
const [form, setForm] = useState(DEFAULT);
|
||||
const [saved, setSaved] = useState(null); // 'ok' | 'err' | null
|
||||
@@ -58,6 +65,7 @@ export default function GeneralSection() {
|
||||
setForm({
|
||||
appName: dd.appName || 'Crowdlending Tracker',
|
||||
appUrl: dd.appUrl || '',
|
||||
environnement: dd.environnement || 'developpement',
|
||||
mcpUrl: dd.mcpUrl || '',
|
||||
allowRegistration: dd.allowRegistration !== false,
|
||||
minPasswordLength: dd.minPasswordLength || 8,
|
||||
@@ -77,6 +85,7 @@ export default function GeneralSection() {
|
||||
await api.patch('/admin/general', {
|
||||
appName: form.appName.trim(),
|
||||
appUrl: form.appUrl.trim(),
|
||||
environnement: form.environnement,
|
||||
mcpUrl: form.mcpUrl.trim(),
|
||||
allowRegistration: form.allowRegistration,
|
||||
minPasswordLength: form.minPasswordLength,
|
||||
@@ -131,6 +140,18 @@ export default function GeneralSection() {
|
||||
/>
|
||||
</SettingRow>
|
||||
|
||||
<SettingRow label="Environnement" description="Identifie cette instance (affiché dans l'interface). Choisi manuellement par un administrateur : conservé tel quel lors d'une restauration de base copiée depuis un autre environnement.">
|
||||
<select
|
||||
value={form.environnement}
|
||||
onChange={e => set('environnement', e.target.value)}
|
||||
style={{ width: '100%' }}
|
||||
>
|
||||
{ENVIRONNEMENTS.map(({ value, label }) => (
|
||||
<option key={value} value={value}>{label}</option>
|
||||
))}
|
||||
</select>
|
||||
</SettingRow>
|
||||
|
||||
<SettingRow label="URL du serveur MCP" description={`Point d'entrée public du serveur MCP (endpoint /mcp inclus). Inclure le protocole (https://).${import.meta.env.DEV ? ' Valeur forcée en développement local (ignore celle de la base, potentiellement une copie de la prod).' : ''}`}>
|
||||
<input
|
||||
className="form-input"
|
||||
|
||||
Reference in new issue
Block a user