diff --git a/package-lock.json b/package-lock.json index d2057c0..3e6d920 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,7 +8,7 @@ "name": "ambient", "version": "0.0.1", "dependencies": { - "astro": "^5.17.1" + "astro": "^5.18.0" } }, "node_modules/@astrojs/compiler": { @@ -1661,9 +1661,9 @@ } }, "node_modules/astro": { - "version": "5.17.3", - "resolved": "https://registry.npmjs.org/astro/-/astro-5.17.3.tgz", - "integrity": "sha512-69dcfPe8LsHzklwj+hl+vunWUbpMB6pmg35mACjetxbJeUNNys90JaBM8ZiwsPK689SAj/4Zqb1ayaANls9/MA==", + "version": "5.18.0", + "resolved": "https://registry.npmjs.org/astro/-/astro-5.18.0.tgz", + "integrity": "sha512-CHiohwJIS4L0G6/IzE1Fx3dgWqXBCXus/od0eGUfxrZJD2um2pE7ehclMmgL/fXqbU7NfE1Ze2pq34h2QaA6iQ==", "license": "MIT", "dependencies": { "@astrojs/compiler": "^2.13.0", diff --git a/package.json b/package.json index a9159b4..cda51a1 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,6 @@ "astro": "astro" }, "dependencies": { - "astro": "^5.17.1" + "astro": "^5.18.0" } -} \ No newline at end of file +} diff --git a/src/components/BankCard.astro b/src/components/BankCard.astro new file mode 100644 index 0000000..ad6c9d1 --- /dev/null +++ b/src/components/BankCard.astro @@ -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" }; +--- + +
+
+
+ {name} + {config.icon} +
+ +
+
+ + + + {percentage}% + +
+
{status.label}
+
+ +
+ Ressource — {type} +
+ +
+
+ { + percentage > 50 + ? "Le souffle circule avec aisance." + : "Le flux se raréfie, prudence." + } +
+ +
+
+ Intégrité +
+ { + Array.from({ length: 5 }).map((_, i) => ( +
+ )) + } +
+
+
+
+ + +
+
+ + diff --git a/src/components/Card.astro b/src/components/Card.astro index 385be49..5cc4645 100644 --- a/src/components/Card.astro +++ b/src/components/Card.astro @@ -21,22 +21,22 @@ const hygieneScore = health?.somatic?.hygiene_level ?? 5; // 2. RÉINTÉGRATION DES COMPTAGES const counts = { - PRO: manifestations?.filter((m) => m.cercle === "PRO").length || 0, - SAN: manifestations?.filter((m) => m.cercle === "SAN").length || 0, - SOC: manifestations?.filter((m) => m.cercle === "SOC").length || 0, - FLX: manifestations?.filter((m) => m.cercle === "FLX").length || 0, - HYG: manifestations?.filter((m) => m.cercle === "HYG").length || 0, + PRO: manifestations?.filter((m: any) => m.cercle === "PRO").length || 0, + SAN: manifestations?.filter((m: any) => m.cercle === "SAN").length || 0, + SOC: manifestations?.filter((m: any) => m.cercle === "SOC").length || 0, + FLX: manifestations?.filter((m: any) => m.cercle === "FLX").length || 0, + HYG: manifestations?.filter((m: any) => m.cercle === "HYG").length || 0, }; // 3. SCORE D'HARMONIE RAFFINÉ const rawHarmony = - manifestations?.reduce((acc, m) => { + manifestations?.reduce((acc: number, m: any) => { if (m.type === "pos") return acc + 1; if (m.type === "neg") return acc - 1; return acc; }, 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 harmonyScore = rawHarmony + (hasAllCercles ? 2 : 0); @@ -128,16 +128,16 @@ const biomes = { weak: "OCEAN", }, }; -const currentBiome = biomes[autoBiome] || biomes.SAVANE; +const currentBiome = biomes[autoBiome as keyof typeof biomes] || biomes.SAVANE; const circleSummaries = Object.entries(counts) - .filter(([_, count]) => count > 0) - .map(([type, count]) => { + .filter(([_, count]: [string, number]) => count > 0) + .map(([type, count]: [string, number]) => { // Calcul du score de dominance pour ce cercle prĂ©cis const circleScore = manifestations - ?.filter((m) => m.cercle === type) - .reduce((acc, m) => { + ?.filter((m: any) => m.cercle === type) + .reduce((acc: number, m: any) => { if (m.type === "pos") return acc + 1; if (m.type === "neg") return acc - 1; return acc; diff --git a/src/components/EventCard.astro b/src/components/EventCard.astro index 6319ead..3692c0a 100644 --- a/src/components/EventCard.astro +++ b/src/components/EventCard.astro @@ -14,7 +14,6 @@ const { image, } = frontmatter; -// DĂ©finition des couleurs par catĂ©gorie const categoryColors = { Festival: "#f59e0b", IRL: "#a1be18", @@ -27,274 +26,171 @@ const categoryColors = { 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 eventDate = new Date(date); - -// Mise Ă  zĂ©ro des heures pour comparer uniquement les jours today.setHours(0, 0, 0, 0); const compareDate = new Date(eventDate); compareDate.setHours(0, 0, 0, 0); - -const diffTime = compareDate - today; +// On utilise .getTime() pour transformer les dates en nombres (ms) +const diffTime = compareDate.getTime() - today.getTime(); 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Ă©") { - dynamicStatus = { label: "TerminĂ©", color: "#10b981" }; -} else if (status === "En cours") { - // Nouvelle condition pour le statut "En cours" +if (status === "TerminĂ©") + dynamicStatus = { label: "TerminĂ©", color: "#10b981", icon: "" }; // Ajoute icon: "" +else if (status === "En cours") dynamicStatus = { label: "En cours", color: "#3b82f6", icon: "⚡" }; -} else if (diffDays < 0) { - dynamicStatus = { label: "En retard", color: "#ef4444" }; -} else if (diffDays === 0) { - dynamicStatus = { label: "Aujourd'hui", color: "#3b82f6" }; -} else if (diffDays > 0 && diffDays <= 7) { - dynamicStatus = { label: "Imminent", color: "#f59e0b" }; -} +else if (diffDays < 0) + dynamicStatus = { label: "En retard", color: "#ef4444", icon: "" }; // Ajoute icon: "" +else if (diffDays === 0) + dynamicStatus = { label: "Aujourd'hui", color: "#3b82f6", icon: "" }; // Ajoute icon: "" +else if (diffDays > 0 && diffDays <= 7) + dynamicStatus = { label: "Imminent", color: "#f59e0b", icon: "" }; // Ajoute icon: "" ---
-
-
- {type.toUpperCase()} - - { - dynamicStatus.icon && ( - {dynamicStatus.icon} - ) - } +
+
+ {type.toUpperCase()} + + {dynamicStatus.icon} {dynamicStatus.label}
-
-
{location}
-

