319 lines
22 KiB
React
319 lines
22 KiB
React
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 />
|
||
− Retraits manuels<br />
|
||
+ Remboursements crédités au porte-monnaie<br />
|
||
+ Bonus (parrainage / plateforme)<br />
|
||
− Capital investi (en cours ou remboursé)<br />
|
||
+ 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.<votre domaine></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.<votre domaine>/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>
|
||
);
|
||
}
|