diff --git a/backend/src/routes/invitations.js b/backend/src/routes/invitations.js index 3514da5..2e345c3 100644 --- a/backend/src/routes/invitations.js +++ b/backend/src/routes/invitations.js @@ -84,7 +84,14 @@ router.post('/', async (req, res, next) => { // Envoyer l'email const cfg = getSmtpConfig(); - const appUrl = cfg.appUrl || ''; + // Retire le / final avant de concaténer (19/09/26 — bug corrigé : app_url est + // habituellement saisi avec un slash final par les admins, ex. "https://mon-domaine/", + // et sans ce trim le lien devenait "https://mon-domaine//invitation/". Ce double + // slash ne matche pas la route React Router "/invitation/:token", qui tombe alors sur le + // catch-all protégé de App.jsx et redirige silencieusement l'utilisateur invité vers + // /login au lieu de la page d'inscription. Même correctif déjà appliqué partout ailleurs + // (auth.js : vérification email, reset de mot de passe). + const appUrl = cfg.appUrl?.replace(/\/$/, '') || ''; const inviteUrl = `${appUrl}/invitation/${token}`; const roleLabel = role === 'admin' ? 'Administrateur' : 'Utilisateur'; diff --git a/frontend/src/pages/PlateformesPe.jsx b/frontend/src/pages/PlateformesPe.jsx index 202ee1b..1028444 100644 --- a/frontend/src/pages/PlateformesPe.jsx +++ b/frontend/src/pages/PlateformesPe.jsx @@ -8,7 +8,7 @@ import ConfirmModal from '../components/ConfirmModal.jsx'; import EmptyState from '../components/EmptyState.jsx'; import PageIcon from '../components/PageIcon.jsx'; import { useAsyncAction } from '../hooks/useAsyncAction.js'; -import { fmtEUR, fmtDate } from '../utils/format.js'; +import { fmtEUR, fmtDate, memberLabel } from '../utils/format.js'; import { sortMouvements, computeSoldeIndicatifMap, signedMontant, MOUV_TYPE_LABELS, ExportDropdown, mouvToCSV, mouvToXLS, mouvToJSON, timestampSuffix, dlBlob, @@ -432,19 +432,20 @@ export default function PlateformesPe() { - + {multiDetenteur && } {platDeals.length === 0 && ( - + )} {platDeals.map(d => ( navigate(`/investissements/${d.id}`)}> + {multiDetenteur && } @@ -468,16 +469,17 @@ export default function PlateformesPe() {

DealSouscrit leMontant investiDealDétenteurSouscrit leMontant investi Dernière valorisationTVPI StatutDétention
Aucun deal sur cette plateforme
Aucun deal sur cette plateforme
{d.nom_deal}{d.investisseur_nom || '—'}{fmtDate(d.date_souscription)} {fmtEUR(d.montant_investi)} {d.derniere_valorisation != null ? fmtEUR(d.derniere_valorisation) : '—'}
- + {multiDetenteur && } {platRembs.length === 0 && ( - + )} {[...platRembs].sort((a, b) => b.date_remb.localeCompare(a.date_remb) || b.id - a.id).map(r => ( + {multiDetenteur && } @@ -496,16 +498,17 @@ export default function PlateformesPe() {
DateDealNatureMontant net perçu
DateDealDétenteurNatureMontant net perçu
Aucune distribution enregistrée
Aucune distribution enregistrée
{fmtDate(r.date_remb)} {deals.find(d => d.id === r.investissement_pe_id)?.nom_deal || '—'}{r.investisseur_nom || '—'}{r.distribution_nature === 'interet_non_deploye' ? 'Intérêt sur capital non déployé' : 'Restitution de capital'} {fmtEUR(r.net_recu)}
- + {multiDetenteur && } {platDepots.length === 0 && ( - + )} {[...platDepots].sort((a, b) => b.date_operation.localeCompare(a.date_operation) || b.id - a.id).map(r => ( openDrEdit(r)}> + {multiDetenteur && }
DateTypeMontantLibellé
DateTypeDétenteurMontantLibellé
Aucun mouvement
Aucun mouvement
{fmtDate(r.date_operation)} {r.type === 'depot' ? 'Dépôt' : 'Retrait'}{(() => { const inv = investisseurs.find(i => i.id === r.investisseur_id); return inv ? memberLabel(inv) : '—'; })()}{fmtEUR(r.montant)} {r.libelle || '—'} e.stopPropagation()}> @@ -536,11 +539,11 @@ export default function PlateformesPe() {

- + {multiDetenteur && } {walletSorted.length === 0 && ( - + )} {walletSorted.map(r => { // signedMontant() (MouvementsShared.jsx) reste la seule source de vérité pour ce @@ -551,6 +554,7 @@ export default function PlateformesPe() { + {multiDetenteur && }
DateTypeLibelléMontantSolde indicatif
DateTypeDétenteurLibelléMontantSolde indicatif
Aucun mouvement
Aucun mouvement
{fmtDate(r.date_operation)} {MOUV_TYPE_LABELS[r.type] || r.type}{r.investisseur_nom || '—'}{r.libelle || '—'} 0 ? 'var(--success, #16a34a)' : undefined }}> {signed > 0 ? '+' : ''}{fmtEUR(signed)}