refacto: constants.ts — source unique frontend
All checks were successful
CI/CD — Build & Deploy / Build & Deploy (push) Successful in 41s

- Centralise RARITY_COLORS, RARITY_LABELS, ZONE_INFO, STAT_LABELS
- Centralise COMBAT_COST, REST_COST, FORGE_*, ATTACK_TYPES
- Supprime 6 duplications dans CombatPage, GuidePage, ShopPage, ForgePage, InventoryPage
This commit is contained in:
2026-03-24 22:30:59 +01:00
parent faf2a98227
commit 17c61a2bb8
6 changed files with 86 additions and 62 deletions

72
frontend/src/constants.ts Normal file
View File

@@ -0,0 +1,72 @@
// ── Game Constants — Source unique frontend ──
// Centralise toutes les constantes dupliquées dans les pages.
export const RARITY_COLORS: Record<string, string> = {
common: '#9ca3af',
rare: '#5ba4f5',
epic: '#a78bfa',
legendary: '#f4c94e',
};
export const RARITY_LABELS: Record<string, string> = {
common: 'Commun',
rare: 'Rare',
epic: 'Épique',
legendary: 'Légendaire',
};
export const ZONE_INFO: Record<string, { name: string; emoji: string; color: string }> = {
marais: { name: 'Les Marais', emoji: '🌿', color: '#3ddc84' },
egouts: { name: 'Les Égouts', emoji: '🕳️', color: '#5ba4f5' },
desert: { name: 'Le Désert', emoji: '🏜️', color: '#f4c94e' },
};
export const STAT_LABELS: Record<string, string> = {
force: 'Force',
agilite: 'Agilité',
intelligence: 'Intelligence',
chance: 'Chance',
vitalite: 'Vitalité',
};
export const TYPE_EMOJI: Record<string, string> = {
weapon: '⚔️',
armor: '🛡️',
consumable: '🧪',
};
// ── Coûts de jeu ──
export const COMBAT_COST = 5;
export const REST_COST = 10;
export const FORGE_ENDURANCE_COST = 10;
export const FORGE_GOLD_COST: Record<number, number> = {
1: 50,
2: 100,
3: 200,
4: 400,
5: 700,
};
export const FORGE_FAIL_CHANCE: Record<number, number> = {
1: 0,
2: 0,
3: 20,
4: 30,
5: 40,
};
export const FORGE_TABLE = [
{ level: 1, gold: 50, endurance: 10, risk: '0%', bonus: '+2' },
{ level: 2, gold: 100, endurance: 10, risk: '0%', bonus: '+4' },
{ level: 3, gold: 200, endurance: 10, risk: '20%', bonus: '+6' },
{ level: 4, gold: 400, endurance: 10, risk: '30%', bonus: '+8' },
{ level: 5, gold: 700, endurance: 10, risk: '40%', bonus: '+10' },
];
export const ATTACK_TYPES = [
{ id: 'melee', label: 'Mêlée', emoji: '⚔️', stat: 'Force × 1.5' },
{ id: 'ranged', label: 'Distance', emoji: '🏹', stat: 'Agilité × 1.5' },
{ id: 'magic', label: 'Magie', emoji: '✨', stat: 'Intelligence × 1.5' },
];

View File

