Correction invitation
This commit is contained in:
1 parent
a58a8460f7
commit
14cf4e5a19
2 files changed
+21
-10
No files matched your search
@@ -84,7 +84,14 @@ router.post('/', async (req, res, next) => {
|
|||||||
|
|
||||||
// Envoyer l'email
|
// Envoyer l'email
|
||||||
const cfg = getSmtpConfig();
|
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/<token>". 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 inviteUrl = `${appUrl}/invitation/${token}`;
|
||||||
const roleLabel = role === 'admin' ? 'Administrateur' : 'Utilisateur';
|
const roleLabel = role === 'admin' ? 'Administrateur' : 'Utilisateur';
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import ConfirmModal from '../components/ConfirmModal.jsx';
|
|||||||
import EmptyState from '../components/EmptyState.jsx';
|
import EmptyState from '../components/EmptyState.jsx';
|
||||||
import PageIcon from '../components/PageIcon.jsx';
|
import PageIcon from '../components/PageIcon.jsx';
|
||||||
import { useAsyncAction } from '../hooks/useAsyncAction.js';
|
import { useAsyncAction } from '../hooks/useAsyncAction.js';
|
||||||
import { fmtEUR, fmtDate } from '../utils/format.js';
|
import { fmtEUR, fmtDate, memberLabel } from '../utils/format.js';
|
||||||
import {
|
import {
|
||||||
sortMouvements, computeSoldeIndicatifMap, signedMontant, MOUV_TYPE_LABELS,
|
sortMouvements, computeSoldeIndicatifMap, signedMontant, MOUV_TYPE_LABELS,
|
||||||
ExportDropdown, mouvToCSV, mouvToXLS, mouvToJSON, timestampSuffix, dlBlob,
|
ExportDropdown, mouvToCSV, mouvToXLS, mouvToJSON, timestampSuffix, dlBlob,
|
||||||
@@ -432,19 +432,20 @@ export default function PlateformesPe() {
|
|||||||
<table>
|
<table>
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Deal</th><th>Souscrit le</th><th className="num">Montant investi</th>
|
<th>Deal</th>{multiDetenteur && <th>Détenteur</th>}<th>Souscrit le</th><th className="num">Montant investi</th>
|
||||||
<th className="num">Dernière valorisation</th><th className="num">TVPI</th>
|
<th className="num">Dernière valorisation</th><th className="num">TVPI</th>
|
||||||
<th>Statut</th><th>Détention</th>
|
<th>Statut</th><th>Détention</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{platDeals.length === 0 && (
|
{platDeals.length === 0 && (
|
||||||
<tr><td colSpan={7} className="text-muted" style={{ textAlign: 'center' }}>Aucun deal sur cette plateforme</td></tr>
|
<tr><td colSpan={multiDetenteur ? 8 : 7} className="text-muted" style={{ textAlign: 'center' }}>Aucun deal sur cette plateforme</td></tr>
|
||||||
)}
|
)}
|
||||||
{platDeals.map(d => (
|
{platDeals.map(d => (
|
||||||
<tr key={d.id} className="dr-row" style={{ cursor: 'pointer' }}
|
<tr key={d.id} className="dr-row" style={{ cursor: 'pointer' }}
|
||||||
onClick={() => navigate(`/investissements/${d.id}`)}>
|
onClick={() => navigate(`/investissements/${d.id}`)}>
|
||||||
<td>{d.nom_deal}</td>
|
<td>{d.nom_deal}</td>
|
||||||
|
{multiDetenteur && <td className="text-muted" style={{ fontSize: 'var(--fs-sm)' }}>{d.investisseur_nom || '—'}</td>}
|
||||||
<td>{fmtDate(d.date_souscription)}</td>
|
<td>{fmtDate(d.date_souscription)}</td>
|
||||||
<td className="num">{fmtEUR(d.montant_investi)}</td>
|
<td className="num">{fmtEUR(d.montant_investi)}</td>
|
||||||
<td className="num">{d.derniere_valorisation != null ? fmtEUR(d.derniere_valorisation) : '—'}</td>
|
<td className="num">{d.derniere_valorisation != null ? fmtEUR(d.derniere_valorisation) : '—'}</td>
|
||||||
@@ -468,16 +469,17 @@ export default function PlateformesPe() {
|
|||||||
</p>
|
</p>
|
||||||
<table>
|
<table>
|
||||||
<thead>
|
<thead>
|
||||||
<tr><th>Date</th><th>Deal</th><th>Nature</th><th className="num">Montant net perçu</th></tr>
|
<tr><th>Date</th><th>Deal</th>{multiDetenteur && <th>Détenteur</th>}<th>Nature</th><th className="num">Montant net perçu</th></tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{platRembs.length === 0 && (
|
{platRembs.length === 0 && (
|
||||||
<tr><td colSpan={4} className="text-muted" style={{ textAlign: 'center' }}>Aucune distribution enregistrée</td></tr>
|
<tr><td colSpan={multiDetenteur ? 5 : 4} className="text-muted" style={{ textAlign: 'center' }}>Aucune distribution enregistrée</td></tr>
|
||||||
)}
|
)}
|
||||||
{[...platRembs].sort((a, b) => b.date_remb.localeCompare(a.date_remb) || b.id - a.id).map(r => (
|
{[...platRembs].sort((a, b) => b.date_remb.localeCompare(a.date_remb) || b.id - a.id).map(r => (
|
||||||
<tr key={r.id}>
|
<tr key={r.id}>
|
||||||
<td>{fmtDate(r.date_remb)}</td>
|
<td>{fmtDate(r.date_remb)}</td>
|
||||||
<td>{deals.find(d => d.id === r.investissement_pe_id)?.nom_deal || '—'}</td>
|
<td>{deals.find(d => d.id === r.investissement_pe_id)?.nom_deal || '—'}</td>
|
||||||
|
{multiDetenteur && <td className="text-muted" style={{ fontSize: 'var(--fs-sm)' }}>{r.investisseur_nom || '—'}</td>}
|
||||||
<td className="text-muted">{r.distribution_nature === 'interet_non_deploye' ? 'Intérêt sur capital non déployé' : 'Restitution de capital'}</td>
|
<td className="text-muted">{r.distribution_nature === 'interet_non_deploye' ? 'Intérêt sur capital non déployé' : 'Restitution de capital'}</td>
|
||||||
<td className="num">{fmtEUR(r.net_recu)}</td>
|
<td className="num">{fmtEUR(r.net_recu)}</td>
|
||||||
</tr>
|
</tr>
|
||||||
@@ -496,16 +498,17 @@ export default function PlateformesPe() {
|
|||||||
</div>
|
</div>
|
||||||
<table>
|
<table>
|
||||||
<thead>
|
<thead>
|
||||||
<tr><th>Date</th><th>Type</th><th className="num">Montant</th><th>Libellé</th><th style={{ width: 40 }}></th></tr>
|
<tr><th>Date</th><th>Type</th>{multiDetenteur && <th>Détenteur</th>}<th className="num">Montant</th><th>Libellé</th><th style={{ width: 40 }}></th></tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{platDepots.length === 0 && (
|
{platDepots.length === 0 && (
|
||||||
<tr><td colSpan={5} className="text-muted" style={{ textAlign: 'center' }}>Aucun mouvement</td></tr>
|
<tr><td colSpan={multiDetenteur ? 6 : 5} className="text-muted" style={{ textAlign: 'center' }}>Aucun mouvement</td></tr>
|
||||||
)}
|
)}
|
||||||
{[...platDepots].sort((a, b) => b.date_operation.localeCompare(a.date_operation) || b.id - a.id).map(r => (
|
{[...platDepots].sort((a, b) => b.date_operation.localeCompare(a.date_operation) || b.id - a.id).map(r => (
|
||||||
<tr key={r.id} className="dr-row" style={{ cursor: 'pointer' }} onClick={() => openDrEdit(r)}>
|
<tr key={r.id} className="dr-row" style={{ cursor: 'pointer' }} onClick={() => openDrEdit(r)}>
|
||||||
<td>{fmtDate(r.date_operation)}</td>
|
<td>{fmtDate(r.date_operation)}</td>
|
||||||
<td>{r.type === 'depot' ? 'Dépôt' : 'Retrait'}</td>
|
<td>{r.type === 'depot' ? 'Dépôt' : 'Retrait'}</td>
|
||||||
|
{multiDetenteur && <td className="text-muted" style={{ fontSize: 'var(--fs-sm)' }}>{(() => { const inv = investisseurs.find(i => i.id === r.investisseur_id); return inv ? memberLabel(inv) : '—'; })()}</td>}
|
||||||
<td className="num">{fmtEUR(r.montant)}</td>
|
<td className="num">{fmtEUR(r.montant)}</td>
|
||||||
<td className="text-muted">{r.libelle || '—'}</td>
|
<td className="text-muted">{r.libelle || '—'}</td>
|
||||||
<td onClick={e => e.stopPropagation()}>
|
<td onClick={e => e.stopPropagation()}>
|
||||||
@@ -536,11 +539,11 @@ export default function PlateformesPe() {
|
|||||||
</p>
|
</p>
|
||||||
<table>
|
<table>
|
||||||
<thead>
|
<thead>
|
||||||
<tr><th>Date</th><th>Type</th><th>Libellé</th><th className="num">Montant</th><th className="num">Solde indicatif</th></tr>
|
<tr><th>Date</th><th>Type</th>{multiDetenteur && <th>Détenteur</th>}<th>Libellé</th><th className="num">Montant</th><th className="num">Solde indicatif</th></tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{walletSorted.length === 0 && (
|
{walletSorted.length === 0 && (
|
||||||
<tr><td colSpan={5} className="text-muted" style={{ textAlign: 'center' }}>Aucun mouvement</td></tr>
|
<tr><td colSpan={multiDetenteur ? 6 : 5} className="text-muted" style={{ textAlign: 'center' }}>Aucun mouvement</td></tr>
|
||||||
)}
|
)}
|
||||||
{walletSorted.map(r => {
|
{walletSorted.map(r => {
|
||||||
// signedMontant() (MouvementsShared.jsx) reste la seule source de vérité pour ce
|
// signedMontant() (MouvementsShared.jsx) reste la seule source de vérité pour ce
|
||||||
@@ -551,6 +554,7 @@ export default function PlateformesPe() {
|
|||||||
<tr key={r.id}>
|
<tr key={r.id}>
|
||||||
<td>{fmtDate(r.date_operation)}</td>
|
<td>{fmtDate(r.date_operation)}</td>
|
||||||
<td>{MOUV_TYPE_LABELS[r.type] || r.type}</td>
|
<td>{MOUV_TYPE_LABELS[r.type] || r.type}</td>
|
||||||
|
{multiDetenteur && <td className="text-muted" style={{ fontSize: 'var(--fs-sm)' }}>{r.investisseur_nom || '—'}</td>}
|
||||||
<td className="text-muted">{r.libelle || '—'}</td>
|
<td className="text-muted">{r.libelle || '—'}</td>
|
||||||
<td className="num" style={{ color: signed < 0 ? 'var(--danger, #dc2626)' : signed > 0 ? 'var(--success, #16a34a)' : undefined }}>
|
<td className="num" style={{ color: signed < 0 ? 'var(--danger, #dc2626)' : signed > 0 ? 'var(--success, #16a34a)' : undefined }}>
|
||||||
{signed > 0 ? '+' : ''}{fmtEUR(signed)}
|
{signed > 0 ? '+' : ''}{fmtEUR(signed)}
|
||||||
|
|||||||
Reference in new issue
Block a user