Modification modale plateforme

This commit is contained in:
ocroguennec committed 2026-08-30 18:24:55 +02:00
1 parent 0c99887846
commit 9ea078302d
7 files changed
+437 -222

No files matched your search

+163 -113
View File
@@ -1,9 +1,9 @@
import { useState, useEffect, useCallback, useRef, useMemo } from 'react';
import { useLocation, useNavigate } from 'react-router-dom';
import { api } from '../api.js';
import InvSelect from '../components/InvSelect.jsx';
import ConfirmModal from '../components/ConfirmModal.jsx';
import Modal from '../components/Modal.jsx';
import PlateformeFormModal from '../components/PlateformeFormModal.jsx';
import ResultBanner from '../components/ResultBanner.jsx';
import CountrySelect, { COUNTRIES, FlagIcon } from '../components/CountrySelect.jsx';
import { usePagination } from '../hooks/usePagination.js';
@@ -45,7 +45,8 @@ function IconUpload() {
const countryLabel = code => COUNTRIES.find(c => c.code === code)?.name ?? code ?? '—';
const FISCALITE_LABELS = { flat_tax: 'Flat tax (30%)', sans_fiscalite_locale: 'Sans fiscalité locale', avec_fiscalite_locale: 'Avec fiscalité locale' };
const CATEGORIE_PLATEFORME_LABELS = { psfp: 'Prestataire de Services de Financement Participatif', psan: 'Prestataire de Services sur Actifs Numériques' };
const EMPTY_REF = { nom: '', url: '', domiciliation: 'france', fiscalite: 'flat_tax', taux_fiscalite_locale: '', type_produit_fiscal: '2TT', categorie_plateforme: 'psfp', methode_remboursement: 'portefeuille', type_pret_defaut: '', freq_interets_defaut: '', description: '', categories: [], categories_inv_ids: [], secteurs_inv_ids: [] };
const FRAIS_PLATEFORME_LABELS = { aucun: 'Aucun frais', entree_gestion_directe: "Frais d'entrée + Frais de gestion (Prélèvement direct)" };
const EMPTY_REF = { nom: '', url: '', domiciliation: 'france', fiscalite: 'flat_tax', taux_fiscalite_locale: '', type_produit_fiscal: '2TT', categorie_plateforme: 'psfp', frais_plateforme: 'aucun', methode_remboursement: 'portefeuille', type_pret_defaut: '', freq_interets_defaut: '', description: '', categories: [], categories_inv_ids: [], secteurs_inv_ids: [] };
/* ── Modal création / édition référentiel ─────────────────────── */
function RefModal({ row, onClose, onSaved }) {
@@ -61,6 +62,7 @@ function RefModal({ row, onClose, onSaved }) {
taux_fiscalite_locale: row.taux_fiscalite_locale != null ? String(row.taux_fiscalite_locale) : '',
type_produit_fiscal: row.type_produit_fiscal || '2TT',
categorie_plateforme: row.categorie_plateforme || 'psfp',
frais_plateforme: row.frais_plateforme || 'aucun',
methode_remboursement: row.methode_remboursement || 'portefeuille',
type_pret_defaut: row.type_pret_defaut || '',
freq_interets_defaut: row.freq_interets_defaut || '',
@@ -96,6 +98,7 @@ function RefModal({ row, onClose, onSaved }) {
? Number(form.taux_fiscalite_locale) : null,
type_produit_fiscal: form.type_produit_fiscal,
categorie_plateforme: form.categorie_plateforme || 'psfp',
frais_plateforme: form.frais_plateforme || 'aucun',
methode_remboursement: form.methode_remboursement || 'portefeuille',
type_pret_defaut: form.type_pret_defaut || null,
freq_interets_defaut: form.freq_interets_defaut || null,
@@ -143,118 +146,165 @@ function RefModal({ row, onClose, onSaved }) {
finally { setSaving(false); }
};
return (
<Modal open title={isNew ? 'Ajouter au référentiel' : `Modifier — ${row.nom}`}
onClose={onClose} width={640}
footer={<>
<button type="button" onClick={onClose} disabled={saving}>Annuler</button>
<button className="primary" form="ref-form" type="submit" disabled={saving}>{saving ? '…' : isNew ? 'Créer' : 'Enregistrer'}</button>
</>}>
<form id="ref-form" onSubmit={handleSubmit}>
<div style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
<div><label>Nom *</label><input value={form.nom} onChange={e => set('nom', e.target.value)} placeholder="Ex : October, Lendosphere…" /></div>
<div><label>URL</label><input value={form.url} onChange={e => set('url', e.target.value)} placeholder="https://…" /></div>
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 12 }}>
<div>
<label>Domiciliation</label>
<CountrySelect
value={form.domiciliation}
onChange={code => set('domiciliation', code)}
showCode
/>
</div>
<div>
<label>Fiscalité</label>
{form.domiciliation === 'FR'
? <input value="Flat tax (automatique)" disabled />
: <select value={form.fiscalite} onChange={e => set('fiscalite', e.target.value)}>
<option value="sans_fiscalite_locale">Sans fiscalité locale</option>
<option value="avec_fiscalite_locale">Avec fiscalité locale</option>
</select>}
</div>
</div>
{fiscalite === 'avec_fiscalite_locale' && (
<div><label>Taux fiscalité locale (%)</label>
<input type="number" min="0" max="100" step="0.1" value={form.taux_fiscalite_locale} onChange={e => set('taux_fiscalite_locale', e.target.value)} placeholder="Ex : 15" /></div>
)}
<div>
<label>Type produit fiscal</label>
<select value={form.type_produit_fiscal} onChange={e => set('type_produit_fiscal', e.target.value)}>
<option value="2TT">2TT — Intérêts</option>
<option value="2TR">2TR — Revenus assimilés</option>
</select>
</div>
<div>
<label>Catégorie de plateforme</label>
<select value={form.categorie_plateforme || 'psfp'} onChange={e => set('categorie_plateforme', e.target.value)}>
<option value="psfp">{CATEGORIE_PLATEFORME_LABELS.psfp}</option>
<option value="psan">{CATEGORIE_PLATEFORME_LABELS.psan}</option>
</select>
</div>
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: 12 }}>
<div>
<label>Méthode de remboursement</label>
<select value={form.methode_remboursement} onChange={e => set('methode_remboursement', e.target.value)}>
<option value="portefeuille">Porte-monnaie</option>
<option value="compte_courant">Compte courant</option>
<option value="choix_investisseur">Choix investisseur</option>
</select>
</div>
<div>
<label>Type de prêt (défaut)</label>
<select value={form.type_pret_defaut} onChange={e => {
const t = e.target.value;
set('type_pret_defaut', t);
if (t === 'differe') set('freq_interets_defaut', 'in_fine');
else if (form.freq_interets_defaut === 'in_fine') set('freq_interets_defaut', 'mensuel');
}}>
<option value="">— Non défini —</option>
<option value="in_fine">In fine</option>
<option value="amortissable">Amortissable</option>
<option value="differe">Différé</option>
</select>
</div>
<div>
<label>Périodicité (défaut)</label>
<select value={form.type_pret_defaut === 'differe' ? 'in_fine' : form.freq_interets_defaut}
disabled={form.type_pret_defaut === 'differe'}
onChange={e => set('freq_interets_defaut', e.target.value)}>
{form.type_pret_defaut === 'differe'
? <option value="in_fine">In fine (unique)</option>
: <>
<option value="">— Non définie —</option>
<option value="mensuel">Mensuelle</option>
<option value="trimestriel">Trimestrielle</option>
</>
}
</select>
</div>
</div>
<div><label>Description</label>
<textarea value={form.description} onChange={e => set('description', e.target.value)}
style={{ minHeight: 60, resize: 'vertical' }} placeholder="Informations générales sur la plateforme…" /></div>
<div>
<label>Catégories d'investissement</label>
<InvSelect
items={catsInv}
selected={form.categories_inv_ids || []}
onChange={ids => set('categories_inv_ids', ids)}
emptyLabel="Aucune catégorie d'investissement"
/>
</div>
<div>
<label>Secteurs d'investissement</label>
<InvSelect
items={secteursInv}
selected={form.secteurs_inv_ids || []}
onChange={ids => set('secteurs_inv_ids', ids)}
emptyLabel="Aucun secteur d'investissement"
/>
</div>
const step1 = (
<div style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
<div><label>Nom *</label><input value={form.nom} onChange={e => set('nom', e.target.value)} placeholder="Ex : October, Lendosphere…" /></div>
<div><label>URL</label><input value={form.url} onChange={e => set('url', e.target.value)} placeholder="https://…" /></div>
<div>
<label>Domiciliation</label>
<CountrySelect
value={form.domiciliation}
onChange={code => set('domiciliation', code)}
showCode
/>
</div>
<div>
<label>Catégorie de plateforme</label>
<select value={form.categorie_plateforme || 'psfp'} onChange={e => set('categorie_plateforme', e.target.value)}>
<option value="psfp">{CATEGORIE_PLATEFORME_LABELS.psfp}</option>
<option value="psan">{CATEGORIE_PLATEFORME_LABELS.psan}</option>
</select>
</div>
<div><label>Description</label>
<textarea value={form.description} onChange={e => set('description', e.target.value)}
style={{ minHeight: 180, resize: 'vertical' }} placeholder="Informations générales sur la plateforme…" /></div>
</div>
);
const step2 = (
<div style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
<div>
<label>Fiscalité</label>
{form.domiciliation === 'FR'
? <input value="Flat tax (automatique)" disabled />
: <select value={form.fiscalite} onChange={e => set('fiscalite', e.target.value)}>
<option value="sans_fiscalite_locale">Sans fiscalité locale</option>
<option value="avec_fiscalite_locale">Avec fiscalité locale</option>
</select>}
</div>
{fiscalite === 'avec_fiscalite_locale' && (
<div><label>Taux fiscalité locale (%)</label>
<input type="number" min="0" max="100" step="0.1" value={form.taux_fiscalite_locale} onChange={e => set('taux_fiscalite_locale', e.target.value)} placeholder="Ex : 15" /></div>
)}
<div>
<label>Type produit fiscal</label>
<select value={form.type_produit_fiscal} onChange={e => set('type_produit_fiscal', e.target.value)}>
<option value="2TT">2TT — Intérêts</option>
<option value="2TR">2TR — Revenus assimilés</option>
</select>
</div>
</div>
);
const step3 = (
<div style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: 12 }}>
<div>
<label>Méthode de remboursement</label>
<select value={form.methode_remboursement} onChange={e => set('methode_remboursement', e.target.value)}>
<option value="portefeuille">Porte-monnaie</option>
<option value="compte_courant">Compte courant</option>
<option value="choix_investisseur">Choix investisseur</option>
</select>
</div>
{err && <div className="error" style={{ marginTop: 12 }}>{err}</div>}
</form>
</Modal>
<div>
<label>Type de prêt (défaut)</label>
<select value={form.type_pret_defaut} onChange={e => {
const t = e.target.value;
set('type_pret_defaut', t);
if (t === 'differe') set('freq_interets_defaut', 'in_fine');
else if (form.freq_interets_defaut === 'in_fine') set('freq_interets_defaut', 'mensuel');
}}>
<option value="">— Non défini —</option>
<option value="in_fine">In fine</option>
<option value="amortissable">Amortissable</option>
<option value="differe">Différé</option>
</select>
</div>
<div>
<label>Périodicité (défaut)</label>
<select value={form.type_pret_defaut === 'differe' ? 'in_fine' : form.freq_interets_defaut}
disabled={form.type_pret_defaut === 'differe'}
onChange={e => set('freq_interets_defaut', e.target.value)}>
{form.type_pret_defaut === 'differe'
? <option value="in_fine">In fine (unique)</option>
: <>
<option value="">— Non définie —</option>
<option value="mensuel">Mensuelle</option>
<option value="trimestriel">Trimestrielle</option>
</>
}
</select>
</div>
</div>
<div>
<label>Frais liés à la plateforme</label>
<select value={form.frais_plateforme || 'aucun'} onChange={e => set('frais_plateforme', e.target.value)}>
<option value="aucun">{FRAIS_PLATEFORME_LABELS.aucun}</option>
<option value="entree_gestion_directe">{FRAIS_PLATEFORME_LABELS.entree_gestion_directe}</option>
</select>
</div>
</div>
);
const step4 = (
<div style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
<div>
<label>Catégories</label>
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 6 }}>
{catsInv.map(c => {
const checked = (form.categories_inv_ids || []).includes(c.id);
return (
<label key={c.id} style={{ display: 'flex', alignItems: 'center', gap: 4, cursor: 'pointer' }}>
<input type="checkbox" checked={checked} style={{ width: 'auto' }}
onChange={e => set('categories_inv_ids', e.target.checked
? [...(form.categories_inv_ids || []), c.id]
: (form.categories_inv_ids || []).filter(id => id !== c.id))} />
<span className="chip-cat" style={{ fontSize: 11 }}>{c.nom}</span>
</label>
);
})}
</div>
</div>
<div>
<label>Secteurs</label>
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 6 }}>
{secteursInv.map(s => {
const checked = (form.secteurs_inv_ids || []).includes(s.id);
return (
<label key={s.id} style={{ display: 'flex', alignItems: 'center', gap: 4, cursor: 'pointer' }}>
<input type="checkbox" checked={checked} style={{ width: 'auto' }}
onChange={e => set('secteurs_inv_ids', e.target.checked
? [...(form.secteurs_inv_ids || []), s.id]
: (form.secteurs_inv_ids || []).filter(id => id !== s.id))} />
<span className="chip-sect" style={{ fontSize: 11 }}>{s.nom}</span>
</label>
);
})}
</div>
</div>
</div>
);
const step5 = (
<div style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
<div className="text-muted" style={{ fontSize: 13 }}>
Le logo et les autres informations de profil (régulateur, statistiques publiques, réseaux sociaux…) se gèrent depuis la fiche détaillée de la plateforme, après création.
</div>
</div>
);
return (
<PlateformeFormModal
title={isNew ? 'Ajouter au référentiel' : `Modifier — ${row.nom}`}
onClose={onClose}
onSubmit={handleSubmit}
saving={saving}
err={err}
submitLabel={isNew ? 'Créer' : 'Enregistrer'}
width={640}
step1={step1} step2={step2} step3={step3} step4={step4} step5={step5}
/>
);
}
+2
View File
@@ -176,6 +176,7 @@ export default function PlatformeProfile() {
taux_fiscalite_locale: form.taux_fiscalite_locale !== '' && form.taux_fiscalite_locale != null ? Number(form.taux_fiscalite_locale) : null,
type_produit_fiscal: form.type_produit_fiscal || '2TT',
categorie_plateforme: form.categorie_plateforme || 'psfp',
frais_plateforme: form.frais_plateforme || 'aucun',
logo_filename: data.logo_filename ?? null,
description: form.description || null,
categories: data.categories || [],
@@ -788,6 +789,7 @@ function ProfilImportBlock({ data, id, onApplied, catsInv = [], secteursInv = []
taux_fiscalite_locale: data.taux_fiscalite_locale ?? null,
type_produit_fiscal: data.type_produit_fiscal || '2TT',
categorie_plateforme: data.categorie_plateforme || 'psfp',
frais_plateforme: data.frais_plateforme || 'aucun',
logo_filename: data.logo_filename ?? null,
description: data.description || null,
categories: data.categories || [],
@@ -3,6 +3,7 @@ import { useNavigate, useSearchParams } from 'react-router-dom';
import { api } from '../../api.js';
import InvSelect from '../../components/InvSelect.jsx';
import Modal from '../../components/Modal.jsx';
import PlateformeFormModal from '../../components/PlateformeFormModal.jsx';
import ConfirmModal from '../../components/ConfirmModal.jsx';
import CountrySelect, { COUNTRIES, FlagIcon } from '../../components/CountrySelect.jsx';
import ResultBanner from '../../components/ResultBanner.jsx';
@@ -25,6 +26,7 @@ function localToday() {
const countryLabel = code => COUNTRIES.find(c => c.code === code)?.name ?? code ?? '—';
const CATEGORIE_PLATEFORME_LABELS = { psfp: 'Prestataire de Services de Financement Participatif', psan: 'Prestataire de Services sur Actifs Numériques' };
const FRAIS_PLATEFORME_LABELS = { aucun: 'Aucun frais', entree_gestion_directe: "Frais d'entrée + Frais de gestion (Prélèvement direct)" };
const FISCALITE_LABELS = {
flat_tax: 'Flat Tax',
sans_fiscalite_locale: 'Sans fiscalité locale',
@@ -52,7 +54,7 @@ function fmtFiscalite(p) {
return base;
}
const EMPTY_PLAT = { nom: '', url: '', domiciliation: 'france', fiscalite: 'flat_tax', taux_fiscalite_locale: '', type_produit_fiscal: '2TT', categorie_plateforme: 'psfp', methode_remboursement: 'portefeuille', investisseur_id: null, date_ouverture: '', logo_filename: null, type_pret_defaut: '', freq_interets_defaut: '', referentiel_id: null };
const EMPTY_PLAT = { nom: '', url: '', domiciliation: 'france', fiscalite: 'flat_tax', taux_fiscalite_locale: '', type_produit_fiscal: '2TT', categorie_plateforme: 'psfp', frais_plateforme: 'aucun', methode_remboursement: 'portefeuille', investisseur_id: null, date_ouverture: '', logo_filename: null, type_pret_defaut: '', freq_interets_defaut: '', referentiel_id: null };
const LOGO_BASE = (import.meta.env.VITE_API_URL || '/api').replace(/\/api$/, '') + '/api/logos/';
const logoUrl = (filename) => filename ? LOGO_BASE + filename : null;
@@ -491,7 +493,7 @@ function PlatPickerModal({ open, onClose, referentiel, onSelect, onManual, err }
}
/* ── Formulaire plateforme partagé (création + édition) ─────────── */
function PlatForm({ state, setter, logoFile, setLogoFile, logoPreview, setLogoPreview,
function platFormSteps({ state, setter, logoFile, setLogoFile, logoPreview, setLogoPreview,
investisseurs, referentiel, categoriesInv, secteursInv, isEdit = false, onDelLogo }) {
const isFrance = state.domiciliation === 'FR';
@@ -513,14 +515,8 @@ function PlatForm({ state, setter, logoFile, setLogoFile, logoPreview, setLogoPr
</div>
);
return (
<>
{/* Nom */}
{row('Nom *', (
<input required value={state.nom} onChange={e => setter(s => ({ ...s, nom: e.target.value }))}
placeholder="Ex: Lendosphere" style={{ width: '100%', boxSizing: 'border-box' }} />
))}
const step1 = (
<div style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
{/* Référentiel */}
{referentiel.length > 0 && row('Lier au référentiel', (
<select value={state.referentiel_id ?? ''} onChange={e => {
@@ -533,6 +529,12 @@ function PlatForm({ state, setter, logoFile, setLogoFile, logoPreview, setLogoPr
</select>
))}
{/* Nom */}
{row('Nom *', (
<input required value={state.nom} onChange={e => setter(s => ({ ...s, nom: e.target.value }))}
placeholder="Ex: Lendosphere" style={{ width: '100%', boxSizing: 'border-box' }} />
))}
{/* URL */}
{row('URL', (
<input type="url" value={state.url || ''} onChange={e => setter(s => ({ ...s, url: e.target.value }))}
@@ -548,6 +550,36 @@ function PlatForm({ state, setter, logoFile, setLogoFile, logoPreview, setLogoPr
/>
))}
{/* Catégorie de plateforme */}
{row('Catégorie de plateforme', (
<select value={state.categorie_plateforme || 'psfp'} onChange={e => setter(s => ({ ...s, categorie_plateforme: e.target.value }))}
style={{ width: '100%' }}>
<option value="psfp">{CATEGORIE_PLATEFORME_LABELS.psfp}</option>
<option value="psan">{CATEGORIE_PLATEFORME_LABELS.psan}</option>
</select>
))}
{/* Détenteur */}
{investisseurs.length > 1 && row('Détenteur', (
<select value={state.investisseur_id ?? ''} style={{ width: '100%' }}
onChange={e => setter(s => ({ ...s, investisseur_id: e.target.value ? Number(e.target.value) : null }))}>
<option value="">— Sélectionner —</option>
{investisseurs.map(inv => (
<option key={inv.id} value={inv.id}>{memberLabel(inv)}</option>
))}
</select>
))}
{/* Date d'ouverture */}
{row("Date d'ouverture du compte", (
<input type="date" value={state.date_ouverture || ''} onChange={e => setter(s => ({ ...s, date_ouverture: e.target.value }))}
style={{ width: '100%', boxSizing: 'border-box' }} />
))}
</div>
);
const step2 = (
<div style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
{/* Fiscalité */}
{row('Fiscalité', (
<select value={state.fiscalite || 'flat_tax'} disabled={isFrance}
@@ -582,16 +614,11 @@ function PlatForm({ state, setter, logoFile, setLogoFile, logoPreview, setLogoPr
<option value="2TY">2TY — Revenus de capitaux (autre)</option>
</select>
))}
</div>
);
{/* Catégorie de plateforme */}
{row('Catégorie de plateforme', (
<select value={state.categorie_plateforme || 'psfp'} onChange={e => setter(s => ({ ...s, categorie_plateforme: e.target.value }))}
style={{ width: '100%' }}>
<option value="psfp">{CATEGORIE_PLATEFORME_LABELS.psfp}</option>
<option value="psan">{CATEGORIE_PLATEFORME_LABELS.psan}</option>
</select>
))}
const step3 = (
<div style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
{/* Méthode de remboursement */}
{row('Versement des remboursements', (
<select value={state.methode_remboursement || 'portefeuille'} onChange={e => setter(s => ({ ...s, methode_remboursement: e.target.value }))}
@@ -624,22 +651,19 @@ function PlatForm({ state, setter, logoFile, setLogoFile, logoPreview, setLogoPr
))}
</div>
{/* Date d'ouverture */}
{row("Date d'ouverture du compte", (
<input type="date" value={state.date_ouverture || ''} onChange={e => setter(s => ({ ...s, date_ouverture: e.target.value }))}
style={{ width: '100%', boxSizing: 'border-box' }} />
))}
{/* Détenteur */}
{investisseurs.length > 1 && row('Détenteur', (
<InvSelect
value={state.investisseur_id}
onChange={id => setter(s => ({ ...s, investisseur_id: id }))}
investisseurs={investisseurs}
placeholder="— Sélectionner —"
/>
{/* Frais liés à la plateforme */}
{row('Frais liés à la plateforme', (
<select value={state.frais_plateforme || 'aucun'} onChange={e => setter(s => ({ ...s, frais_plateforme: e.target.value }))}
style={{ width: '100%' }}>
<option value="aucun">{FRAIS_PLATEFORME_LABELS.aucun}</option>
<option value="entree_gestion_directe">{FRAIS_PLATEFORME_LABELS.entree_gestion_directe}</option>
</select>
))}
</div>
);
const step4 = (
<div style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
{/* Catégories d'investissement (édition seulement) */}
{isEdit && categoriesInv.length > 0 && row('Catégories', (
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 6 }}>
@@ -686,6 +710,16 @@ function PlatForm({ state, setter, logoFile, setLogoFile, logoPreview, setLogoPr
</div>
))}
{!isEdit && (
<div className="text-muted" style={{ fontSize: 13 }}>
Les catégories et secteurs d'investissement pourront être précisés après la création de la plateforme.
</div>
)}
</div>
);
const step5 = (
<div style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
{/* Logo */}
{row('Logo', (
<div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
@@ -699,8 +733,10 @@ function PlatForm({ state, setter, logoFile, setLogoFile, logoPreview, setLogoPr
)}
</div>
))}
</>
</div>
);
return { step1, step2, step3, step4, step5 };
}
export default function PlateformesSection() {
@@ -860,6 +896,7 @@ export default function PlateformesSection() {
taux_fiscalite_locale: ref.taux_fiscalite_locale ?? null,
type_produit_fiscal: ref.type_produit_fiscal || '2TT',
categorie_plateforme: ref.categorie_plateforme || 'psfp',
frais_plateforme: ref.frais_plateforme || 'aucun',
methode_remboursement: ref.methode_remboursement || 'portefeuille',
type_pret_defaut: ref.type_pret_defaut || null,
freq_interets_defaut: ref.freq_interets_defaut || null,
@@ -965,6 +1002,7 @@ export default function PlateformesSection() {
taux_fiscalite_locale: p.taux_fiscalite_locale ?? '',
type_produit_fiscal: p.type_produit_fiscal || '2TT',
categorie_plateforme: p.categorie_plateforme || 'psfp',
frais_plateforme: p.frais_plateforme || 'aucun',
methode_remboursement: p.methode_remboursement || 'portefeuille',
investisseur_id: p.investisseur_id ?? null,
date_ouverture: p.date_ouverture || '',
@@ -988,6 +1026,7 @@ export default function PlateformesSection() {
? Number(editPlat.taux_fiscalite_locale) : null,
type_produit_fiscal: editPlat.type_produit_fiscal || '2TT',
categorie_plateforme: editPlat.categorie_plateforme || 'psfp',
frais_plateforme: editPlat.frais_plateforme || 'aucun',
methode_remboursement: editPlat.methode_remboursement || 'portefeuille',
investisseur_id: editPlat.investisseur_id ? Number(editPlat.investisseur_id) : null,
date_ouverture: editPlat.date_ouverture || null,
@@ -1209,61 +1248,51 @@ export default function PlateformesSection() {
/>
{/* ── Modal : Ajouter une plateforme ── */}
<Modal
open={showNewPlat}
title="Nouvelle plateforme"
onClose={() => { setShowNewPlat(false); setNewPlat(EMPTY_PLAT); setNewPlatLogoFile(null); setNewPlatLogoPreview(null); setErr(null); }}
width={600}
footer={
<div style={{ display: 'flex', justifyContent: 'flex-end', width: '100%', gap: 8 }}>
<button type="button" onClick={() => { setShowNewPlat(false); setNewPlat(EMPTY_PLAT); setNewPlatLogoFile(null); setNewPlatLogoPreview(null); setErr(null); }}>Annuler</button>
<button className="primary" form="form-new-plat" type="submit">Créer</button>
</div>
}
>
<form id="form-new-plat" onSubmit={addPlat} style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
{err && <div className="error">{err}</div>}
<PlatForm
state={newPlat} setter={setNewPlat}
logoFile={newPlatLogoFile} setLogoFile={setNewPlatLogoFile}
logoPreview={newPlatLogoPreview} setLogoPreview={setNewPlatLogoPreview}
investisseurs={investisseurs} referentiel={referentiel}
categoriesInv={[]} secteursInv={[]}
{showNewPlat && (() => {
const s = platFormSteps({
state: newPlat, setter: setNewPlat,
logoFile: newPlatLogoFile, setLogoFile: setNewPlatLogoFile,
logoPreview: newPlatLogoPreview, setLogoPreview: setNewPlatLogoPreview,
investisseurs, referentiel,
categoriesInv: [], secteursInv: [],
});
return (
<PlateformeFormModal
title="Nouvelle plateforme"
onClose={() => { setShowNewPlat(false); setNewPlat(EMPTY_PLAT); setNewPlatLogoFile(null); setNewPlatLogoPreview(null); setErr(null); }}
onSubmit={addPlat}
err={err}
submitLabel="Créer"
width={600}
step1={s.step1} step2={s.step2} step3={s.step3} step4={s.step4} step5={s.step5}
/>
</form>
</Modal>
);
})()}
{/* ── Modal : Modifier une plateforme ── */}
<Modal
open={!!editPlat}
title="Modifier la plateforme"
onClose={() => { setEditPlat(null); setEditPlatLogoFile(null); setEditPlatLogoPreview(null); setErr(null); }}
width={600}
footer={
<div style={{ display: 'flex', justifyContent: 'space-between', width: '100%' }}>
<button className="danger" type="button" onClick={() => { const p = editPlat; setEditPlat(null); delPlat(p.id); }}>Supprimer</button>
<div style={{ display: 'flex', gap: 8 }}>
<button type="button" onClick={() => { setEditPlat(null); setEditPlatLogoFile(null); setEditPlatLogoPreview(null); setErr(null); }}>Annuler</button>
<button className="primary" form="form-edit-plat" type="submit">Enregistrer</button>
</div>
</div>
}
>
{editPlat && (
<form id="form-edit-plat" onSubmit={saveEditPlat} style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
{err && <div className="error">{err}</div>}
<PlatForm
state={editPlat} setter={setEditPlat}
logoFile={editPlatLogoFile} setLogoFile={setEditPlatLogoFile}
logoPreview={editPlatLogoPreview} setLogoPreview={setEditPlatLogoPreview}
investisseurs={investisseurs} referentiel={referentiel}
categoriesInv={categoriesInv} secteursInv={secteursInv}
isEdit
onDelLogo={() => delLogoPlat(editPlat.id)}
/>
</form>
)}
</Modal>
{editPlat && (() => {
const s = platFormSteps({
state: editPlat, setter: setEditPlat,
logoFile: editPlatLogoFile, setLogoFile: setEditPlatLogoFile,
logoPreview: editPlatLogoPreview, setLogoPreview: setEditPlatLogoPreview,
investisseurs, referentiel,
categoriesInv, secteursInv,
isEdit: true,
onDelLogo: () => delLogoPlat(editPlat.id),
});
return (
<PlateformeFormModal
title="Modifier la plateforme"
onClose={() => { setEditPlat(null); setEditPlatLogoFile(null); setEditPlatLogoPreview(null); setErr(null); }}
onSubmit={saveEditPlat}
err={err}
submitLabel="Enregistrer"
width={600}
onDelete={() => { const p = editPlat; setEditPlat(null); delPlat(p.id); }}
step1={s.step1} step2={s.step2} step3={s.step3} step4={s.step4} step5={s.step5}
/>
);
})()}
<ConfirmModal
open={!!confirmDelete}