correction config.ts et evolution
This commit is contained in:
8
package-lock.json
generated
8
package-lock.json
generated
@@ -8,7 +8,7 @@
|
|||||||
"name": "ambient",
|
"name": "ambient",
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"astro": "^5.17.1"
|
"astro": "^5.18.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@astrojs/compiler": {
|
"node_modules/@astrojs/compiler": {
|
||||||
@@ -1661,9 +1661,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/astro": {
|
"node_modules/astro": {
|
||||||
"version": "5.17.3",
|
"version": "5.18.0",
|
||||||
"resolved": "https://registry.npmjs.org/astro/-/astro-5.17.3.tgz",
|
"resolved": "https://registry.npmjs.org/astro/-/astro-5.18.0.tgz",
|
||||||
"integrity": "sha512-69dcfPe8LsHzklwj+hl+vunWUbpMB6pmg35mACjetxbJeUNNys90JaBM8ZiwsPK689SAj/4Zqb1ayaANls9/MA==",
|
"integrity": "sha512-CHiohwJIS4L0G6/IzE1Fx3dgWqXBCXus/od0eGUfxrZJD2um2pE7ehclMmgL/fXqbU7NfE1Ze2pq34h2QaA6iQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@astrojs/compiler": "^2.13.0",
|
"@astrojs/compiler": "^2.13.0",
|
||||||
|
|||||||
@@ -9,6 +9,6 @@
|
|||||||
"astro": "astro"
|
"astro": "astro"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"astro": "^5.17.1"
|
"astro": "^5.18.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
257
src/components/BankCard.astro
Normal file
257
src/components/BankCard.astro
Normal file
@@ -0,0 +1,257 @@
|
|||||||
|
---
|
||||||
|
// 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: "🍴" }, // Orange Up
|
||||||
|
"La Banque Postale": { color: "#137bb1", icon: "🏦" }, // Bleu BP
|
||||||
|
Sumeria: { color: "#116853", icon: "⚡" }, // Vert d'eau
|
||||||
|
};
|
||||||
|
|
||||||
|
const config = bankConfigs[name as keyof typeof bankConfigs] || {
|
||||||
|
color: "#85a9bc",
|
||||||
|
icon: "◈",
|
||||||
|
};
|
||||||
|
const color = config.color;
|
||||||
|
|
||||||
|
const now = new Date();
|
||||||
|
// Importe le type si nécessaire (Astro le génère pour toi)
|
||||||
|
// import type { CollectionEntry } from 'astro:content';
|
||||||
|
|
||||||
|
const currentMonthEntries = allHumans.filter((h: any) => {
|
||||||
|
// Remplace 'any' par 'CollectionEntry<"humans">' si tu veux être strict
|
||||||
|
const d = new Date(h.data.date);
|
||||||
|
return (
|
||||||
|
d.getMonth() === now.getMonth() && d.getFullYear() === now.getFullYear()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Somme des flux
|
||||||
|
// BankCard.astro (Partie Logique)
|
||||||
|
const totalFlux = currentMonthEntries.reduce((acc: number, entry: any) => {
|
||||||
|
const flxManifests =
|
||||||
|
entry.data.manifestations?.filter((m: any) => {
|
||||||
|
// ON FILTRE : cercle FLX ET la source doit correspondre au nom de la carte
|
||||||
|
return m.cercle === "FLX" && m.source === name;
|
||||||
|
}) || [];
|
||||||
|
|
||||||
|
return (
|
||||||
|
acc +
|
||||||
|
flxManifests.reduce(
|
||||||
|
(sum: number, m: any) => sum + (Number(m.amount) || 0),
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}, 0);
|
||||||
|
|
||||||
|
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-color: var(
|
||||||
|
--accent
|
||||||
|
) !important; /* Bordure subtile de la couleur de la banque */
|
||||||
|
}
|
||||||
|
|
||||||
|
.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; /* Souligné subtil */
|
||||||
|
padding-bottom: 5px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-name {
|
||||||
|
font-weight: bold;
|
||||||
|
font-family: "Philosopher", serif;
|
||||||
|
font-size: 1.1rem;
|
||||||
|
color: var(--accent); /* Nom coloré subtilement */
|
||||||
|
}
|
||||||
|
|
||||||
|
.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);
|
||||||
|
}
|
||||||
|
|
||||||
|
.circular-chart {
|
||||||
|
max-width: 100px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
.circle-bg {
|
||||||
|
fill: none;
|
||||||
|
stroke: rgba(0, 0, 0, 0.05);
|
||||||
|
stroke-width: 3.8;
|
||||||
|
}
|
||||||
|
.circle {
|
||||||
|
fill: none;
|
||||||
|
stroke: var(--accent); /* La jauge prend la couleur de la banque */
|
||||||
|
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
|
||||||
|
); /* Garde la couleur d'état (vert/orange/rouge) pour la sécurité */
|
||||||
|
color: white;
|
||||||
|
font-size: 0.5rem;
|
||||||
|
padding: 1px 6px;
|
||||||
|
border-radius: 4px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.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;
|
||||||
|
}
|
||||||
|
|
||||||
|
.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: 4px;
|
||||||
|
background: #e2e8f0;
|
||||||
|
border-radius: 1px;
|
||||||
|
}
|
||||||
|
.segment.active {
|
||||||
|
background: var(--accent); /* Segments de la couleur de la banque */
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-footer {
|
||||||
|
margin-top: auto;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
font-size: 0.6rem;
|
||||||
|
color: #94a3b8;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -21,22 +21,22 @@ const hygieneScore = health?.somatic?.hygiene_level ?? 5;
|
|||||||
|
|
||||||
// 2. RÉINTÉGRATION DES COMPTAGES
|
// 2. RÉINTÉGRATION DES COMPTAGES
|
||||||
const counts = {
|
const counts = {
|
||||||
PRO: manifestations?.filter((m) => m.cercle === "PRO").length || 0,
|
PRO: manifestations?.filter((m: any) => m.cercle === "PRO").length || 0,
|
||||||
SAN: manifestations?.filter((m) => m.cercle === "SAN").length || 0,
|
SAN: manifestations?.filter((m: any) => m.cercle === "SAN").length || 0,
|
||||||
SOC: manifestations?.filter((m) => m.cercle === "SOC").length || 0,
|
SOC: manifestations?.filter((m: any) => m.cercle === "SOC").length || 0,
|
||||||
FLX: manifestations?.filter((m) => m.cercle === "FLX").length || 0,
|
FLX: manifestations?.filter((m: any) => m.cercle === "FLX").length || 0,
|
||||||
HYG: manifestations?.filter((m) => m.cercle === "HYG").length || 0,
|
HYG: manifestations?.filter((m: any) => m.cercle === "HYG").length || 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
// 3. SCORE D'HARMONIE RAFFINÉ
|
// 3. SCORE D'HARMONIE RAFFINÉ
|
||||||
const rawHarmony =
|
const rawHarmony =
|
||||||
manifestations?.reduce((acc, m) => {
|
manifestations?.reduce((acc: number, m: any) => {
|
||||||
if (m.type === "pos") return acc + 1;
|
if (m.type === "pos") return acc + 1;
|
||||||
if (m.type === "neg") return acc - 1;
|
if (m.type === "neg") return acc - 1;
|
||||||
return acc;
|
return acc;
|
||||||
}, 0) || 0;
|
}, 0) || 0;
|
||||||
|
|
||||||
const uniqueCercles = new Set(manifestations?.map((m) => m.cercle)).size;
|
const uniqueCercles = new Set(manifestations?.map((m: any) => m.cercle)).size;
|
||||||
const hasAllCercles = uniqueCercles === 5;
|
const hasAllCercles = uniqueCercles === 5;
|
||||||
const harmonyScore = rawHarmony + (hasAllCercles ? 2 : 0);
|
const harmonyScore = rawHarmony + (hasAllCercles ? 2 : 0);
|
||||||
|
|
||||||
@@ -128,16 +128,16 @@ const biomes = {
|
|||||||
weak: "OCEAN",
|
weak: "OCEAN",
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
const currentBiome = biomes[autoBiome] || biomes.SAVANE;
|
const currentBiome = biomes[autoBiome as keyof typeof biomes] || biomes.SAVANE;
|
||||||
|
|
||||||
const circleSummaries = Object.entries(counts)
|
const circleSummaries = Object.entries(counts)
|
||||||
.filter(([_, count]) => count > 0)
|
.filter(([_, count]: [string, number]) => count > 0)
|
||||||
.map(([type, count]) => {
|
.map(([type, count]: [string, number]) => {
|
||||||
// Calcul du score de dominance pour ce cercle précis
|
// Calcul du score de dominance pour ce cercle précis
|
||||||
const circleScore =
|
const circleScore =
|
||||||
manifestations
|
manifestations
|
||||||
?.filter((m) => m.cercle === type)
|
?.filter((m: any) => m.cercle === type)
|
||||||
.reduce((acc, m) => {
|
.reduce((acc: number, m: any) => {
|
||||||
if (m.type === "pos") return acc + 1;
|
if (m.type === "pos") return acc + 1;
|
||||||
if (m.type === "neg") return acc - 1;
|
if (m.type === "neg") return acc - 1;
|
||||||
return acc;
|
return acc;
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ const {
|
|||||||
image,
|
image,
|
||||||
} = frontmatter;
|
} = frontmatter;
|
||||||
|
|
||||||
// Définition des couleurs par catégorie
|
|
||||||
const categoryColors = {
|
const categoryColors = {
|
||||||
Festival: "#f59e0b",
|
Festival: "#f59e0b",
|
||||||
IRL: "#a1be18",
|
IRL: "#a1be18",
|
||||||
@@ -27,274 +26,171 @@ const categoryColors = {
|
|||||||
default: "#ec4899",
|
default: "#ec4899",
|
||||||
};
|
};
|
||||||
|
|
||||||
const accentColor = categoryColors[type] || categoryColors["default"];
|
const accentColor =
|
||||||
|
categoryColors[type as keyof typeof categoryColors] ||
|
||||||
|
categoryColors["default"];
|
||||||
|
|
||||||
// --- LOGIQUE DE NOTIFICATION DE STATUT ---
|
// --- LOGIQUE DE STATUT ---
|
||||||
const today = new Date();
|
const today = new Date();
|
||||||
const eventDate = new Date(date);
|
const eventDate = new Date(date);
|
||||||
|
|
||||||
// Mise à zéro des heures pour comparer uniquement les jours
|
|
||||||
today.setHours(0, 0, 0, 0);
|
today.setHours(0, 0, 0, 0);
|
||||||
const compareDate = new Date(eventDate);
|
const compareDate = new Date(eventDate);
|
||||||
compareDate.setHours(0, 0, 0, 0);
|
compareDate.setHours(0, 0, 0, 0);
|
||||||
|
// On utilise .getTime() pour transformer les dates en nombres (ms)
|
||||||
const diffTime = compareDate - today;
|
const diffTime = compareDate.getTime() - today.getTime();
|
||||||
const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24));
|
const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24));
|
||||||
|
|
||||||
let dynamicStatus = { label: status, color: "rgba(255,255,255,0.3)", icon: "" };
|
let dynamicStatus = { label: status, color: "rgba(0,0,0,0.3)", icon: "" };
|
||||||
|
|
||||||
if (status === "Terminé") {
|
if (status === "Terminé")
|
||||||
dynamicStatus = { label: "Terminé", color: "#10b981" };
|
dynamicStatus = { label: "Terminé", color: "#10b981", icon: "" }; // Ajoute icon: ""
|
||||||
} else if (status === "En cours") {
|
else if (status === "En cours")
|
||||||
// Nouvelle condition pour le statut "En cours"
|
|
||||||
dynamicStatus = { label: "En cours", color: "#3b82f6", icon: "⚡" };
|
dynamicStatus = { label: "En cours", color: "#3b82f6", icon: "⚡" };
|
||||||
} else if (diffDays < 0) {
|
else if (diffDays < 0)
|
||||||
dynamicStatus = { label: "En retard", color: "#ef4444" };
|
dynamicStatus = { label: "En retard", color: "#ef4444", icon: "" }; // Ajoute icon: ""
|
||||||
} else if (diffDays === 0) {
|
else if (diffDays === 0)
|
||||||
dynamicStatus = { label: "Aujourd'hui", color: "#3b82f6" };
|
dynamicStatus = { label: "Aujourd'hui", color: "#3b82f6", icon: "" }; // Ajoute icon: ""
|
||||||
} else if (diffDays > 0 && diffDays <= 7) {
|
else if (diffDays > 0 && diffDays <= 7)
|
||||||
dynamicStatus = { label: "Imminent", color: "#f59e0b" };
|
dynamicStatus = { label: "Imminent", color: "#f59e0b", icon: "" }; // Ajoute icon: ""
|
||||||
}
|
|
||||||
---
|
---
|
||||||
|
|
||||||
<article
|
<article
|
||||||
class={`event-card ${image ? "has-bg" : ""}`}
|
class={`tcg-card ${image ? "is-dark has-bg" : ""}`}
|
||||||
style={`--accent-color: ${accentColor}; --bg-image: url('${image}'); --status-bg: ${dynamicStatus.color};`}
|
style={`background-color: ${accentColor}; --bg-image: url('${image}');`}
|
||||||
>
|
>
|
||||||
<div class="card-inner">
|
<div class="card-border">
|
||||||
<header class="event-header">
|
<header class="card-header">
|
||||||
<span class="event-type">{type.toUpperCase()}</span>
|
<span class="event-type-badge">{type.toUpperCase()}</span>
|
||||||
<span class="status-notification">
|
<span
|
||||||
{
|
class="status-badge"
|
||||||
dynamicStatus.icon && (
|
style={`background: ${dynamicStatus.color}`}
|
||||||
<span class="status-icon">{dynamicStatus.icon}</span>
|
>
|
||||||
)
|
{dynamicStatus.icon}
|
||||||
}
|
|
||||||
{dynamicStatus.label}
|
{dynamicStatus.label}
|
||||||
</span>
|
</span>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<div class="event-art">
|
<div class="card-art event-visual">
|
||||||
<div class="location-tag">{location}</div>
|
<div class="location-overlay">{location}</div>
|
||||||
<h2 class="event-title">{title}</h2>
|
<h2 class="event-title-main">{title}</h2>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="event-info-line">
|
<div class="card-type-line">
|
||||||
Prévu le : <strong
|
Événement — {new Date(date).toLocaleDateString("fr-FR")}
|
||||||
>{new Date(date).toLocaleDateString("fr-FR")}</strong
|
|
||||||
>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="event-body">
|
<div class="card-body">
|
||||||
<p class="summary">{summary}</p>
|
<p class="summary-text">{summary}</p>
|
||||||
|
|
||||||
<div class="participants-box">
|
<div class="participants-section">
|
||||||
<span class="label">
|
<span class="mini-label"
|
||||||
|
>{type === "Festival" ? "Artistes :" : "Membres :"}</span
|
||||||
|
>
|
||||||
|
<div class="participant-tags">
|
||||||
{
|
{
|
||||||
type === "Festival"
|
participants.map((p: string) => (
|
||||||
? "Artistes invités :"
|
<span class="p-tag">{p}</span>
|
||||||
: "Membres impliqués :"
|
))
|
||||||
}
|
}
|
||||||
</span>
|
|
||||||
<div class="tags">
|
|
||||||
{participants.map((p) => <span class="p-tag">{p}</span>)}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<footer class="event-footer">
|
<footer class="card-footer">
|
||||||
<div class="date-recorded">
|
<div class="footer-meta">Détecté : {target_date}</div>
|
||||||
Projet détecté le : <strong>{target_date}</strong>
|
<div class="circle-badge">{circle}</div>
|
||||||
</div>
|
|
||||||
<div class="social-impact">{circle}</div>
|
|
||||||
</footer>
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
</article>
|
</article>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.event-card {
|
/* On surcharge uniquement ce qui est spécifique à l'événement */
|
||||||
width: 320px;
|
.event-visual {
|
||||||
height: 480px;
|
|
||||||
background-color: var(--accent-color);
|
|
||||||
border-radius: 18px;
|
|
||||||
padding: 12px;
|
|
||||||
font-family: "Philosopher", serif;
|
|
||||||
color: rgba(0, 0, 0, 0.7);
|
|
||||||
transition: background 0.3s ease;
|
|
||||||
position: relative;
|
|
||||||
background-size: cover;
|
|
||||||
background-position: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.event-card.has-bg {
|
|
||||||
color: #fff;
|
|
||||||
text-shadow: 1px 1px 1px rgba(0, 0, 0, 1);
|
|
||||||
background-image:
|
|
||||||
linear-gradient(
|
|
||||||
to bottom,
|
|
||||||
rgba(0, 0, 0, 0) 0%,
|
|
||||||
rgba(0, 0, 0, 0) 100%
|
|
||||||
),
|
|
||||||
var(--bg-image) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-inner {
|
|
||||||
height: 100%;
|
|
||||||
border-radius: 10px;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
padding: 12px;
|
background: rgba(0, 0, 0, 0.05);
|
||||||
background: linear-gradient(
|
|
||||||
rgba(20, 0, 255, 0.1) 0%,
|
|
||||||
rgba(255, 191, 0, 0.1) 70%,
|
|
||||||
rgba(255, 0, 0, 0.3) 100%
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
.event-header {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
font-size: 0.7rem;
|
|
||||||
font-weight: bold;
|
|
||||||
letter-spacing: 1px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Style du nouveau badge de notification */
|
|
||||||
.status-notification {
|
|
||||||
background: var(--status-bg);
|
|
||||||
color: white;
|
|
||||||
padding: 2px 10px;
|
|
||||||
border-radius: 5px;
|
|
||||||
font-size: 0.6rem;
|
|
||||||
text-transform: uppercase;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 5px;
|
|
||||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
|
||||||
text-shadow: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes blink {
|
|
||||||
0%,
|
|
||||||
100% {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
50% {
|
|
||||||
opacity: 0.5;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.event-art {
|
|
||||||
flex: 1;
|
|
||||||
border: 1px solid rgba(0, 0, 0, 0.1);
|
|
||||||
margin: 10px 0;
|
|
||||||
border-radius: 6px;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
text-align: center;
|
text-align: center;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.event-card.has-bg .event-art {
|
.has-bg .card-border {
|
||||||
border: 1px solid rgba(0, 0, 0, 0.1);
|
background: rgba(0, 0, 0, 0.4); /* Voile noir transparent */
|
||||||
|
backdrop-filter: blur(
|
||||||
|
2px
|
||||||
|
); /* Optionnel : floute légèrement l'image derrière le texte */
|
||||||
|
color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
.event-title {
|
.event-title-main {
|
||||||
font-size: 1.4rem;
|
font-size: 1.2rem;
|
||||||
margin: 5px 0;
|
margin: 5px 0;
|
||||||
|
line-height: 1.1;
|
||||||
|
font-family: "Philosopher", serif;
|
||||||
|
font-weight: bold;
|
||||||
|
color: inherit; /* Utilise la couleur parente (blanc ou sombre) */
|
||||||
}
|
}
|
||||||
|
|
||||||
.location-tag {
|
.event-type-badge {
|
||||||
font-size: 0.7rem;
|
font-size: 0.6rem;
|
||||||
opacity: 0.9;
|
padding: 2px 6px;
|
||||||
}
|
border-radius: 3px;
|
||||||
|
background: rgba(0, 0, 0, 0.2);
|
||||||
.event-card.has-bg .location-tag {
|
color: white;
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.event-info-line {
|
|
||||||
font-size: 0.8rem;
|
|
||||||
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
|
|
||||||
padding-bottom: 5px;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.event-card.has-bg .event-info-line {
|
|
||||||
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.event-body {
|
|
||||||
flex: 1.2;
|
|
||||||
font-size: 0.85rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.event-type {
|
|
||||||
text-transform: uppercase;
|
|
||||||
background: rgba(255, 255, 255, 0.3);
|
|
||||||
padding: 2px 8px;
|
|
||||||
border-radius: 4px;
|
|
||||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.event-card.has-bg .event-type {
|
|
||||||
background: rgba(0, 0, 0, 0.4);
|
|
||||||
text-shadow: none;
|
text-shadow: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.summary {
|
.status-badge {
|
||||||
font-style: italic;
|
font-size: 0.6rem;
|
||||||
line-height: 1.4;
|
padding: 2px 8px;
|
||||||
margin-bottom: 10px;
|
border-radius: 10px;
|
||||||
|
color: white;
|
||||||
|
text-transform: uppercase;
|
||||||
}
|
}
|
||||||
|
|
||||||
.participants-box .label {
|
.summary-text {
|
||||||
font-size: 0.65rem;
|
font-size: 0.85rem;
|
||||||
|
font-style: italic;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
line-height: 1.3;
|
||||||
|
opacity: 0.9;
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mini-label {
|
||||||
|
font-size: 0.6rem;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
display: block;
|
display: block;
|
||||||
margin-bottom: 5px;
|
opacity: 0.8;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tags {
|
.participant-tags {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
gap: 4px;
|
gap: 4px;
|
||||||
|
margin-top: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.p-tag {
|
.p-tag {
|
||||||
background: rgba(255, 255, 255, 0.2);
|
font-size: 0.65rem;
|
||||||
|
background: rgba(0, 0, 0, 0.1);
|
||||||
|
padding: 1px 5px;
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.circle-badge {
|
||||||
|
background: rgba(255, 255, 255, 0.8);
|
||||||
|
color: #222;
|
||||||
padding: 2px 6px;
|
padding: 2px 6px;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
font-size: 0.65rem;
|
|
||||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.event-card.has-bg .p-tag {
|
|
||||||
background: rgba(0, 0, 0, 0.4);
|
|
||||||
text-shadow: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.event-footer {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
font-size: 0.7rem;
|
|
||||||
padding-top: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.social-impact {
|
|
||||||
background: #ffffff80;
|
|
||||||
padding: 2px 8px;
|
|
||||||
border-radius: 5px;
|
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
font-size: 0.7rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.event-card.has-bg .social-impact {
|
/* Gestion de l'image de fond si elle existe */
|
||||||
background: rgba(0, 0, 0, 0.4);
|
.has-bg {
|
||||||
text-shadow: none;
|
background-image:
|
||||||
|
linear-gradient(rgba(0, 0, 0, 0.2), rgba(0, 0, 0, 0.2)),
|
||||||
|
var(--bg-image) !important;
|
||||||
|
background-size: cover;
|
||||||
|
background-position: center;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -32,13 +32,13 @@ const { title } = Astro.props;
|
|||||||
>
|
>
|
||||||
<!-- Google tag (gtag.js) -->
|
<!-- Google tag (gtag.js) -->
|
||||||
|
|
||||||
<script>
|
<script is:inline>
|
||||||
|
// On déclare dataLayer pour que TS ne râle pas
|
||||||
window.dataLayer = window.dataLayer || [];
|
window.dataLayer = window.dataLayer || [];
|
||||||
function gtag() {
|
function gtag() {
|
||||||
dataLayer.push(arguments);
|
window.dataLayer.push(arguments);
|
||||||
}
|
}
|
||||||
gtag("js", new Date());
|
gtag("js", new Date());
|
||||||
|
|
||||||
gtag("config", "G-5M920H5PW7");
|
gtag("config", "G-5M920H5PW7");
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -49,49 +49,71 @@ const { title } = Astro.props;
|
|||||||
|
|
||||||
<style is:global>
|
<style is:global>
|
||||||
:root {
|
:root {
|
||||||
--bg-gradient: radial-gradient(
|
--card-width: 320px;
|
||||||
circle at 50% 50%,
|
--card-height: 480px;
|
||||||
#ffffff 0%,
|
--inner-radius: 10px;
|
||||||
#f1f5f9 100%
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
* {
|
/* La coquille physique de toutes les cartes */
|
||||||
box-sizing: border-box;
|
.tcg-card {
|
||||||
margin: 0;
|
width: var(--card-width);
|
||||||
padding: 0;
|
height: var(--card-height);
|
||||||
}
|
border-radius: 18px;
|
||||||
|
padding: 12px;
|
||||||
html,
|
|
||||||
body {
|
|
||||||
overflow-x: hidden;
|
|
||||||
height: 100%;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
font-family: "Inter", sans-serif;
|
|
||||||
background-image: var(--bg-gradient);
|
|
||||||
background-attachment: fixed;
|
|
||||||
color: #1e293b;
|
|
||||||
min-height: 100vh;
|
|
||||||
display: block; /* Change de flex à block pour laisser le contrôle aux pages */
|
|
||||||
line-height: 1.5;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Style spécifique pour les titres de cartes TCG */
|
|
||||||
h1,
|
|
||||||
h2,
|
|
||||||
.name,
|
|
||||||
.trigram {
|
|
||||||
font-family: "Philosopher", serif;
|
font-family: "Philosopher", serif;
|
||||||
|
position: relative;
|
||||||
|
box-sizing: border-box;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.15);
|
||||||
|
color: #1e293b;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Optimisation mobile pour ton Samsung */
|
/* La bordure intérieure commune */
|
||||||
@media (max-width: 480px) {
|
.card-border {
|
||||||
body {
|
flex: 1;
|
||||||
padding: 10px;
|
border-radius: var(--inner-radius);
|
||||||
align-items: flex-start; /* Permet de scroller si la carte est longue */
|
border: 1px solid rgba(0, 0, 0, 0.1);
|
||||||
}
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
padding: 10px;
|
||||||
|
overflow: hidden;
|
||||||
|
background: rgba(
|
||||||
|
255,
|
||||||
|
255,
|
||||||
|
255,
|
||||||
|
0.1
|
||||||
|
); /* Transparence pour laisser voir le biome/couleur */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Les zones partagées par Card et EventCard */
|
||||||
|
.card-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-art {
|
||||||
|
flex: 1;
|
||||||
|
margin: 5px 0;
|
||||||
|
position: relative;
|
||||||
|
border: 1px solid rgba(0, 0, 0, 0.1);
|
||||||
|
border-radius: 4px;
|
||||||
|
overflow: hidden;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
background: rgba(0, 0, 0, 0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-footer {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding-top: 8px;
|
||||||
|
margin-top: auto;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -3,26 +3,35 @@ import { getCollection } from "astro:content";
|
|||||||
import Layout from "../layouts/Layout.astro";
|
import Layout from "../layouts/Layout.astro";
|
||||||
import Card from "../components/Card.astro";
|
import Card from "../components/Card.astro";
|
||||||
import EventCard from "../components/EventCard.astro";
|
import EventCard from "../components/EventCard.astro";
|
||||||
|
import BankCard from "../components/BankCard.astro"; // Import du nouveau composant
|
||||||
|
|
||||||
const allHumans = await getCollection("humans");
|
const allHumans = await getCollection("humans");
|
||||||
|
const myHumans = allHumans.filter((entry: any) => entry.id.startsWith("latchimynicolas/"));
|
||||||
|
|
||||||
|
|
||||||
const allEvents = await getCollection("events");
|
const allEvents = await getCollection("events");
|
||||||
|
const allBanks = await getCollection("banks"); // Récupération des cartes banques/objets
|
||||||
|
|
||||||
// 1. Tri chronologique global
|
// 1. Tri chronologique global
|
||||||
const sortedEntries = allHumans.sort(
|
const sortedEntries = allHumans.sort(
|
||||||
(a, b) => new Date(b.data.date).getTime() - new Date(a.data.date).getTime(),
|
(a: any, b: any) => new Date(b.data.date).getTime() - new Date(a.data.date).getTime()
|
||||||
);
|
);
|
||||||
|
|
||||||
const sortedEvents = allEvents.sort(
|
const sortedEvents = allEvents.sort(
|
||||||
(a, b) => new Date(a.data.date).getTime() - new Date(b.data.date).getTime(),
|
(a: any, b: any) => new Date(a.data.date).getTime() - new Date(b.data.date).getTime(),
|
||||||
);
|
);
|
||||||
|
|
||||||
// 2. Groupement par utilisateur [cite: 80]
|
const sortedBanks = allBanks.sort(
|
||||||
const groupedHumans = sortedEntries.reduce((acc, entry) => {
|
(a: any, b: any) => a.data.name.localeCompare(b.data.name));
|
||||||
|
|
||||||
|
// 2. Groupement par utilisateur
|
||||||
|
const groupedHumans = sortedEntries.reduce((acc: any, entry: any) => {
|
||||||
const userName = entry.data.name;
|
const userName = entry.data.name;
|
||||||
if (!acc[userName]) acc[userName] = [];
|
if (!acc[userName]) acc[userName] = [];
|
||||||
acc[userName].push(entry);
|
acc[userName].push(entry);
|
||||||
return acc;
|
return acc;
|
||||||
}, {});
|
}, {});
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
<Layout title="Collection">
|
<Layout title="Collection">
|
||||||
@@ -38,27 +47,28 @@ const groupedHumans = sortedEntries.reduce((acc, entry) => {
|
|||||||
Object.entries(groupedHumans).map(([userName, cards]) => (
|
Object.entries(groupedHumans).map(([userName, cards]) => (
|
||||||
<section class="user-collection">
|
<section class="user-collection">
|
||||||
<h2 class="user-title">
|
<h2 class="user-title">
|
||||||
{userName} <span>({cards.length} souffles)</span>
|
{userName}
|
||||||
|
<span>({(cards as any[]).length} souffles)</span>
|
||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
<div class="user-flex-row">
|
<div class="user-flex-row">
|
||||||
{cards.map((entry) => (
|
{
|
||||||
<a
|
(cards as any[]).map((entry) => (
|
||||||
href={`/${entry.id.replace(/\.md$/, "")}`}
|
<a
|
||||||
class="card-link"
|
href={`/${entry.id.replace(/\.md$/, "")}`}
|
||||||
>
|
class="card-link"
|
||||||
<div class="card-scaler">
|
>
|
||||||
<Card frontmatter={entry.data} />
|
<div class="card-scaler">
|
||||||
<div class="entry-meta">
|
<Card frontmatter={entry.data} />
|
||||||
<p>
|
<div class="entry-meta">
|
||||||
{new Date(
|
<p>
|
||||||
entry.data.date,
|
{new Date(entry.data.date).toLocaleDateString("fr-FR")}
|
||||||
).toLocaleDateString("fr-FR")}
|
</p>
|
||||||
</p>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</a>
|
||||||
</a>
|
))
|
||||||
))}
|
}
|
||||||
</div>
|
</div>
|
||||||
<hr class="separator" />
|
<hr class="separator" />
|
||||||
</section>
|
</section>
|
||||||
@@ -90,8 +100,28 @@ const groupedHumans = sortedEntries.reduce((acc, entry) => {
|
|||||||
</a>
|
</a>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
<hr class="separator" />
|
<hr class="separator" />
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
<section class="user-collection">
|
||||||
|
<h2 class="user-title">Trésoreries <span>(Sources de Flux)</span></h2>
|
||||||
|
<div class="user-flex-row">
|
||||||
|
{
|
||||||
|
sortedBanks.map((bank) => (
|
||||||
|
<div class="card-link">
|
||||||
|
<div class="card-scaler">
|
||||||
|
{bank.data.sub_category === "finances" ? (
|
||||||
|
<BankCard frontmatter={bank.data} allHumans={myHumans} />
|
||||||
|
) : (
|
||||||
|
/* Autre type de stuff si nécessaire */
|
||||||
|
<div class="tcg-card">Objet: {bank.data.name}</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
</main>
|
</main>
|
||||||
@@ -139,6 +169,7 @@ const groupedHumans = sortedEntries.reduce((acc, entry) => {
|
|||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
gap: 45px; /* Gap simple comme demandé */
|
gap: 45px; /* Gap simple comme demandé */
|
||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
|
padding: 20px 0;
|
||||||
margin-bottom: 100px;
|
margin-bottom: 100px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,16 +3,20 @@ import { getCollection } from "astro:content";
|
|||||||
import Layout from "../layouts/Layout.astro";
|
import Layout from "../layouts/Layout.astro";
|
||||||
import Card from "../components/Card.astro";
|
import Card from "../components/Card.astro";
|
||||||
import EventCard from "../components/EventCard.astro";
|
import EventCard from "../components/EventCard.astro";
|
||||||
|
import BankCard from "../components/BankCard.astro"; // <-- AJOUT
|
||||||
|
|
||||||
// 1. Récupération des données
|
// 1. Récupération des données
|
||||||
const allHumans = await getCollection("humans", ({ id }) => {
|
const allHumans = await getCollection("humans", ({ id }) => {
|
||||||
return id.startsWith("latchimynicolas/");
|
return id.startsWith("latchimynicolas/");
|
||||||
});
|
});
|
||||||
const allEvents = await getCollection("events", ({ data }) => {
|
const allEvents = await getCollection("events", ({ data }) => {
|
||||||
// On ne garde que les événements qui ne sont pas terminés
|
|
||||||
return data.status !== "Terminé";
|
return data.status !== "Terminé";
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Récupération de la banque
|
||||||
|
const allBanks = await getCollection("banks");
|
||||||
|
const upCard = allBanks.find((b) => b.id === "up" || b.id === "up.md");
|
||||||
|
|
||||||
// 2. Tris temporels
|
// 2. Tris temporels
|
||||||
const sortedAll = allHumans.sort(
|
const sortedAll = allHumans.sort(
|
||||||
(a, b) => new Date(b.data.date).getTime() - new Date(a.data.date).getTime(),
|
(a, b) => new Date(b.data.date).getTime() - new Date(a.data.date).getTime(),
|
||||||
@@ -22,17 +26,18 @@ const latestEvent = allEvents.sort(
|
|||||||
(a, b) => new Date(a.data.date).getTime() - new Date(b.data.date).getTime(),
|
(a, b) => new Date(a.data.date).getTime() - new Date(b.data.date).getTime(),
|
||||||
)[0];
|
)[0];
|
||||||
|
|
||||||
// 3. Logique de Streak (Série)
|
// 3. Logique de Streak (Série) - TON CODE ORIGINAL
|
||||||
const getStreak = (name, entries) => {
|
const getStreak = (name: any, entries: any) => {
|
||||||
const userDates = entries
|
const userDates = entries
|
||||||
.filter((e) => e.data.name === name)
|
.filter((e: any) => e.data.name === name)
|
||||||
.map((e) => ({
|
.map((e: any) => ({
|
||||||
date: new Date(e.data.date).toISOString().split("T")[0],
|
date: new Date(e.data.date).toISOString().split("T")[0],
|
||||||
hasHyg:
|
hasHyg:
|
||||||
e.data.manifestations?.some((m) => m.cercle === "HYG") || false,
|
e.data.manifestations?.some((m: any) => m.cercle === "HYG") ||
|
||||||
|
false,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const uniqueDates = [...new Set(userDates.map((d) => d.date))]
|
const uniqueDates = [...new Set(userDates.map((d: any) => d.date))]
|
||||||
.sort()
|
.sort()
|
||||||
.reverse();
|
.reverse();
|
||||||
let streak = 0;
|
let streak = 0;
|
||||||
@@ -60,7 +65,6 @@ const getStreak = (name, entries) => {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const shiftDays = todaySkipped ? 1 : 0;
|
const shiftDays = todaySkipped ? 1 : 0;
|
||||||
const last7Days = Array.from({ length: 7 }, (_, j) => {
|
const last7Days = Array.from({ length: 7 }, (_, j) => {
|
||||||
let d = new Date(
|
let d = new Date(
|
||||||
@@ -71,10 +75,9 @@ const getStreak = (name, entries) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const hygCount = userDates.filter(
|
const hygCount = userDates.filter(
|
||||||
(e) => last7Days.includes(e.date) && e.hasHyg,
|
(e: any) => last7Days.includes(e.date) && e.hasHyg,
|
||||||
).length;
|
).length;
|
||||||
if (hygCount < 3 && streak > 0) streak = Math.max(0, streak - 1);
|
if (hygCount < 3 && streak > 0) streak = Math.max(0, streak - 1);
|
||||||
|
|
||||||
return streak;
|
return streak;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -87,19 +90,6 @@ const latestPerUser = Array.from(
|
|||||||
}, new Map())
|
}, new Map())
|
||||||
.values(),
|
.values(),
|
||||||
);
|
);
|
||||||
|
|
||||||
// 5. CALCUL DE SYNERGIE
|
|
||||||
const activeSynergies = latestPerUser.map((user) => {
|
|
||||||
const matchingManifestation = user.data.manifestations?.find(
|
|
||||||
(m) => m.cercle === latestEvent?.data.circle,
|
|
||||||
);
|
|
||||||
return {
|
|
||||||
userName: user.data.name,
|
|
||||||
hasSynergy: !!matchingManifestation,
|
|
||||||
cercle: latestEvent?.data.circle,
|
|
||||||
type: matchingManifestation?.type,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
---
|
---
|
||||||
|
|
||||||
<Layout title="Dashboard & Codex">
|
<Layout title="Dashboard & Codex">
|
||||||
@@ -113,74 +103,7 @@ const activeSynergies = latestPerUser.map((user) => {
|
|||||||
|
|
||||||
<main>
|
<main>
|
||||||
<h1 class="main-title">Ambient</h1>
|
<h1 class="main-title">Ambient</h1>
|
||||||
<section class="design-intent">
|
|
||||||
<header class="rules-header">
|
|
||||||
<h2>🛠️ Chantier Ergonomique (en cours)</h2>
|
|
||||||
<p class="intent-quote">
|
|
||||||
<em
|
|
||||||
>"Repenser la visualisation des cartes pour un parcours
|
|
||||||
utilisateur en totale concordance avec les besoins du
|
|
||||||
type d'utilisateur."</em
|
|
||||||
>
|
|
||||||
</p>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<div class="work-todo-grid">
|
|
||||||
<div class="todo-column">
|
|
||||||
<h3>🎴 Ergonomie des Cartes</h3>
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
Optimiser le ratio d'aspect pour une lecture
|
|
||||||
mobile-first sur Samsung/Android.
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
Intégrer les indicateurs de "Synergie" directement
|
|
||||||
sur le visuel de la carte.
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
Refondre le système de rareté (Shiny/Légendaire)
|
|
||||||
avec des effets CSS avancés.
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="todo-column">
|
|
||||||
<h3>🛤️ Parcours Utilisateur</h3>
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
Simplifier la transition entre la vue "Dashboard" et
|
|
||||||
la "Collection" complète.
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
Créer des points d'entrée contextuels selon le
|
|
||||||
cercle dominant (PRO, SAN, etc.).
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
Améliorer la narration visuelle entre le manuel de
|
|
||||||
jeu et les logs d'événements.
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="todo-column">
|
|
||||||
<h3>⚙️ Technique & Backlog</h3>
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
Automatiser la mise à jour des versions (Changelog)
|
|
||||||
via les fichiers Markdown.
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
Améliorer la performance du rendu Astro sur le
|
|
||||||
serveur Raspberry Pi 4.
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
Finaliser les composants EventCard pour les
|
|
||||||
correctifs (Fixes).
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
<section class="hero-flex">
|
<section class="hero-flex">
|
||||||
{
|
{
|
||||||
latestPerUser.map((entry) => {
|
latestPerUser.map((entry) => {
|
||||||
@@ -214,162 +137,37 @@ const activeSynergies = latestPerUser.map((user) => {
|
|||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
</section>
|
|
||||||
|
|
||||||
<hr class="separator" />
|
{
|
||||||
|
upCard && (
|
||||||
<section class="system-rules">
|
<div class="card-focus">
|
||||||
<header class="rules-header">
|
<BankCard
|
||||||
<h2>📖 Manuel de Jeu : TCG (Ruleset Complet)</h2>
|
frontmatter={upCard.data}
|
||||||
<p>Traduction technique des algorithmes du moteur de jeu</p>
|
allHumans={allHumans}
|
||||||
</header>
|
/>
|
||||||
|
<div class="entry-meta">
|
||||||
<div class="rules-layout">
|
<p>État du cercle FLX</p>
|
||||||
<div class="rule-category">
|
</div>
|
||||||
<h3>✨ Rareté & Évolution</h3>
|
</div>
|
||||||
<ul>
|
)
|
||||||
<li>
|
}
|
||||||
<strong>SHINY :</strong> Streak ≥ 3 + 3 cercles uniques.
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<strong>LÉGENDAIRE :</strong> Streak ≥ 10 + Harmonie
|
|
||||||
≥ 4.
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<strong>MYTHIQUE :</strong> Streak ≥ 20 + 0 Hallucinations.
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="rule-category">
|
|
||||||
<h3>⚔️ Système de Combat</h3>
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
<strong>Puissance (ATK) :</strong>
|
|
||||||
<code
|
|
||||||
>(PRO × 2) + (Motiv/4) + (Énergie/3) + Bonus
|
|
||||||
Harmonie + StreakBonus</code
|
|
||||||
>.
|
|
||||||
<em
|
|
||||||
>Le travail (PRO) est ton multiplicateur
|
|
||||||
principal.</em
|
|
||||||
>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<strong>Résilience (DEF) :</strong>
|
|
||||||
<code
|
|
||||||
>10 + (Hygiène/2) - Stress - Hallu - Pénalité
|
|
||||||
Sommeil + StreakBonus</code
|
|
||||||
>.
|
|
||||||
<em
|
|
||||||
>L'hygiène est ton armure, le stress ta faille.</em
|
|
||||||
>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<strong>Coût d'Invocation :</strong> Nombre total de manifestations
|
|
||||||
divisé par 2 (arrondi au supérieur).
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="rule-category">
|
|
||||||
<h3>📈 Séries & Rareté (Streak)</h3>
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
<strong>Streak Bonus :</strong> +1 ATK/DEF tous les 7
|
|
||||||
jours de série consécutifs.
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<strong>Éveil Shiny :</strong> Activé dès 3 jours de série
|
|
||||||
+ 3 cercles différents activés.
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<strong>Échelle de Rareté :</strong>
|
|
||||||
<br />• <strong>PEU COMMUNE :</strong> 3 jours de série.
|
|
||||||
<br />• <strong>RARE :</strong> 6 jours de série.
|
|
||||||
<br />• <strong>LÉGENDAIRE :</strong> 10 jours + Harmonie
|
|
||||||
≥ 4.
|
|
||||||
<br />• <strong>MYTHIQUE :</strong> 20 jours + 0 Hallucinations.
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="rule-category">
|
|
||||||
<h3>🌍 Influence des Biomes</h3>
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
<strong>ÉTANG (L'Inerte) :</strong> Priorité Somatique.
|
|
||||||
S'active si <code>Stress ≥ 7</code>, <code
|
|
||||||
>Hallu ≥ 2</code
|
|
||||||
> ou <code>Harmonie ≤ -3</code>.
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<strong>BELOUVE (La Racine) :</strong> État d'Harmonie.
|
|
||||||
S'active si 3+ cercles sont présents avec au moins un
|
|
||||||
log <strong>SAN</strong>.
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<strong>OCEAN (Le Flux) :</strong> S'active si le Social
|
|
||||||
(SOC) domine le Pro ou si le Flux (FLX) est > 2.
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<strong>FOURNAISE (La Forge) :</strong> S'active par dominance
|
|
||||||
technique (PRO ≥ 2 et PRO ≥ SOC).
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<strong>SAVANE (La Clarté) :</strong> Biome d'équilibre
|
|
||||||
par défaut.
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="rule-category">
|
|
||||||
<h3>🔧 Maintenance & Malus</h3>
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
<strong>Pénalité Sommeil :</strong> Si (Sommeil - Malus
|
|
||||||
Travail) < 6h, ta DEF baisse de 2.
|
|
||||||
<em>(Le malus travail s'active si PRO ≥ 5).</em>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<strong>Harmonie des Cercles :</strong> Posséder les 5
|
|
||||||
cercles (SAN, FLX, PRO, SOC, HYG) sur un log octroie un
|
|
||||||
bonus massif de **+2** au Score d'Harmonie.
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<strong>Règle HYG (Prévention) :</strong> Si moins de
|
|
||||||
3 logs "HYG" sont détectés sur les 7 derniers jours, la
|
|
||||||
série (Streak) est amputée d'un point de pénalité.
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="rule-category">
|
|
||||||
<h3>🎭 Objets & Équipements</h3>
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
<strong>VÉLO :</strong> Convertit 2 points de Stress en
|
|
||||||
1 point d'Énergie au petit matin.
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<strong>ARIPIPRAZOLE :</strong> Fixe Hallucinations à
|
|
||||||
0. Ajoute le trait "Stabilité".
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
</section>
|
||||||
</main>
|
</main>
|
||||||
</Layout>
|
</Layout>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.design-intent {
|
.hero-flex {
|
||||||
max-width: 1400px; /* Occupation maximale de l'espace */
|
display: flex;
|
||||||
margin: 60px auto 100px auto;
|
flex-wrap: wrap;
|
||||||
padding: 40px;
|
justify-content: center;
|
||||||
background: #f8fafc;
|
gap: 40px;
|
||||||
border-radius: 24px;
|
padding: 60px 20px;
|
||||||
border: 1px dashed #cbd5e1; /* Aspect "travail en cours" */
|
scroll-snap-type: x mandatory; /* Optionnel pour le swipe Samsung */
|
||||||
}
|
}
|
||||||
|
.card-focus {
|
||||||
|
scroll-snap-align: center;
|
||||||
|
}
|
||||||
|
/* ... le reste de ton CSS ... */
|
||||||
|
|
||||||
.intent-quote {
|
.intent-quote {
|
||||||
max-width: 800px;
|
max-width: 800px;
|
||||||
|
|||||||
Reference in New Issue
Block a user