{title}

+
+
{location}
+

{title}

-
- Prévu le : {new Date(date).toLocaleDateString("fr-FR")} +
+ ÉvĂ©nement — {new Date(date).toLocaleDateString("fr-FR")}
-
-

{summary}

+
+

{summary}

-
- +
+ {type === "Festival" ? "Artistes :" : "Membres :"} +
{ - type === "Festival" - ? "Artistes invités :" - : "Membres impliqués :" + participants.map((p: string) => ( + {p} + )) } - -
- {participants.map((p) => {p})}
-
diff --git a/src/layouts/Layout.astro b/src/layouts/Layout.astro index 4e2e43e..d032c93 100644 --- a/src/layouts/Layout.astro +++ b/src/layouts/Layout.astro @@ -32,13 +32,13 @@ const { title } = Astro.props; > - @@ -49,49 +49,71 @@ const { title } = Astro.props; diff --git a/src/pages/collection.astro b/src/pages/collection.astro index ab6ca06..5cf6a79 100644 --- a/src/pages/collection.astro +++ b/src/pages/collection.astro @@ -3,26 +3,35 @@ import { getCollection } from "astro:content"; import Layout from "../layouts/Layout.astro"; import Card from "../components/Card.astro"; import EventCard from "../components/EventCard.astro"; +import BankCard from "../components/BankCard.astro"; // Import du nouveau composant const allHumans = await getCollection("humans"); +const myHumans = allHumans.filter((entry: any) => entry.id.startsWith("latchimynicolas/")); + + const allEvents = await getCollection("events"); +const allBanks = await getCollection("banks"); // Récupération des cartes banques/objets // 1. Tri chronologique global 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( - (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 groupedHumans = sortedEntries.reduce((acc, entry) => { +const sortedBanks = allBanks.sort( + (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; if (!acc[userName]) acc[userName] = []; acc[userName].push(entry); return acc; }, {}); + --- @@ -38,27 +47,28 @@ const groupedHumans = sortedEntries.reduce((acc, entry) => { Object.entries(groupedHumans).map(([userName, cards]) => (

