refacto: découpage composants — 5 extractions
All checks were successful
CI/CD — Build & Deploy / Build & Deploy (push) Successful in 34s
All checks were successful
CI/CD — Build & Deploy / Build & Deploy (push) Successful in 34s
- MonsterCard, CombatViews (Log+Multi+History), CreateCharacter - RarityBadge + RarityDot partagés (Guide, Drawer, pages) - CombatPage 341→215 lignes (−37%) - DashboardPage 368→307 lignes (−17%) - 9 composants dans components/
This commit is contained in:
@@ -2,137 +2,11 @@ import { useState, useCallback } from 'react';
|
||||
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
import toast from 'react-hot-toast';
|
||||
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';
|
||||
|
||||
import type { Monster, CombatResult, MultiCombatResult } from '../api/types';
|
||||
import { Swords, Clock, Zap, Heart, Lock } from 'lucide-react';
|
||||
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;
|
||||
const tooEasy = m.maxLevel < playerLevel - 3;
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`card card-hover ${selected ? 'card-gold' : ''}`}
|
||||
onClick={onSelect}
|
||||
style={{ cursor: 'pointer', transition: 'all 0.15s', opacity: tooHard ? 0.4 : 1 }}
|
||||
>
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', marginBottom: 6 }}>
|
||||
<span style={{ fontWeight: 700, fontSize: 14, color: selected ? '#f4c94e' : '#dce4f0' }}>{m.name}</span>
|
||||
<span className={tooHard ? 'badge badge-red' : tooEasy ? 'badge' : 'badge badge-green'} style={{ fontSize: 10 }}>
|
||||
Niv. {m.minLevel}–{m.maxLevel}
|
||||
</span>
|
||||
</div>
|
||||
<div style={{ display: 'flex', gap: 12, fontSize: 12, color: '#6b7a99' }}>
|
||||
<span>❤️ {m.hp}</span>
|
||||
<span>⚔️ {m.attack}</span>
|
||||
<span>🛡️ {m.defense}</span>
|
||||
<span>⭐ {m.xpReward} XP</span>
|
||||
<span>💰 {m.goldMin}–{m.goldMax}</span>
|
||||
</div>
|
||||
{tooHard && <div style={{ fontSize: 10, color: '#e84040', marginTop: 4 }}>Niveau trop élevé</div>}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function CombatLogView({ result }: { result: CombatResult }) {
|
||||
const won = result.winner === 'player';
|
||||
return (
|
||||
<div className="card" style={{ marginTop: '1rem' }}>
|
||||
<div style={{ textAlign: 'center', padding: '0.75rem 0', marginBottom: '0.75rem', borderBottom: '1px solid #2a3448' }}>
|
||||
{won
|
||||
? <div style={{ color: '#3ddc84', fontWeight: 800, fontSize: 18 }}>
|
||||
<Trophy size={20} style={{ display: 'inline', marginRight: 8 }} />
|
||||
Victoire ! +{result.rewards.xp} XP +{result.rewards.gold} or
|
||||
</div>
|
||||
: <div style={{ color: '#e84040', fontWeight: 800, fontSize: 18 }}>
|
||||
<Skull size={20} style={{ display: 'inline', marginRight: 8 }} />
|
||||
Défaite… Retour à l'auberge
|
||||
</div>
|
||||
}
|
||||
{result.rewards.loot && (
|
||||
<div style={{ fontSize: 13, color: '#f4c94e', marginTop: 4 }}>
|
||||
🎁 Loot : {result.rewards.loot.name} ×{result.rewards.loot.quantity}
|
||||
</div>
|
||||
)}
|
||||
{result.rewards.levelUp && (
|
||||
<div style={{ fontSize: 13, color: '#a78bfa', marginTop: 4 }}>
|
||||
🎉 LEVEL UP ! Niveau {result.rewards.newLevel} — +{result.rewards.statPointsGained} points de stats
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<p style={{ margin: '0 0 6px', fontSize: 12, fontWeight: 700, color: '#6b7a99' }}>
|
||||
Log — {result.rounds.length} tour{result.rounds.length > 1 ? 's' : ''}
|
||||
</p>
|
||||
<div className="combat-log">
|
||||
{result.rounds.flatMap(r =>
|
||||
r.log.map((line, i) => {
|
||||
const cls = line.includes('CRITIQUE') ? 'log-crit'
|
||||
: line.includes('esquive') ? 'log-crit'
|
||||
: line.includes('HP') ? 'log-system'
|
||||
: i === 0 ? 'log-player'
|
||||
: 'log-monster';
|
||||
return <div key={`${r.round}-${i}`} className={cls}>[T{r.round}] {line}</div>;
|
||||
})
|
||||
)}
|
||||
{won
|
||||
? <div className="log-system">══ Victoire ══</div>
|
||||
: <div className="log-monster">══ Défaite ══</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function MultiCombatView({ result }: { result: MultiCombatResult }) {
|
||||
const t = result.totals;
|
||||
return (
|
||||
<div className="card" style={{ marginTop: '1rem' }}>
|
||||
<div style={{ textAlign: 'center', padding: '0.75rem 0', marginBottom: '0.75rem', borderBottom: '1px solid #2a3448' }}>
|
||||
<div style={{ fontWeight: 800, fontSize: 18, color: t.losses > 0 ? '#e84040' : '#3ddc84' }}>
|
||||
{t.losses > 0 ? <Skull size={20} style={{ display: 'inline', marginRight: 8 }} /> : <Trophy size={20} style={{ display: 'inline', marginRight: 8 }} />}
|
||||
{result.count} combat{result.count > 1 ? 's' : ''} — {t.wins}V / {t.losses}D
|
||||
</div>
|
||||
<div style={{ fontSize: 14, color: '#dce4f0', marginTop: 6 }}>
|
||||
+{t.xp} XP +{t.gold} Or
|
||||
{t.goldLost > 0 && <span style={{ color: '#e84040' }}> −{t.goldLost} Or</span>}
|
||||
</div>
|
||||
{t.levelsGained > 0 && (
|
||||
<div style={{ fontSize: 13, color: '#a78bfa', marginTop: 4 }}>
|
||||
🎉 {t.levelsGained} level up{t.levelsGained > 1 ? 's' : ''} !
|
||||
</div>
|
||||
)}
|
||||
{t.loot.length > 0 && (
|
||||
<div style={{ fontSize: 13, color: '#f4c94e', marginTop: 4 }}>
|
||||
🎁 Loot : {t.loot.reduce((sum, l) => sum + l.quantity, 0)} matériaux
|
||||
</div>
|
||||
)}
|
||||
{t.losses > 0 && (
|
||||
<div style={{ fontSize: 11, color: '#6b7a99', marginTop: 4 }}>
|
||||
Série interrompue par une défaite
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function HistoryEntry({ h }: { h: CombatLog }) {
|
||||
return (
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between', fontSize: 12, padding: '3px 0', borderBottom: '1px solid #1e2535' }}>
|
||||
<span style={{ color: h.winner === 'player' ? '#3ddc84' : '#e84040' }}>
|
||||
{h.winner === 'player' ? '✓' : '✗'} {h.monster.name}
|
||||
</span>
|
||||
<span style={{ color: '#6b7a99' }}>
|
||||
{h.winner === 'player'
|
||||
? `+${h.xpEarned}xp +${h.goldEarned}or${h.lootQuantity > 0 ? ` 🎁×${h.lootQuantity}` : ''}`
|
||||
: `${h.totalRounds} tours`
|
||||
}
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
import { MonsterCard } from '../components/MonsterCard';
|
||||
import { CombatLogView, MultiCombatView, HistoryEntry } from '../components/CombatViews';
|
||||
|
||||
export function CombatPage() {
|
||||
const qc = useQueryClient();
|
||||
|
||||
Reference in New Issue
Block a user