274 lines
7.6 KiB
Plaintext
274 lines
7.6 KiB
Plaintext
---
|
|
// BankCard.astro
|
|
const { frontmatter, allHumans } = Astro.props;
|
|
const { name, monthly_allowance, type = "FLUX" } = frontmatter;
|
|
|
|
// --- LOGIQUE DE COULEUR SUBTILE ---
|
|
const bankConfigs = {
|
|
UpDéjeuner: { color: "#f97316", icon: "🍴" },
|
|
"La Banque Postale": { color: "#137bb1", icon: "🏦" },
|
|
Sumeria: { color: "#116853", icon: "⚡" },
|
|
};
|
|
|
|
const config = bankConfigs[name as keyof typeof bankConfigs] || {
|
|
color: "#85a9bc",
|
|
icon: "◈",
|
|
};
|
|
const color = config.color;
|
|
|
|
// --- LOGIQUE DE RÉCUPÉRATION TEMPORELLE DYNAMIQUE ---
|
|
|
|
// 1. On cherche toutes les entrées qui contiennent réellement des flux
|
|
const entriesWithFlux = allHumans.filter((h: any) =>
|
|
h.data.manifestations?.some((m: any) => m.cercle === "FLX"),
|
|
);
|
|
|
|
// 2. On trouve la date de la toute dernière transaction enregistrée (pour ne pas être bloqué par un changement de mois vide)
|
|
const lastFluxEntry = entriesWithFlux.sort(
|
|
(a: any, b: any) =>
|
|
new Date(b.data.date).getTime() - new Date(a.data.date).getTime(),
|
|
)[0];
|
|
|
|
// 3. On définit la période de référence (Mois/Année)
|
|
const refDate = lastFluxEntry ? new Date(lastFluxEntry.data.date) : new Date();
|
|
const refMonth = refDate.getMonth();
|
|
const refYear = refDate.getFullYear();
|
|
|
|
// 4. Somme des flux pour cette période et pour cette banque spécifique
|
|
const totalFlux = allHumans.reduce((acc: number, entry: any) => {
|
|
const d = new Date(entry.data.date);
|
|
const isSamePeriod =
|
|
d.getMonth() === refMonth && d.getFullYear() === refYear;
|
|
|
|
if (!isSamePeriod) return acc;
|
|
|
|
const flxManifests =
|
|
entry.data.manifestations?.filter((m: any) => {
|
|
const sourceName = m.source?.trim().toLowerCase();
|
|
const cardName = name?.trim().toLowerCase();
|
|
return m.cercle === "FLX" && sourceName === cardName;
|
|
}) || [];
|
|
|
|
return (
|
|
acc +
|
|
flxManifests.reduce(
|
|
(sum: number, m: any) => sum + (Number(m.amount) || 0),
|
|
0,
|
|
)
|
|
);
|
|
}, 0);
|
|
|
|
// Log de contrôle dans ton terminal Lenovo
|
|
console.log(
|
|
`[${name}] Bilan basé sur : ${refMonth + 1}/${refYear} | Total : ${totalFlux}`,
|
|
);
|
|
|
|
const percentage = Math.max(
|
|
0,
|
|
Math.min(100, Math.round((totalFlux / monthly_allowance) * 100)),
|
|
);
|
|
|
|
// État du Hebel
|
|
let status = { label: "ABONDANCE", color: "#10b981" };
|
|
if (percentage < 20) status = { label: "ÉPUISEMENT", color: "#ef4444" };
|
|
else if (percentage < 50) status = { label: "FLUX TENDU", color: "#f59e0b" };
|
|
else if (percentage < 85) status = { label: "ÉQUILIBRE", color: "#3b82f6" };
|
|
---
|
|
|
|
<article
|
|
class="tcg-card bank-card"
|
|
style={`--accent: ${color}; --state-color: ${status.color}`}
|
|
>
|
|
<div class="card-inner">
|
|
<header class="card-header">
|
|
<span class="card-name">{name}</span>
|
|
<span class="card-cost">{config.icon}</span>
|
|
</header>
|
|
|
|
<div class="visual-container">
|
|
<div class="source-gauge">
|
|
<svg viewBox="0 0 36 36" class="circular-chart">
|
|
<path
|
|
class="circle-bg"
|
|
d="M18 2.0845 a 15.9155 15.9155 0 0 1 0 31.831 a 15.9155 15.9155 0 0 1 0 -31.831"
|
|
></path>
|
|
<path
|
|
class="circle"
|
|
stroke-dasharray={`${percentage}, 100`}
|
|
d="M18 2.0845 a 15.9155 15.9155 0 0 1 0 31.831 a 15.9155 15.9155 0 0 1 0 -31.831"
|
|
></path>
|
|
<text x="18" y="20.35" class="percentage"
|
|
>{percentage}%</text
|
|
>
|
|
</svg>
|
|
</div>
|
|
<div class="status-badge">{status.label}</div>
|
|
</div>
|
|
|
|
<div class="card-type-line">
|
|
Ressource — {type}
|
|
</div>
|
|
|
|
<div class="card-body">
|
|
<div class="flavor-text">
|
|
{
|
|
percentage > 50
|
|
? "Le souffle circule avec aisance."
|
|
: "Le flux se raréfie, prudence."
|
|
}
|
|
</div>
|
|
|
|
<div class="mechanics">
|
|
<div class="stat-row">
|
|
<span>Intégrité</span>
|
|
<div class="power-bar">
|
|
{
|
|
Array.from({ length: 5 }).map((_, i) => (
|
|
<div
|
|
class={`segment ${i < percentage / 20 ? "active" : ""}`}
|
|
/>
|
|
))
|
|
}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<footer class="card-footer">
|
|
<span class="rarity-symbol">✦✦✧</span>
|
|
<div class="reserve-level">RÉSERVE {percentage}/100</div>
|
|
</footer>
|
|
</div>
|
|
</article>
|
|
|
|
<style>
|
|
.bank-card {
|
|
background: linear-gradient(135deg, #fff 0%, #f8fafc 100%);
|
|
color: #1a202c;
|
|
border-width: 1px;
|
|
}
|
|
|
|
.card-inner {
|
|
height: 100%;
|
|
padding: 10px;
|
|
border-radius: 10px;
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
|
|
.card-header {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
border-bottom: 1px solid #e2e8f0;
|
|
padding-bottom: 5px;
|
|
margin-bottom: 10px;
|
|
}
|
|
|
|
.card-name {
|
|
font-weight: bold;
|
|
font-family: "Philosopher", serif;
|
|
font-size: 1.1rem;
|
|
color: var(--accent);
|
|
}
|
|
|
|
.visual-container {
|
|
flex: 1;
|
|
background: #f1f5f9;
|
|
border-radius: 8px;
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
justify-content: center;
|
|
position: relative;
|
|
overflow: hidden;
|
|
gap: 10px;
|
|
border: 1px solid rgba(0, 0, 0, 0.05);
|
|
padding: 10px 0;
|
|
}
|
|
|
|
.circular-chart {
|
|
max-width: 80px;
|
|
width: 100%;
|
|
}
|
|
.circle-bg {
|
|
fill: none;
|
|
stroke: rgba(0, 0, 0, 0.05);
|
|
stroke-width: 3.8;
|
|
}
|
|
.circle {
|
|
fill: none;
|
|
stroke: var(--accent);
|
|
stroke-width: 2.8;
|
|
stroke-linecap: round;
|
|
transition: stroke-dasharray 1s ease;
|
|
}
|
|
.percentage {
|
|
fill: #1a202c;
|
|
font-family: "Inter", sans-serif;
|
|
font-size: 0.5rem;
|
|
text-anchor: middle;
|
|
font-weight: bold;
|
|
}
|
|
|
|
.status-badge {
|
|
background: var(--state-color);
|
|
color: white;
|
|
font-size: 0.5rem;
|
|
padding: 2px 8px;
|
|
border-radius: 4px;
|
|
text-transform: uppercase;
|
|
font-weight: bold;
|
|
}
|
|
|
|
.card-type-line {
|
|
font-size: 0.7rem;
|
|
font-weight: bold;
|
|
border-bottom: 1px solid #e2e8f0;
|
|
margin: 8px 0;
|
|
padding-bottom: 2px;
|
|
text-transform: uppercase;
|
|
color: #64748b;
|
|
}
|
|
|
|
.flavor-text {
|
|
font-size: 0.65rem;
|
|
font-style: italic;
|
|
color: #94a3b8;
|
|
margin-bottom: 10px;
|
|
min-height: 1.5rem;
|
|
line-height: 1.2;
|
|
}
|
|
|
|
.stat-row {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
font-size: 0.75rem;
|
|
color: #475569;
|
|
}
|
|
.power-bar {
|
|
display: flex;
|
|
gap: 3px;
|
|
}
|
|
.segment {
|
|
width: 14px;
|
|
height: 6px;
|
|
background: #e2e8f0;
|
|
border-radius: 1px;
|
|
}
|
|
.segment.active {
|
|
background: var(--accent);
|
|
}
|
|
|
|
.card-footer {
|
|
margin-top: auto;
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
font-size: 0.6rem;
|
|
color: #94a3b8;
|
|
padding-top: 10px;
|
|
}
|
|
</style>
|