@@ -5,14 +5,7 @@ import { combatApi, characterApi } from '../api/endpoints';
import type { Monster, CombatResult, MultiCombatResult, CombatLog } from '../api/types';
import { Swords, Trophy, Skull, Clock, Zap, Heart, Lock } from 'lucide-react';
const COMBAT_COST = 5;
const REST_COST = 10;
const ATTACK_TYPES = [
{ id: 'melee', label: 'Mêlée', emoji: '⚔️', stat: 'Force × 1.5' },
{ id: 'ranged', label: 'Distance', emoji: '🏹', stat: 'Agilité × 1.5' },
{ id: 'magic', label: 'Magie', emoji: '✨', stat: 'Intelligence × 1.5' },
];
import { COMBAT_COST, REST_COST, ATTACK_TYPES, ZONE_INFO } from '../constants';
function MonsterCard({ m, selected, onSelect, playerLevel }: { m: Monster; selected: boolean; onSelect: () => void; playerLevel: number }) {
const tooHard = m.minLevel > playerLevel + 2;
@@ -211,11 +204,7 @@ export function CombatPage() {
monstersByZone.set(zone, list);
}
const ZONE_LABELS: Record<string, { name: string; emoji: string }> = {
marais: { name: 'Les Marais', emoji: '🌿' },
egouts: { name: 'Les Égouts', emoji: '🕳' },
desert: { name: 'Le Désert', emoji: '🏜' },
};
const ZONE_LABELS = ZONE_INFO;
// Locked zones (zones not in monsters response = locked)
const lockedZones = (zones ?? []).filter((z: any) => !z.unlocked);

View File

@@ -5,10 +5,10 @@ import { itemApi, forgeApi, characterApi } from '../api/endpoints';
import type { CharacterItem } from '../api/types';
import { Shield, CheckCircle, XCircle, AlertTriangle, Zap, Coins } from 'lucide-react';
const FORGE_RISK = [0, 0, 0, 20, 30, 40];
const FORGE_LABEL = ['—', '—', 'Garanti', '20% échec', '30% échec', '40% échec'];
const FORGE_ENDURANCE_COST = 10;
const FORGE_GOLD_COST: Record<number, number> = { 1: 50, 2: 100, 3: 200, 4: 400, 5: 700 };
import { FORGE_FAIL_CHANCE, FORGE_ENDURANCE_COST, FORGE_GOLD_COST } from '../constants';
const FORGE_RISK = [0, 0, 0, FORGE_FAIL_CHANCE[3], FORGE_FAIL_CHANCE[4], FORGE_FAIL_CHANCE[5]];
const FORGE_LABEL = ['—', '—', 'Garanti', `${FORGE_FAIL_CHANCE[3]}% échec`, `${FORGE_FAIL_CHANCE[4]}% échec`, `${FORGE_FAIL_CHANCE[5]}% échec`];
function ForgePanel({ nextLevel, risk, endurance, gold, isPending, onForge }: {
nextLevel: number; risk: number; endurance: number; gold: number; isPending: boolean; onForge: () => void;

View File

@@ -3,29 +3,12 @@ import { useNavigate } from 'react-router-dom';
import type { Monster, Item, Recipe } from '../api/types';
import { Swords, Shield, Map as MapIcon, Hammer, ShoppingBag, BookOpen, Sparkles, Search, Gamepad2 } from 'lucide-react';
import { useGuideData } from '../hooks/useGuideData';
// ── Constants ──
import { RARITY_COLORS, RARITY_LABELS, FORGE_TABLE, ZONE_INFO } from '../constants';
const ZONES = [
{ id: 'marais', name: 'Les Marais', emoji: '🌿', desc: 'Zone de départ. Monstres niv. 1-9. Terre de boue et de brume.', color: '#3ddc84' },
{ id: 'egouts', name: 'Les Égouts', emoji: '🕳️', desc: 'Sous-terrain infesté. Monstres niv. 4-10. Rats, slimes et croco.', color: '#5ba4f5' },
{ id: 'desert', name: 'Le Désert', emoji: '🏜️', desc: 'Sable brûlant. Monstres niv. 8-15. Scorpions, momies et le Sphinx.', color: '#f4c94e' },
];
const RARITY_COLORS: Record<string, string> = {
common: '#9ca3af', rare: '#5ba4f5', epic: '#a78bfa', legendary: '#f4c94e',
};
const RARITY_LABELS: Record<string, string> = {
common: 'Commun', rare: 'Rare', epic: 'Épique', legendary: 'Légendaire',
};
const FORGE_TABLE = [
{ level: 1, gold: 50, endurance: 10, risk: '0%', bonus: '+2' },
{ level: 2, gold: 100, endurance: 10, risk: '0%', bonus: '+4' },
{ level: 3, gold: 200, endurance: 10, risk: '20%', bonus: '+6' },
{ level: 4, gold: 400, endurance: 10, risk: '30%', bonus: '+8' },
{ level: 5, gold: 700, endurance: 10, risk: '40%', bonus: '+10' },
{ id: 'marais', ...ZONE_INFO.marais, desc: 'Zone de départ. Monstres niv. 1-9. Terre de boue et de brume.' },
{ id: 'egouts', ...ZONE_INFO.egouts, desc: 'Sous-terrain infesté. Monstres niv. 4-10. Rats, slimes et croco.' },
{ id: 'desert', ...ZONE_INFO.desert, desc: 'Sable brûlant. Monstres niv. 8-15. Scorpions, momies et le Sphinx.' },
];
const TABS = [

View File

@@ -5,9 +5,7 @@ import { api } from '../api/client';
import type { CharacterItem } from '../api/types';
import { Package, Sword, Shield, Coins } from 'lucide-react';
const RARITY_LABEL: Record<string, string> = {
common: 'Commun', rare: 'Rare', epic: 'Épique', legendary: 'Légendaire',
};
import { RARITY_LABELS as RARITY_LABEL, FORGE_GOLD_COST as FORGE_COSTS_MAP } from '../constants';
function ItemCard({ ci, onEquip, onUnequip, onSell, selling }: {
ci: CharacterItem; onEquip: () => void; onUnequip: () => void; onSell: () => void; selling: boolean;
@@ -17,9 +15,8 @@ function ItemCard({ ci, onEquip, onUnequip, onSell, selling }: {
const forgeBonusDEF = item.type === 'armor' ? ci.forgeLevel * 2 : 0;
const totalATK = item.attackBonus + forgeBonusATK;
const totalDEF = item.defenseBonus + forgeBonusDEF;
const FORGE_COSTS: Record<number, number> = { 1: 50, 2: 100, 3: 200, 4: 400, 5: 700 };
let forgeInvestment = 0;
for (let i = 1; i <= ci.forgeLevel; i++) forgeInvestment += FORGE_COSTS[i] ?? 0;
for (let i = 1; i <= ci.forgeLevel; i++) forgeInvestment += FORGE_COSTS_MAP[i] ?? 0;
const sellPrice = Math.floor(((item as any).buyPrice || 0) * 0.4 + forgeInvestment * 0.5);
const bonuses = [

View File

@@ -4,24 +4,7 @@ import { characterApi } from '../api/endpoints';
import { api } from '../api/client';
import { Coins, ShoppingBag, Sword, Shield, Heart, Zap } from 'lucide-react';
const RARITY_COLORS: Record<string, string> = {
common: '#9ca3af',
rare: '#5ba4f5',
epic: '#a78bfa',
legendary: '#f4c94e',
};
const TYPE_EMOJI: Record<string, string> = {
weapon: '⚔️',
armor: '🛡️',
consumable: '🧪',
};
const ZONE_LABELS: Record<string, string> = {
marais: '🌿 Marais',
egouts: '🕳️ Égouts',
desert: '🏜️ Désert',
};
import { RARITY_COLORS, TYPE_EMOJI, ZONE_INFO } from '../constants';
interface ShopItem {
id: string;
@@ -153,7 +136,7 @@ export function ShopPage() {
{sortedZones.map(([zone, items]) => (
<div key={zone} style={{ marginBottom: '1.5rem' }}>
<p style={{ margin: '0 0 0.5rem', fontSize: 13, fontWeight: 700, color: '#9ca3af' }}>
{zone === 'general' ? '🧪 Consommables' : ZONE_LABELS[zone] ?? zone}
{zone === 'general' ? '🧪 Consommables' : ZONE_INFO[zone] ? `${ZONE_INFO[zone].emoji} ${ZONE_INFO[zone].name}` : zone}
</p>
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 6 }}>
{items.map(item => (