- {userName} ({cards.length} souffles) + {userName} + ({(cards as any[]).length} souffles)

@@ -90,8 +100,28 @@ const groupedHumans = sortedEntries.reduce((acc, entry) => { ))} -
+
+ +
+

Trésoreries (Sources de Flux)

+
+ { + sortedBanks.map((bank) => ( + + )) + } +
+
) } @@ -139,6 +169,7 @@ const groupedHumans = sortedEntries.reduce((acc, entry) => { flex-wrap: wrap; gap: 45px; /* Gap simple comme demandé */ justify-content: flex-start; + padding: 20px 0; margin-bottom: 100px; } diff --git a/src/pages/index.astro b/src/pages/index.astro index 46f38ef..c44f99c 100644 --- a/src/pages/index.astro +++ b/src/pages/index.astro @@ -3,16 +3,20 @@ import { getCollection } from "astro:content"; import Layout from "../layouts/Layout.astro"; import Card from "../components/Card.astro"; import EventCard from "../components/EventCard.astro"; +import BankCard from "../components/BankCard.astro"; // <-- AJOUT // 1. Récupération des données const allHumans = await getCollection("humans", ({ id }) => { return id.startsWith("latchimynicolas/"); }); const allEvents = await getCollection("events", ({ data }) => { - // On ne garde que les événements qui ne sont pas terminés 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 const sortedAll = allHumans.sort( (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(), )[0]; -// 3. Logique de Streak (Série) -const getStreak = (name, entries) => { +// 3. Logique de Streak (Série) - TON CODE ORIGINAL +const getStreak = (name: any, entries: any) => { const userDates = entries - .filter((e) => e.data.name === name) - .map((e) => ({ + .filter((e: any) => e.data.name === name) + .map((e: any) => ({ date: new Date(e.data.date).toISOString().split("T")[0], 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() .reverse(); let streak = 0; @@ -60,7 +65,6 @@ const getStreak = (name, entries) => { break; } } - const shiftDays = todaySkipped ? 1 : 0; const last7Days = Array.from({ length: 7 }, (_, j) => { let d = new Date( @@ -71,10 +75,9 @@ const getStreak = (name, entries) => { }); const hygCount = userDates.filter( - (e) => last7Days.includes(e.date) && e.hasHyg, + (e: any) => last7Days.includes(e.date) && e.hasHyg, ).length; if (hygCount < 3 && streak > 0) streak = Math.max(0, streak - 1); - return streak; }; @@ -87,19 +90,6 @@ const latestPerUser = Array.from( }, new Map()) .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, - }; -}); --- @@ -113,74 +103,7 @@ const activeSynergies = latestPerUser.map((user) => {

Ambient

-
-
-

đŸ› ïž Chantier Ergonomique (en cours)

-

- "Repenser la visualisation des cartes pour un parcours - utilisateur en totale concordance avec les besoins du - type d'utilisateur." -

-
-
-
-

🎮 Ergonomie des Cartes

-
    -
  • - Optimiser le ratio d'aspect pour une lecture - mobile-first sur Samsung/Android. -
  • -
  • - IntĂ©grer les indicateurs de "Synergie" directement - sur le visuel de la carte. -
  • -
  • - Refondre le systĂšme de raretĂ© (Shiny/LĂ©gendaire) - avec des effets CSS avancĂ©s. -
  • -
-
- -
-

đŸ›€ïž Parcours Utilisateur

-
    -
  • - Simplifier la transition entre la vue "Dashboard" et - la "Collection" complĂšte. -
  • -
  • - CrĂ©er des points d'entrĂ©e contextuels selon le - cercle dominant (PRO, SAN, etc.). -
  • -
  • - AmĂ©liorer la narration visuelle entre le manuel de - jeu et les logs d'Ă©vĂ©nements. -
  • -
-
- -
-

⚙ Technique & Backlog

-
    -
  • - Automatiser la mise Ă  jour des versions (Changelog) - via les fichiers Markdown. -
  • -
  • - AmĂ©liorer la performance du rendu Astro sur le - serveur Raspberry Pi 4. -
  • -
  • - Finaliser les composants EventCard pour les - correctifs (Fixes). -
  • -
-
-
-
{ latestPerUser.map((entry) => { @@ -214,162 +137,37 @@ const activeSynergies = latestPerUser.map((user) => { ) } -
-
- -
-
-

📖 Manuel de Jeu : TCG (Ruleset Complet)

-

Traduction technique des algorithmes du moteur de jeu

-
- -
-
-

✹ RaretĂ© & Évolution

-
    -
  • - SHINY : Streak ≥ 3 + 3 cercles uniques. -
  • -
  • - LÉGENDAIRE : Streak ≥ 10 + Harmonie - ≥ 4. -
  • -
  • - MYTHIQUE : Streak ≥ 20 + 0 Hallucinations. -
  • -
-
- -
-

⚔ SystĂšme de Combat

-
    -
  • - Puissance (ATK) : - (PRO × 2) + (Motiv/4) + (Énergie/3) + Bonus - Harmonie + StreakBonus. - Le travail (PRO) est ton multiplicateur - principal. -
  • -
  • - RĂ©silience (DEF) : - 10 + (HygiĂšne/2) - Stress - Hallu - PĂ©nalitĂ© - Sommeil + StreakBonus. - L'hygiĂšne est ton armure, le stress ta faille. -
  • -
  • - CoĂ»t d'Invocation : Nombre total de manifestations - divisĂ© par 2 (arrondi au supĂ©rieur). -
  • -
-
- -
-

📈 SĂ©ries & RaretĂ© (Streak)

-
    -
  • - Streak Bonus : +1 ATK/DEF tous les 7 - jours de sĂ©rie consĂ©cutifs. -
  • -
  • - Éveil Shiny : ActivĂ© dĂšs 3 jours de sĂ©rie - + 3 cercles diffĂ©rents activĂ©s. -
  • -
  • - Échelle de RaretĂ© : -
    ‱ PEU COMMUNE : 3 jours de sĂ©rie. -
    ‱ RARE : 6 jours de sĂ©rie. -
    ‱ LÉGENDAIRE : 10 jours + Harmonie - ≥ 4. -
    ‱ MYTHIQUE : 20 jours + 0 Hallucinations. -
  • -
-
- -
-

🌍 Influence des Biomes

-
    -
  • - ÉTANG (L'Inerte) : PrioritĂ© Somatique. - S'active si Stress ≥ 7, Hallu ≥ 2 ou Harmonie ≤ -3. -
  • -
  • - BELOUVE (La Racine) : État d'Harmonie. - S'active si 3+ cercles sont prĂ©sents avec au moins un - log SAN. -
  • -
  • - OCEAN (Le Flux) : S'active si le Social - (SOC) domine le Pro ou si le Flux (FLX) est > 2. -
  • -
  • - FOURNAISE (La Forge) : S'active par dominance - technique (PRO ≥ 2 et PRO ≥ SOC). -
  • -
  • - SAVANE (La ClartĂ©) : Biome d'Ă©quilibre - par dĂ©faut. -
  • -
-
- -
-

🔧 Maintenance & Malus

-
    -
  • - PĂ©nalitĂ© Sommeil : Si (Sommeil - Malus - Travail) < 6h, ta DEF baisse de 2. - (Le malus travail s'active si PRO ≥ 5). -
  • -
  • - Harmonie des Cercles : PossĂ©der les 5 - cercles (SAN, FLX, PRO, SOC, HYG) sur un log octroie un - bonus massif de **+2** au Score d'Harmonie. -
  • -
  • - RĂšgle HYG (PrĂ©vention) : 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Ă©. -
  • -
-
- -
-

🎭 Objets & Équipements

-
    -
  • - VÉLO : Convertit 2 points de Stress en - 1 point d'Énergie au petit matin. -
  • -
  • - ARIPIPRAZOLE : Fixe Hallucinations Ă  - 0. Ajoute le trait "StabilitĂ©". -
  • -
-
-
+ { + upCard && ( +
+ + +
+ ) + }
+