Gestion du Porte-monnaie Page plateforme
This commit is contained in:
1 parent
c2d5712e2c
commit
9ef32cef90
3 files changed
+1272
-417
No files matched your search
@@ -0,0 +1,421 @@
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import * as XLSX from 'xlsx';
|
||||
import { fmtEUR, fmtDate, today } from '../utils/format.js';
|
||||
|
||||
/* ── Éléments partagés entre l'onglet "Mouvements" de DepotsRetraits.jsx et l'onglet
|
||||
"Mouvements Porte-monnaie" de Plateformes.jsx. Toute évolution ici s'applique aux deux
|
||||
pages — c'est le but : éviter la dérive entre les deux implémentations. ── */
|
||||
|
||||
/* ── Helpers export ──────────────────────────────────────────── */
|
||||
/** Horodatage local AAAAMMJJ_HHmmss (jamais toISOString(), cf. décalage UTC) */
|
||||
export function timestampSuffix() {
|
||||
const d = new Date();
|
||||
const pad = n => String(n).padStart(2, '0');
|
||||
return `${d.getFullYear()}${pad(d.getMonth() + 1)}${pad(d.getDate())}_${pad(d.getHours())}${pad(d.getMinutes())}${pad(d.getSeconds())}`;
|
||||
}
|
||||
export function dlBlob(content, filename, type) {
|
||||
const blob = new Blob([content], { type });
|
||||
const url = URL.createObjectURL(blob);
|
||||
const a = document.createElement('a');
|
||||
a.href = url; a.download = filename; a.click();
|
||||
URL.revokeObjectURL(url);
|
||||
}
|
||||
|
||||
export const DEBIT_TYPES = new Set(['retrait', 'souscription', 'prelevement_ir', 'prelevement_csg', 'prelevement_local']);
|
||||
|
||||
// Montant signé, identique à ce qui est affiché à l'écran (+ crédit / − débit),
|
||||
// pour permettre de sommer directement l'extraction et contrôler le solde.
|
||||
// depot/remboursement/bonus → crédit (+) ; retrait/souscription/prélèvements → débit (−) ;
|
||||
// correction → déjà signé en base (montant peut être positif ou négatif).
|
||||
export function signedMontant(r) {
|
||||
if (DEBIT_TYPES.has(r.type)) return -Math.abs(r.montant);
|
||||
if (r.type === 'correction') return r.montant;
|
||||
return Math.abs(r.montant);
|
||||
}
|
||||
|
||||
export const MOUV_TYPE_LABELS = {
|
||||
depot: 'Dépôt', retrait: 'Retrait', correction: 'Correction de solde',
|
||||
remboursement: 'Remboursement', bonus: 'Bonus / Cashback', souscription: 'Souscription',
|
||||
prelevement_ir: 'Prélèvement IR', prelevement_csg: 'Prélèvement CSG',
|
||||
prelevement_local: "Prélèvement impôts à l'étranger",
|
||||
};
|
||||
|
||||
export function mouvToCSV(rows) {
|
||||
const BOM = ''; const sep = ';';
|
||||
const q = v => `"${String(v ?? '').replace(/"/g, '""')}"`;
|
||||
const headers = ['Date','Plateforme','Type','Projet','Montant (€)','Libellé','Référence','Source'];
|
||||
const data = rows.map(r => [
|
||||
r.date_operation, r.plateforme_nom||'', MOUV_TYPE_LABELS[r.type] || r.type, r._projet||'',
|
||||
String(signedMontant(r)).replace('.',','), r.libelle||'', r.reference||'', r.source||'',
|
||||
]);
|
||||
return BOM + [headers,...data].map(r => r.map(q).join(sep)).join('\r\n');
|
||||
}
|
||||
|
||||
export function mouvToXLS(rows, multiDetenteur) {
|
||||
const data = rows.map(r => ({
|
||||
'Date': r.date_operation,
|
||||
'Plateforme': r.plateforme_nom || '',
|
||||
...(multiDetenteur ? { 'Détenteur': r.plateforme_detenteur_nom || '' } : {}),
|
||||
'Type': MOUV_TYPE_LABELS[r.type] || r.type,
|
||||
'Projet': r._projet || '',
|
||||
'Montant (€)': signedMontant(r),
|
||||
}));
|
||||
const ws = XLSX.utils.json_to_sheet(data);
|
||||
const wb = XLSX.utils.book_new();
|
||||
XLSX.utils.book_append_sheet(wb, ws, 'Mouvements');
|
||||
return XLSX.write(wb, { type: 'array', bookType: 'xlsx' });
|
||||
}
|
||||
|
||||
export function mouvToJSON(rows) {
|
||||
const data = rows.map(r => ({
|
||||
date_operation: r.date_operation,
|
||||
plateforme_nom: r.plateforme_nom || '',
|
||||
type: r.type,
|
||||
projet: r._projet || null,
|
||||
montant: signedMontant(r),
|
||||
libelle: r.libelle || null,
|
||||
reference: r.reference || null,
|
||||
source: r.source || null,
|
||||
}));
|
||||
return JSON.stringify(data, null, 2);
|
||||
}
|
||||
|
||||
/* ── Constantes ──────────────────────────────────────────────── */
|
||||
export const MOIS_FR = ['Janvier','Février','Mars','Avril','Mai','Juin','Juillet','Août','Septembre','Octobre','Novembre','Décembre'];
|
||||
|
||||
export const emptyMouvementForm = { investisseur_id: '', plateforme_id: '', date_operation: today(), type: 'depot', montant: '', libelle: '', reference: '', notes: '' };
|
||||
|
||||
/* ── Menu déroulant d'export CSV/XLS/JSON ────────────────────── */
|
||||
export function ExportDropdown({ disabled, onCSV, onXLS, onJSON }) {
|
||||
const [open, setOpen] = useState(false);
|
||||
const ref = useRef(null);
|
||||
useEffect(() => {
|
||||
if (!open) return;
|
||||
const h = e => { if (!ref.current?.contains(e.target)) setOpen(false); };
|
||||
document.addEventListener('mousedown', h);
|
||||
return () => document.removeEventListener('mousedown', h);
|
||||
}, [open]);
|
||||
const choose = fn => { setOpen(false); fn(); };
|
||||
return (
|
||||
<div ref={ref} style={{ position: 'relative' }}>
|
||||
<button type="button" className="icon-btn" disabled={disabled}
|
||||
onClick={() => setOpen(o => !o)} title="Exporter">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
||||
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/>
|
||||
</svg>
|
||||
</button>
|
||||
{open && (
|
||||
<div className="export-dropdown" role="menu">
|
||||
<button role="menuitem" onClick={() => choose(onCSV)}>
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
||||
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/>
|
||||
<line x1="8" y1="13" x2="16" y2="13"/><line x1="8" y1="17" x2="16" y2="17"/>
|
||||
</svg>
|
||||
<span><strong>Format CSV</strong><small>Compatible Excel, LibreOffice</small></span>
|
||||
</button>
|
||||
<button role="menuitem" onClick={() => choose(onXLS)}>
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
||||
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/><path d="M9 13l2 2 4-4"/>
|
||||
</svg>
|
||||
<span><strong>Format Excel</strong><small>Fichier .xlsx Microsoft Excel</small></span>
|
||||
</button>
|
||||
<button role="menuitem" onClick={() => choose(onJSON)}>
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
||||
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/>
|
||||
<path d="M8 13h1.5a1 1 0 0 1 1 1v1a1 1 0 0 0 1 1 1 1 0 0 0-1 1v1a1 1 0 0 1-1 1H8"/>
|
||||
<path d="M16 13h-1.5a1 1 0 0 0-1 1v1a1 1 0 0 1-1 1 1 1 0 0 1 1 1v1a1 1 0 0 0 1 1H16"/>
|
||||
</svg>
|
||||
<span><strong>Format JSON</strong><small>Réimportable, structuré</small></span>
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/* ── Panneau de détail d'un mouvement ────────────────────────── */
|
||||
export function DetailPanel({ row, onEdit, onDeleteCorrection, onViewRemb, onViewInvestissement }) {
|
||||
if (!row) return (
|
||||
<div className="dr-detail dr-detail-empty">
|
||||
<svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" style={{ opacity: 0.25, marginBottom: 8 }}>
|
||||
<circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/>
|
||||
</svg>
|
||||
<span>Sélectionnez un mouvement</span>
|
||||
</div>
|
||||
);
|
||||
|
||||
const isCorrection = row.type === 'correction';
|
||||
const isRemb = row.type === 'remboursement' || row.type === 'bonus';
|
||||
const isBonus = row.type === 'bonus';
|
||||
const isSouscription = row.type === 'souscription';
|
||||
const isPrelevement = row.type === 'prelevement_ir' || row.type === 'prelevement_csg' || row.type === 'prelevement_local';
|
||||
const isAuto = row.source === 'auto_remboursement';
|
||||
|
||||
const typeLabel = isCorrection ? 'Correction de solde'
|
||||
: isBonus ? 'Bonus / Cashback'
|
||||
: row.type === 'remboursement' ? 'Remboursement (porte-monnaie, brut)'
|
||||
: row.type === 'prelevement_ir' ? 'Prélèvement impôt sur le revenu'
|
||||
: row.type === 'prelevement_csg' ? 'Prélèvement CSG'
|
||||
: row.type === 'prelevement_local' ? "Prélèvement impôts à l'étranger"
|
||||
: isSouscription ? (row._isReinvest ? 'Réinvestissement' : 'Souscription à un investissement')
|
||||
: row.type === 'depot' ? 'Dépôt' : 'Retrait';
|
||||
|
||||
const editLabel = isCorrection ? 'Modifier la correction de solde'
|
||||
: row.type === 'depot' ? 'Modifier le dépôt' : 'Modifier le retrait';
|
||||
|
||||
// Débit (−) pour retrait/souscription/prélèvements, crédit (+) pour dépôt/remboursement/bonus ;
|
||||
// la correction est déjà signée en base.
|
||||
const isDebitType = row.type === 'retrait' || isSouscription || isPrelevement;
|
||||
const isPos = isCorrection ? row.montant >= 0 : !isDebitType;
|
||||
const sign = isPos ? '+' : '−';
|
||||
const montantFmt = `${sign} ${fmtEUR(Math.abs(row.montant))}`;
|
||||
const montantColor = undefined;
|
||||
|
||||
const fields = [
|
||||
{ label: 'Plateforme', value: row.plateforme_nom },
|
||||
{ label: 'Type de mouvement', value: typeLabel },
|
||||
{ label: 'Montant', value: montantFmt, color: montantColor },
|
||||
{ label: 'Date', value: fmtDate(row.date_operation) },
|
||||
isRemb && !isBonus && row.libelle && { label: 'Projet', value: row.libelle },
|
||||
isRemb && !isBonus && { label: 'Capital', value: fmtEUR(row._capital) },
|
||||
isRemb && !isBonus && row._cashback > 0 && { label: 'Cashback', value: fmtEUR(row._cashback) },
|
||||
isRemb && !isBonus && { label: 'Intérêts bruts', value: fmtEUR(row._interets_bruts) },
|
||||
isRemb && !isBonus && { label: 'Traitement fiscal', value: row._isFlatTax
|
||||
? 'Flat tax France (PFU prélevé à la source — voir prélèvements associés)'
|
||||
: row._taxeLocale > 0
|
||||
? "Étranger avec retenue fiscale locale — voir prélèvement associé"
|
||||
: 'Étranger — indicatif, aucun prélèvement à la source ici' },
|
||||
isPrelevement && row.libelle && { label: 'Projet', value: row.libelle },
|
||||
isSouscription && row.libelle && { label: 'Projet', value: row.libelle },
|
||||
!isCorrection && !isRemb && !isSouscription && !isPrelevement && row.libelle && { label: 'Libellé', value: row.libelle },
|
||||
!isCorrection && !isRemb && row.reference && { label: 'Référence', value: row.reference },
|
||||
row.notes && { label: 'Notes', value: row.notes },
|
||||
].filter(Boolean);
|
||||
|
||||
return (
|
||||
<div className="dr-detail">
|
||||
<div className="dr-detail-title">Détails du mouvement</div>
|
||||
|
||||
{isAuto && (
|
||||
<div style={{
|
||||
display: 'flex', alignItems: 'flex-start', gap: 8,
|
||||
margin: '5px 5px 12px',
|
||||
padding: '9px 12px', borderRadius: 6,
|
||||
background: 'color-mix(in srgb, var(--primary) 10%, transparent)',
|
||||
border: '1px solid color-mix(in srgb, var(--primary) 25%, transparent)',
|
||||
fontSize: 'var(--fs-sm)', color: 'var(--text-muted)',
|
||||
lineHeight: 1.4,
|
||||
}}>
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor"
|
||||
strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"
|
||||
style={{ marginTop: 1, flexShrink: 0, color: 'var(--primary)' }}>
|
||||
<path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/>
|
||||
</svg>
|
||||
<span>
|
||||
Retrait automatique — généré lors d'un remboursement sur compte courant.
|
||||
Modifiable via la page Remboursements ou depuis la fiche détail de l'investissement.
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{isRemb && (
|
||||
<div style={{
|
||||
display: 'flex', alignItems: 'flex-start', gap: 8,
|
||||
margin: '5px 5px 12px',
|
||||
padding: '9px 12px', borderRadius: 6,
|
||||
background: 'color-mix(in srgb, var(--success) 10%, transparent)',
|
||||
border: '1px solid color-mix(in srgb, var(--success) 25%, transparent)',
|
||||
fontSize: 'var(--fs-sm)', color: 'var(--text-muted)',
|
||||
lineHeight: 1.4,
|
||||
}}>
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor"
|
||||
strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"
|
||||
style={{ marginTop: 1, flexShrink: 0, color: 'var(--success)' }}>
|
||||
<path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/>
|
||||
</svg>
|
||||
<span>
|
||||
{isBonus
|
||||
? 'Bonus crédité directement au porte-monnaie de la plateforme.'
|
||||
: row._isAutoSweep
|
||||
? "Remboursement (montant brut) crédité sur le porte-monnaie puis reversé aussitôt sur le compte courant (retrait automatique associé). Les prélèvements éventuels apparaissent en lignes séparées. Modifiable depuis la page Remboursements."
|
||||
: "Montant brut du remboursement versé sur le porte-monnaie de la plateforme (méthode « portefeuille ») — les prélèvements éventuels apparaissent en lignes séparées. Modifiable depuis la page Remboursements."}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{isPrelevement && (
|
||||
<div style={{
|
||||
display: 'flex', alignItems: 'flex-start', gap: 8,
|
||||
margin: '5px 5px 12px',
|
||||
padding: '9px 12px', borderRadius: 6,
|
||||
background: `color-mix(in srgb, ${row.type === 'prelevement_local' ? '#f97316' : 'var(--danger)'} 10%, transparent)`,
|
||||
border: `1px solid color-mix(in srgb, ${row.type === 'prelevement_local' ? '#f97316' : 'var(--danger)'} 25%, transparent)`,
|
||||
fontSize: 'var(--fs-sm)', color: 'var(--text-muted)',
|
||||
lineHeight: 1.4,
|
||||
}}>
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor"
|
||||
strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"
|
||||
style={{ marginTop: 1, flexShrink: 0, color: row.type === 'prelevement_local' ? '#f97316' : 'var(--danger)' }}>
|
||||
<path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/>
|
||||
</svg>
|
||||
<span>
|
||||
{row.type === 'prelevement_local'
|
||||
? "Retenue fiscale prélevée localement à l'étranger, avant que le remboursement n'atteigne le porte-monnaie de la plateforme."
|
||||
: "Prélèvement forfaitaire unique (PFU France) retenu à la source sur ce remboursement. Modifiable depuis la page Remboursements."}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{isSouscription && (
|
||||
<div style={{
|
||||
display: 'flex', alignItems: 'flex-start', gap: 8,
|
||||
margin: '5px 5px 12px',
|
||||
padding: '9px 12px', borderRadius: 6,
|
||||
background: 'color-mix(in srgb, var(--danger) 10%, transparent)',
|
||||
border: '1px solid color-mix(in srgb, var(--danger) 25%, transparent)',
|
||||
fontSize: 'var(--fs-sm)', color: 'var(--text-muted)',
|
||||
lineHeight: 1.4,
|
||||
}}>
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor"
|
||||
strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"
|
||||
style={{ marginTop: 1, flexShrink: 0, color: 'var(--danger)' }}>
|
||||
<path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/>
|
||||
</svg>
|
||||
<span>
|
||||
{row._isReinvest
|
||||
? "Réinvestissement automatique du capital d'intérêts dans un nouvel investissement — capital sorti du porte-monnaie de la plateforme."
|
||||
: "Souscription à un investissement — capital sorti du porte-monnaie de la plateforme."}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="dr-detail-fields">
|
||||
{fields.map(f => (
|
||||
<div className="dr-detail-field" key={f.label}>
|
||||
<span className="dr-detail-label">{f.label}</span>
|
||||
<span className="dr-detail-value" style={f.color ? { color: f.color, fontWeight: 600 } : undefined}>{f.value}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="dr-detail-footer">
|
||||
{isAuto ? null : (isRemb || isPrelevement) ? (
|
||||
<button className="dr-detail-edit-btn" onClick={() => onViewRemb(row)}>
|
||||
Voir / modifier le remboursement
|
||||
</button>
|
||||
) : isSouscription ? (
|
||||
<button className="dr-detail-edit-btn" onClick={() => onViewInvestissement(row)}>
|
||||
Voir l'investissement
|
||||
</button>
|
||||
) : isCorrection ? (
|
||||
<button className="dr-detail-edit-btn" onClick={() => onDeleteCorrection(row._correctionId)}>
|
||||
Supprimer la correction
|
||||
</button>
|
||||
) : (
|
||||
<button className="dr-detail-edit-btn" onClick={() => onEdit(row)}>
|
||||
{editLabel}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/** Modal de correction de solde porte-monnaie (micro-écarts flat_tax) */
|
||||
export function CorrectionModal({ open, onClose, onConfirm,
|
||||
plateforme_nom, type_operation, computedBalance, date }) {
|
||||
const isRetrait = type_operation === 'retrait';
|
||||
// Défaut : 0 pour un retrait (vider le PM), solde calculé pour un dépôt
|
||||
const [declared, setDeclared] = useState('');
|
||||
|
||||
useEffect(() => {
|
||||
if (open) setDeclared(isRetrait ? '0' : String(Math.round(computedBalance * 100) / 100));
|
||||
}, [open, isRetrait, computedBalance]);
|
||||
|
||||
const diff = Math.round((Number(declared) - computedBalance) * 100) / 100;
|
||||
const hasDiff = Math.abs(diff) >= 0.005;
|
||||
|
||||
const handleConfirm = () => {
|
||||
onConfirm(hasDiff ? diff : 0);
|
||||
};
|
||||
|
||||
if (!open) return null;
|
||||
return (
|
||||
<div style={{
|
||||
position: 'fixed', inset: 0, zIndex: 1000,
|
||||
display: 'flex', alignItems: 'center', justifyContent: 'center',
|
||||
background: 'rgba(0,0,0,0.4)',
|
||||
}}>
|
||||
<div style={{
|
||||
background: 'var(--surface)', borderRadius: 10,
|
||||
padding: '24px 28px', width: 420, maxWidth: '90vw',
|
||||
boxShadow: '0 8px 32px rgba(0,0,0,0.18)',
|
||||
border: '1px solid var(--border)',
|
||||
}}>
|
||||
<div style={{ marginBottom: 6, fontWeight: 700, fontSize: 'var(--fs-base)' }}>
|
||||
Vérification du solde porte-monnaie
|
||||
</div>
|
||||
<div style={{
|
||||
fontSize: 'var(--fs-sm)', color: 'var(--text-muted)',
|
||||
marginBottom: 18, lineHeight: 1.5,
|
||||
}}>
|
||||
Après ce {isRetrait ? 'retrait' : 'dépôt'} sur <strong>{plateforme_nom}</strong>,
|
||||
quel est le solde indiqué sur la plateforme ?
|
||||
{' '}Si différent du solde calculé, une correction sera créée automatiquement.
|
||||
</div>
|
||||
|
||||
<div style={{ marginBottom: 12 }}>
|
||||
<div style={{
|
||||
display: 'flex', justifyContent: 'space-between', alignItems: 'center',
|
||||
padding: '8px 12px', borderRadius: 6, marginBottom: 6,
|
||||
background: 'var(--surface-2)', fontSize: 'var(--fs-sm)',
|
||||
}}>
|
||||
<span style={{ color: 'var(--text-muted)' }}>Solde calculé</span>
|
||||
<span style={{ fontWeight: 600 }}>{Math.round(computedBalance * 100) / 100} €</span>
|
||||
</div>
|
||||
<label style={{ fontSize: 'var(--fs-sm)', fontWeight: 500, display: 'block', marginBottom: 4 }}>
|
||||
Solde constaté sur la plateforme (€)
|
||||
</label>
|
||||
<input
|
||||
type="number" step="0.01"
|
||||
value={declared}
|
||||
onChange={e => setDeclared(e.target.value)}
|
||||
style={{ width: '100%', fontSize: 'var(--fs-base)' }}
|
||||
autoFocus
|
||||
/>
|
||||
</div>
|
||||
|
||||
{hasDiff && (
|
||||
<div style={{
|
||||
padding: '8px 12px', borderRadius: 6, marginBottom: 12,
|
||||
background: diff > 0
|
||||
? 'color-mix(in srgb, var(--success) 12%, transparent)'
|
||||
: 'color-mix(in srgb, var(--danger) 12%, transparent)',
|
||||
border: `1px solid ${diff > 0 ? 'color-mix(in srgb, var(--success) 30%, transparent)' : 'color-mix(in srgb, var(--danger) 30%, transparent)'}`,
|
||||
fontSize: 'var(--fs-sm)', color: 'var(--text-muted)',
|
||||
}}>
|
||||
Une correction de <strong style={{ color: diff > 0 ? 'var(--success)' : 'var(--danger)' }}>
|
||||
{diff > 0 ? '+' : ''}{diff} €
|
||||
</strong> sera créée.
|
||||
</div>
|
||||
)}
|
||||
{!hasDiff && declared !== '' && (
|
||||
<div style={{
|
||||
padding: '8px 12px', borderRadius: 6, marginBottom: 12,
|
||||
background: 'color-mix(in srgb, var(--primary) 10%, transparent)',
|
||||
fontSize: 'var(--fs-sm)', color: 'var(--text-muted)',
|
||||
}}>
|
||||
Solde identique au calcul — aucune correction nécessaire.
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div style={{ display: 'flex', gap: 8, justifyContent: 'flex-end', marginTop: 8 }}>
|
||||
<button onClick={onClose}>Passer</button>
|
||||
<button className="primary" onClick={handleConfirm}>
|
||||
{hasDiff ? 'Créer la correction' : 'OK'}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user