Résumé de partie : détail des manches + icônes de catégories
Build and Publish Docker Image / build-and-push-image (push) Successful in 59s
Build and Publish Docker Image / build-and-push-image (push) Successful in 59s
- GameOver : nouvelle section "Détail des manches" (tableau manches × joueurs avec ligne Total) pour les parties multi-manches ; indicateurs Cabo/Kamikaze - ScoringCategory : champs icon (lucide) + color pour un visuel par catégorie - Harmonies : icônes + couleurs de terrain (arbres, montagnes, champs, bâtiments, rivière, animaux, esprits) affichées dans PlayGame et GameOver Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -1,17 +1,19 @@
|
|||||||
import { GameConfig, ScoringCategory } from "../../types";
|
import { GameConfig, ScoringCategory } from "../../types";
|
||||||
|
|
||||||
const BASE_CATEGORIES: ScoringCategory[] = [
|
const BASE_CATEGORIES: ScoringCategory[] = [
|
||||||
{ id: "arbres", label: "Arbres" },
|
{ id: "arbres", label: "Arbres", icon: "TreePine", color: "#16a34a" },
|
||||||
{ id: "montagnes", label: "Montagnes" },
|
{ id: "montagnes", label: "Montagnes", icon: "Mountain", color: "#64748b" },
|
||||||
{ id: "champs", label: "Champs" },
|
{ id: "champs", label: "Champs", icon: "Wheat", color: "#d97706" },
|
||||||
{ id: "batiments", label: "Bâtiments" },
|
{ id: "batiments", label: "Bâtiments", icon: "Building2", color: "#dc2626" },
|
||||||
{ id: "eau", label: "Rivière" },
|
{ id: "eau", label: "Rivière", icon: "Waves", color: "#0ea5e9" },
|
||||||
{ id: "animaux", label: "Animaux" },
|
{ id: "animaux", label: "Animaux", icon: "Rabbit", color: "#a16207" },
|
||||||
];
|
];
|
||||||
|
|
||||||
const NATURE_SPIRIT_CATEGORY: ScoringCategory = {
|
const NATURE_SPIRIT_CATEGORY: ScoringCategory = {
|
||||||
id: "esprits_nature",
|
id: "esprits_nature",
|
||||||
label: "Esprits de la nature",
|
label: "Esprits de la nature",
|
||||||
|
icon: "Sparkles",
|
||||||
|
color: "#8b5cf6",
|
||||||
};
|
};
|
||||||
|
|
||||||
export const harmonieConfig: GameConfig = {
|
export const harmonieConfig: GameConfig = {
|
||||||
|
|||||||
+94
-1
@@ -137,7 +137,7 @@ export default function GameOver() {
|
|||||||
const topWinner = totals.find((t) => t.isWinner)?.player;
|
const topWinner = totals.find((t) => t.isWinner)?.player;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col min-h-screen bg-background pb-24">
|
<div className="flex flex-col min-h-screen bg-background pb-40">
|
||||||
{/* 💥 Animation de Célébration (disparaît après qq secondes) */}
|
{/* 💥 Animation de Célébration (disparaît après qq secondes) */}
|
||||||
{showCelebration && topWinner && (
|
{showCelebration && topWinner && (
|
||||||
<CelebrationOverlay winner={topWinner} gameId={gameConfig?.id || ""} />
|
<CelebrationOverlay winner={topWinner} gameId={gameConfig?.id || ""} />
|
||||||
@@ -242,11 +242,21 @@ export default function GameOver() {
|
|||||||
{categories.map((cat) => {
|
{categories.map((cat) => {
|
||||||
const val = t.details?.[cat.id];
|
const val = t.details?.[cat.id];
|
||||||
if (!val || val === "0") return null;
|
if (!val || val === "0") return null;
|
||||||
|
const CatIcon = cat.icon
|
||||||
|
? (Icons as any)[cat.icon]
|
||||||
|
: null;
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
key={cat.id}
|
key={cat.id}
|
||||||
className="flex flex-col items-center bg-secondary/30 rounded-lg p-1.5"
|
className="flex flex-col items-center bg-secondary/30 rounded-lg p-1.5"
|
||||||
>
|
>
|
||||||
|
{CatIcon && (
|
||||||
|
<CatIcon
|
||||||
|
className="w-4 h-4 mb-0.5"
|
||||||
|
style={{ color: cat.color ?? "currentColor" }}
|
||||||
|
strokeWidth={2.2}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
<span className="text-[10px] text-muted-foreground truncate w-full text-center">
|
<span className="text-[10px] text-muted-foreground truncate w-full text-center">
|
||||||
{cat.label}
|
{cat.label}
|
||||||
</span>
|
</span>
|
||||||
@@ -262,6 +272,89 @@ export default function GameOver() {
|
|||||||
</motion.div>
|
</motion.div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Round-by-round detail (multi-round games) */}
|
||||||
|
{session.rounds.length > 1 && (
|
||||||
|
<section className="pt-2">
|
||||||
|
<h2 className="text-xl font-semibold px-2 mb-2">
|
||||||
|
Détail des manches
|
||||||
|
</h2>
|
||||||
|
<Card className="overflow-hidden">
|
||||||
|
<CardContent className="p-0 overflow-x-auto">
|
||||||
|
<table className="w-full text-sm border-collapse">
|
||||||
|
<thead>
|
||||||
|
<tr className="border-b border-border/60">
|
||||||
|
<th className="text-left font-black text-muted-foreground px-3 py-2.5 sticky left-0 bg-card z-10">
|
||||||
|
Manche
|
||||||
|
</th>
|
||||||
|
{session.players.map((p) => (
|
||||||
|
<th
|
||||||
|
key={p.id}
|
||||||
|
className="px-3 py-2.5 text-right font-bold whitespace-nowrap"
|
||||||
|
>
|
||||||
|
{p.name}
|
||||||
|
</th>
|
||||||
|
))}
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{session.rounds.map((round) => (
|
||||||
|
<tr
|
||||||
|
key={round.id}
|
||||||
|
className="border-b border-border/30 last:border-0"
|
||||||
|
>
|
||||||
|
<td className="px-3 py-2 font-bold text-muted-foreground sticky left-0 bg-card z-10">
|
||||||
|
#{round.roundNumber}
|
||||||
|
</td>
|
||||||
|
{session.players.map((p) => {
|
||||||
|
const s = round.scores.find(
|
||||||
|
(sc) => sc.playerId === p.id,
|
||||||
|
);
|
||||||
|
return (
|
||||||
|
<td
|
||||||
|
key={p.id}
|
||||||
|
className="px-3 py-2 text-right tabular-nums"
|
||||||
|
>
|
||||||
|
{s ? s.score : "—"}
|
||||||
|
{s?.caboCall === "success" && (
|
||||||
|
<span className="ml-1 text-[10px] text-green-600 font-bold">
|
||||||
|
✓
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
{s?.caboCall === "fail" && (
|
||||||
|
<span className="ml-1 text-[10px] text-destructive font-bold">
|
||||||
|
✗
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
{s?.kamikaze && (
|
||||||
|
<span className="ml-1 text-[10px] text-amber-600 font-bold">
|
||||||
|
K
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</td>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
<tr className="bg-secondary/50 font-black">
|
||||||
|
<td className="px-3 py-2.5 sticky left-0 bg-secondary/50 z-10">
|
||||||
|
Total
|
||||||
|
</td>
|
||||||
|
{session.players.map((p) => (
|
||||||
|
<td
|
||||||
|
key={p.id}
|
||||||
|
className="px-3 py-2.5 text-right tabular-nums text-primary"
|
||||||
|
>
|
||||||
|
{calculateTotalScore(p.id)}
|
||||||
|
</td>
|
||||||
|
))}
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</section>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="fixed bottom-0 left-0 right-0 p-4 bg-background/95 backdrop-blur-md border-t flex flex-wrap gap-3 justify-center max-w-md mx-auto">
|
<div className="fixed bottom-0 left-0 right-0 p-4 bg-background/95 backdrop-blur-md border-t flex flex-wrap gap-3 justify-center max-w-md mx-auto">
|
||||||
|
|||||||
+25
-7
@@ -322,13 +322,31 @@ export default function PlayGame() {
|
|||||||
{/* Current Round Input Section */}
|
{/* Current Round Input Section */}
|
||||||
<section className="relative">
|
<section className="relative">
|
||||||
<div className="flex items-center justify-between px-2 mb-4">
|
<div className="flex items-center justify-between px-2 mb-4">
|
||||||
<h2 className="text-2xl font-black tracking-tighter drop-shadow-sm">
|
<div className="flex items-center gap-2.5 min-w-0">
|
||||||
{categories
|
{categories &&
|
||||||
? categories[currentCategoryIndex].label
|
(() => {
|
||||||
: isSingleRound
|
const cat = categories[currentCategoryIndex];
|
||||||
? "Saisie des scores"
|
const CatIcon = cat.icon ? (Icons as any)[cat.icon] : null;
|
||||||
: "À vos marques !"}
|
return CatIcon ? (
|
||||||
</h2>
|
<span
|
||||||
|
className="w-11 h-11 rounded-2xl grid place-items-center shrink-0 shadow-sm"
|
||||||
|
style={{
|
||||||
|
backgroundColor: `${cat.color ?? "#14b8a6"}22`,
|
||||||
|
color: cat.color ?? "#14b8a6",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<CatIcon className="w-6 h-6" strokeWidth={2.2} />
|
||||||
|
</span>
|
||||||
|
) : null;
|
||||||
|
})()}
|
||||||
|
<h2 className="text-2xl font-black tracking-tighter drop-shadow-sm truncate">
|
||||||
|
{categories
|
||||||
|
? categories[currentCategoryIndex].label
|
||||||
|
: isSingleRound
|
||||||
|
? "Saisie des scores"
|
||||||
|
: "À vos marques !"}
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
{categories && currentCategoryIndex > 0 && (
|
{categories && currentCategoryIndex > 0 && (
|
||||||
<Button
|
<Button
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ export type ScoreType =
|
|||||||
export interface ScoringCategory {
|
export interface ScoringCategory {
|
||||||
id: string;
|
id: string;
|
||||||
label: string;
|
label: string;
|
||||||
|
icon?: string; // nom d'icône lucide (ex: "TreePine") — visuel de la catégorie
|
||||||
|
color?: string; // teinte d'accent hex pour l'icône
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface GameConfig {
|
export interface GameConfig {
|
||||||
|
|||||||
Reference in New Issue
Block a user