Files
crowdlending-app/frontend/src/pages/Aide.jsx
T

319 lines
22 KiB
React
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { useState } from 'react';
import { useLocation, useNavigate } from 'react-router-dom';
/* ── Accordéon FAQ ───────────────────────────────────────────── */
function FaqItem({ question, children }) {
const [open, setOpen] = useState(false);
return (
<div style={{
borderBottom: '1px solid var(--border)',
padding: '0',
}}>
<button
onClick={() => setOpen(o => !o)}
style={{
width: '100%', textAlign: 'left', background: 'none', border: 'none',
padding: '14px 0', cursor: 'pointer', display: 'flex',
alignItems: 'center', justifyContent: 'space-between', gap: 12,
color: 'var(--text)', fontSize: 'var(--fs-base)', fontWeight: 500,
}}
>
<span>{question}</span>
<svg
width="16" height="16" viewBox="0 0 24 24" fill="none"
stroke="currentColor" strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round"
style={{ flexShrink: 0, transform: open ? 'rotate(180deg)' : 'none', transition: 'transform .2s' }}
>
<polyline points="6 9 12 15 18 9" />
</svg>
</button>
{open && (
<div style={{
paddingBottom: 16, color: 'var(--text-muted)',
fontSize: 'var(--fs-sm)', lineHeight: 1.7,
}}>
{children}
</div>
)}
</div>
);
}
/* ── Navigation ─────────────────────────────────────────────── */
const NAV = [
{
id: 'faq',
label: 'FAQ',
icon: (
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor"
strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<circle cx="12" cy="12" r="10" />
<path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3" />
<line x1="12" y1="17" x2="12.01" y2="17" />
</svg>
),
},
];
/* ── Page principale ─────────────────────────────────────────── */
export default function Aide() {
const { search } = useLocation();
const navigate = useNavigate();
const section = new URLSearchParams(search).get('section') || 'faq';
const setSection = (s) => navigate(`/aide?section=${s}`, { replace: true });
return (
<div className="account-layout">
{/* ── Nav gauche ───────────────────────────────────────── */}
<aside className="account-sidebar">
<h1 className="account-title">Centre d'aide</h1>
{NAV.map(item => (
<button
key={item.id}
className={`account-nav-item${section === item.id ? ' active' : ''}`}
onClick={() => setSection(item.id)}
>
{item.icon}
{item.label}
</button>
))}
</aside>
{/* ── Contenu ──────────────────────────────────────────── */}
<div className="account-content">
{section === 'faq' && (
<div>
<h2 style={{ marginTop: 0, marginBottom: 24 }}>Questions fréquentes</h2>
<FaqItem question="Comment est calculé le solde du porte-monnaie d'une plateforme ?">
<p style={{ marginTop: 0 }}>
Le solde du porte-monnaie représente les liquidités disponibles sur une plateforme,
c'est-à-dire l'argent que vous pouvez retirer ou réinvestir. Il est calculé comme suit :
</p>
<div style={{ margin: '12px 0', padding: '12px 16px', background: 'var(--surface-2)', borderRadius: 8, fontFamily: 'monospace', fontSize: 'var(--fs-sm)', color: 'var(--text)', lineHeight: 2 }}>
Solde = Dépôts<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Retraits manuels<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;+ Remboursements crédités au porte-monnaie<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;+ Bonus (parrainage / plateforme)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Capital investi (en cours ou remboursé)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;+ Corrections de solde
</div>
<p><strong style={{ color: 'var(--text)' }}>Retraits manuels</strong> — seuls les retraits que vous avez saisis manuellement sont déduits.
Les retraits générés automatiquement lors d'un remboursement en mode "compte courant" sont exclus,
car ils ne représentent pas un vrai mouvement de porte-monnaie.</p>
<p><strong style={{ color: 'var(--text)' }}>Remboursements crédités au porte-monnaie</strong> — uniquement les remboursements
dont le mode est "Portefeuille" (et non "Compte courant"). Le montant crédité dépend de la fiscalité
de la plateforme :</p>
<ul style={{ margin: '8px 0 8px 16px', paddingLeft: 0 }}>
<li style={{ marginBottom: 6 }}>
<strong style={{ color: 'var(--text)' }}>Plateforme française (Flat Tax)</strong> — le porte-monnaie
reçoit le <em>net reçu</em>, c'est-à-dire le montant après déduction du PFU français (17,2 % de prélèvements
sociaux + 12,8 % d'impôt sur le revenu), prélevé directement à la source par la plateforme.
</li>
<li style={{ marginBottom: 6 }}>
<strong style={{ color: 'var(--text)' }}>Plateforme hors France (sans fiscalité locale)</strong> — le porte-monnaie
reçoit le capital remboursé + cashback + intérêts bruts. Le PFU français n'est pas prélevé à la
source : vous devez le déclarer séparément dans votre déclaration fiscale annuelle.
</li>
<li>
<strong style={{ color: 'var(--text)' }}>Plateforme hors France (avec retenue à la source locale)</strong> — même
principe que ci-dessus, mais la plateforme a déjà prélevé une taxe locale sur les intérêts. Le
porte-monnaie reçoit le capital + cashback + intérêts bruts <em>après</em> cette retenue locale.
Le PFU français reste à déclarer séparément.
</li>
</ul>
<p><strong style={{ color: 'var(--text)' }}>Capital investi</strong> — le montant que vous avez placé dans des prêts actifs
(y compris les réinvestissements complémentaires) est soustrait du porte-monnaie, car ces fonds ne sont
plus disponibles. Ils reviennent progressivement via les remboursements.</p>
<p style={{ marginBottom: 0 }}><strong style={{ color: 'var(--text)' }}>Corrections de solde</strong> — ajustements manuels
permettant de réconcilier de micro-écarts de calcul (par exemple un arrondi de centimes sur la fiscalité).</p>
</FaqItem>
<FaqItem question="Comment mettre en place un réinvestissement automatique des intérêts ?">
<p style={{ marginTop: 0 }}>
Le réinvestissement automatique permet de capitaliser les intérêts perçus après chaque remboursement,
sans aucune saisie manuelle. Les intérêts sont automatiquement réinjectés dans le capital du prêt,
ce qui augmente progressivement le montant investi et les intérêts futurs.
</p>
<h4 style={{ margin: '16px 0 8px', color: 'var(--text)' }}>Activation</h4>
<ol style={{ margin: '0 0 12px 16px', paddingLeft: 0, lineHeight: 1.8 }}>
<li>Ouvrez la fiche d'un investissement.</li>
<li>Cliquez sur le bouton <strong style={{ color: 'var(--text)' }}>⋮</strong> en haut à droite du bloc <em>Informations du projet</em>, puis choisissez <strong style={{ color: 'var(--text)' }}>Réinvestir</strong>.</li>
<li>Dans la modale, sélectionnez l'onglet <strong style={{ color: 'var(--text)' }}>Automatique</strong>.</li>
<li>Cliquez sur <strong style={{ color: 'var(--text)' }}>Activer</strong>.</li>
</ol>
<p>
Une fois activé, le bloc <em>Réinvestissements complémentaires</em> apparaît sur la fiche avec
un badge <strong style={{ color: 'var(--primary)' }}>auto</strong>, même si aucun remboursement
n'a encore eu lieu.
</p>
<h4 style={{ margin: '16px 0 8px', color: 'var(--text)' }}>Quel montant est réinvesti ?</h4>
<p>Le montant réinvesti après chaque remboursement dépend de la fiscalité de la plateforme :</p>
<ul style={{ margin: '8px 0 12px 16px', paddingLeft: 0, lineHeight: 1.8 }}>
<li>
<strong style={{ color: 'var(--text)' }}>Plateforme française (Flat Tax)</strong> — les <em>intérêts nets</em> sont réinvestis
(après déduction du PFU prélevé à la source). C'est le montant réellement reçu sur votre porte-monnaie.
</li>
<li>
<strong style={{ color: 'var(--text)' }}>Plateforme hors France</strong> — les <em>intérêts bruts</em> sont réinvestis,
car aucune retenue n'est effectuée à la source. Pensez à provisionner la fiscalité due lors de votre déclaration annuelle.
</li>
</ul>
<p>Si les intérêts d'un remboursement sont nuls (remboursement de capital seul), aucun réinvestissement n'est créé.</p>
<h4 style={{ margin: '16px 0 8px', color: 'var(--text)' }}>Désactivation</h4>
<p style={{ marginBottom: 0 }}>
Pour désactiver le réinvestissement automatique, cliquez sur <strong style={{ color: 'var(--text)' }}>⋮</strong> dans le bloc
<em> Informations du projet</em> et choisissez <strong style={{ color: 'var(--text)' }}>Désactiver le réinvestissement auto</strong>.
Les réinvestissements déjà créés sont conservés ; les prochains remboursements n'en génèreront plus.
</p>
</FaqItem>
<FaqItem question="Comment configurer le serveur MCP en production (sans passer par le développement local) ?">
<p style={{ marginTop: 0 }}>
Le serveur MCP tourne déjà en continu en production (service Docker <code style={{ background: 'var(--surface-2)', padding: '1px 5px', borderRadius: 4 }}>crowdlending-mcp</code>,
exposé via Traefik) — contrairement au développement local, vous n'avez rien à démarrer ni
à laisser tourner sur votre machine. Il suffit de connecter Claude Desktop à l'URL publique.
</p>
<h4 style={{ margin: '16px 0 8px', color: 'var(--text)' }}>Prérequis</h4>
<ul style={{ margin: '0 0 12px 16px', paddingLeft: 0, lineHeight: 1.8 }}>
<li>Le service <code style={{ background: 'var(--surface-2)', padding: '1px 5px', borderRadius: 4 }}>crowdlending-mcp</code> du
<code style={{ background: 'var(--surface-2)', padding: '1px 5px', borderRadius: 4 }}> docker-compose.yml</code> doit être déployé, avec un enregistrement
DNS pour <code style={{ background: 'var(--surface-2)', padding: '1px 5px', borderRadius: 4 }}>mcp.&lt;votre domaine&gt;</code> pointant vers la même IP que l'app
(certificat TLS automatique via Traefik).</li>
<li>Une clé API <strong style={{ color: 'var(--text)' }}>dédiée à la production</strong>, distincte de celle utilisée en développement
local si vous en avez une — cela permet de révoquer l'une sans affecter l'autre.</li>
</ul>
<h4 style={{ margin: '16px 0 8px', color: 'var(--text)' }}>Étapes</h4>
<ol style={{ margin: '0 0 12px 16px', paddingLeft: 0, lineHeight: 1.8 }}>
<li>Générez une clé API dédiée : <strong style={{ color: 'var(--text)' }}>Mon compte → Clés API → Nouvelle clé</strong> (par exemple nommée « MCP Prod »).</li>
<li>Allez dans <strong style={{ color: 'var(--text)' }}>Mon compte → Serveur MCP</strong> et renseignez l'URL publique
(<code style={{ background: 'var(--surface-2)', padding: '1px 5px', borderRadius: 4 }}>https://mcp.&lt;votre domaine&gt;/mcp</code>) ainsi que la clé générée. L'environnement
se détecte automatiquement sur « PROD » dès que l'URL ne contient ni <code style={{ background: 'var(--surface-2)', padding: '1px 5px', borderRadius: 4 }}>localhost</code> ni
<code style={{ background: 'var(--surface-2)', padding: '1px 5px', borderRadius: 4 }}> dev</code> — cochez « Forcer manuellement » si votre domaine de test prête à confusion.</li>
<li>Copiez la configuration générée dans <code style={{ background: 'var(--surface-2)', padding: '1px 5px', borderRadius: 4 }}>claude_desktop_config.json</code> (Réglages
→ Développeur → Serveurs MCP locaux → Modifier la config), exactement comme en développement — seule l'URL change,
le mécanisme <code style={{ background: 'var(--surface-2)', padding: '1px 5px', borderRadius: 4 }}>mcp-remote</code> (et le wrapper <code style={{ background: 'var(--surface-2)', padding: '1px 5px', borderRadius: 4 }}>cmd /c</code> sous
Windows) reste identique.</li>
<li>Redémarrez complètement Claude Desktop.</li>
</ol>
<p>
Vous pouvez connecter dev et prod <strong style={{ color: 'var(--text)' }}>simultanément</strong> : répétez ces étapes une seconde fois
avec l'URL locale et une clé distincte, les deux entrées de config (<code style={{ background: 'var(--surface-2)', padding: '1px 5px', borderRadius: 4 }}>crowdlending-dev</code> /
<code style={{ background: 'var(--surface-2)', padding: '1px 5px', borderRadius: 4 }}> crowdlending-prod</code>) coexistent sans collision.
</p>
<h4 style={{ margin: '16px 0 8px', color: 'var(--text)' }}>Sécurité</h4>
<ul style={{ margin: '0 0 12px 16px', paddingLeft: 0, lineHeight: 1.8 }}>
<li>Le point d'entrée public n'a <strong style={{ color: 'var(--text)' }}>volontairement aucune restriction d'IP</strong> (accessible
depuis n'importe où, pour un usage nomade) : la clé API est donc la seule barrière d'accès. Traitez-la comme un
mot de passe, et révoquez-la immédiatement en cas de doute (Mon compte → Clés API).</li>
<li>Limite de 60 requêtes/minute par IP (au-delà, erreur 429) et fermeture automatique des sessions inactives
depuis plus de 30 minutes — aucune donnée de session n'est conservée entre deux connexions.</li>
<li>L'outil <code style={{ background: 'var(--surface-2)', padding: '1px 5px', borderRadius: 4 }}>crowdlending_fetch_url</code> (lecture d'une page web arbitraire) reste
<strong style={{ color: 'var(--text)' }}> désactivé en production</strong>, même s'il est activé chez vous en développement local.</li>
</ul>
<p style={{ marginBottom: 0 }}>
Si la connexion reste bloquée sans erreur visible, la cause est presque toujours la même qu'en développement local
(<code style={{ background: 'var(--surface-2)', padding: '1px 5px', borderRadius: 4 }}>npx</code> qui échoue silencieusement à joindre le registre npm) — voir la
section Dépannage de <strong style={{ color: 'var(--text)' }}>Mon compte → Serveur MCP</strong>.
</p>
</FaqItem>
<FaqItem question="Comment utiliser le serveur MCP au quotidien ? (fonctions disponibles et exemples)">
<p style={{ marginTop: 0 }}>
Une fois connecté, Claude (Desktop ou tout autre client MCP) peut consulter votre portefeuille en langage
naturel il choisit lui-même le bon outil selon votre question. Le serveur est <strong style={{ color: 'var(--text)' }}>strictement
en lecture seule</strong> : aucune donnée n'est jamais créée, modifiée ou supprimée depuis une conversation. Toute
saisie (nouvel investissement, remboursement…) reste manuelle dans l'application.
</p>
<h4 style={{ margin: '16px 0 8px', color: 'var(--text)' }}>Fonctions disponibles</h4>
<table style={{ width: '100%', borderCollapse: 'collapse', fontSize: 'var(--fs-sm)', margin: '0 0 14px' }}>
<tbody>
<tr style={{ borderBottom: '1px solid var(--border)' }}>
<td style={{ padding: '8px 10px 8px 0', verticalAlign: 'top', whiteSpace: 'nowrap' }}>
<code style={{ background: 'var(--surface-2)', padding: '1px 5px', borderRadius: 4 }}>crowdlending_get_investisseur</code>
</td>
<td style={{ padding: '8px 0', verticalAlign: 'top' }}>Profil de l'investisseur lié à la clé API (nom, type famille/entreprise, régime fiscal). Avec une clé « Famille et entreprises », renvoie la liste des profils du foyer.</td>
</tr>
<tr style={{ borderBottom: '1px solid var(--border)' }}>
<td style={{ padding: '8px 10px 8px 0', verticalAlign: 'top', whiteSpace: 'nowrap' }}>
<code style={{ background: 'var(--surface-2)', padding: '1px 5px', borderRadius: 4 }}>crowdlending_get_dashboard</code>
</td>
<td style={{ padding: '8px 0', verticalAlign: 'top' }}>KPIs du portefeuille : capital investi, capital en risque, montant remboursé, intérêts bruts/nets, dépôts/retraits — filtrable par année.</td>
</tr>
<tr style={{ borderBottom: '1px solid var(--border)' }}>
<td style={{ padding: '8px 10px 8px 0', verticalAlign: 'top', whiteSpace: 'nowrap' }}>
<code style={{ background: 'var(--surface-2)', padding: '1px 5px', borderRadius: 4 }}>crowdlending_list_investissements</code>
</td>
<td style={{ padding: '8px 0', verticalAlign: 'top' }}>Liste des investissements (projet, émetteur, plateforme, montant, taux, durée, statut), filtrable par statut.</td>
</tr>
<tr style={{ borderBottom: '1px solid var(--border)' }}>
<td style={{ padding: '8px 10px 8px 0', verticalAlign: 'top', whiteSpace: 'nowrap' }}>
<code style={{ background: 'var(--surface-2)', padding: '1px 5px', borderRadius: 4 }}>crowdlending_get_investissement</code>
</td>
<td style={{ padding: '8px 0', verticalAlign: 'top' }}>Détail complet d'un investissement (par id), y compris la liste de ses remboursements réels perçus.</td>
</tr>
<tr style={{ borderBottom: '1px solid var(--border)' }}>
<td style={{ padding: '8px 10px 8px 0', verticalAlign: 'top', whiteSpace: 'nowrap' }}>
<code style={{ background: 'var(--surface-2)', padding: '1px 5px', borderRadius: 4 }}>crowdlending_list_remboursements</code>
</td>
<td style={{ padding: '8px 0', verticalAlign: 'top' }}>Historique des remboursements perçus (toutes plateformes), filtrable par période.</td>
</tr>
<tr style={{ borderBottom: '1px solid var(--border)' }}>
<td style={{ padding: '8px 10px 8px 0', verticalAlign: 'top', whiteSpace: 'nowrap' }}>
<code style={{ background: 'var(--surface-2)', padding: '1px 5px', borderRadius: 4 }}>crowdlending_list_depots_retraits</code>
</td>
<td style={{ padding: '8px 0', verticalAlign: 'top' }}>Historique des mouvements de cash (dépôts et retraits), du plus récent au plus ancien.</td>
</tr>
<tr>
<td style={{ padding: '8px 10px 8px 0', verticalAlign: 'top', whiteSpace: 'nowrap' }}>
<code style={{ background: 'var(--surface-2)', padding: '1px 5px', borderRadius: 4 }}>crowdlending_fetch_url</code>
</td>
<td style={{ padding: '8px 0', verticalAlign: 'top' }}>
<em>Développement local uniquement</em>, désactivé par défaut. Lit une page web (ex. annonce de projet sur
une plateforme) et en extrait le texte propre c'est à vous d'en reprendre les informations utiles pour
créer l'investissement manuellement, l'outil ne saisit rien lui-même.
</td>
</tr>
</tbody>
</table>
<h4 style={{ margin: '16px 0 8px', color: 'var(--text)' }}>Exemples de questions</h4>
<ul style={{ margin: '0 0 12px 16px', paddingLeft: 0, lineHeight: 2 }}>
<li>« Quel est mon encours de crowdlending actuellement ? »</li>
<li>« Quel a été mon rendement (intérêts nets) en 2026 ? »</li>
<li>« Liste-moi les investissements en retard ou en procédure. »</li>
<li>« Donne-moi le détail de l'investissement 42, avec ses remboursements. »</li>
<li>« Quels remboursements ai-je reçus entre le 1er et le 30 juin 2026 ? »</li>
<li>« Quels ont été mes derniers dépôts et retraits ? »</li>
<li>« Regarde cette annonce de projet et propose-moi les infos pour créer l'investissement : [URL] »
(développement local, avec <code style={{ background: 'var(--surface-2)', padding: '1px 5px', borderRadius: 4 }}>crowdlending_fetch_url</code> activé)</li>
</ul>
<p style={{ marginBottom: 0 }}>
Ces exemples fonctionnent aussi bien en dev qu'en prod dès lors que le serveur correspondant est connecté
(voir les FAQ de configuration ci-dessus) les six premiers outils sont identiques dans les deux environnements.
</p>
</FaqItem>
</div>
)}
</div>
</div>
);
}