import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import { useNavigate } from 'react-router-dom'; import toast from 'react-hot-toast'; import { npcApi, characterApi } from '../api/endpoints'; import type { NpcView } from '../api/types'; import { Landmark, Heart, Zap, ArrowRight } from 'lucide-react'; import { REST_COST } from '../constants'; // ── Constants ── const ROLE_EMOJI: Record = { mentor: '🧙', companion: '💚', merchant: '🛍️', quest_giver: '📜', sage: '🔮', rival: '⚔️', }; const ROLE_LABELS: Record = { mentor: 'Mentor', companion: 'Compagnon', merchant: 'Marchand', quest_giver: 'Quêtes', sage: 'Sage', rival: 'Rival', }; const ROLE_COLORS: Record = { mentor: '#f4c94e', companion: '#3ddc84', merchant: '#5ba4f5', quest_giver: '#a78bfa', sage: '#a78bfa', rival: '#e84040', }; const ACTION_LABELS: Record = { heal: { label: 'Soins', emoji: '🩹' }, open_quests: { label: 'Voir les quêtes', emoji: '📜' }, open_shop: { label: 'Voir la boutique', emoji: '🛍️' }, open_forge: { label: 'Voir la forge', emoji: '🔨' }, challenge: { label: 'Défier', emoji: '⚔️' }, }; const ACTION_ROUTES: Record = { open_quests: '/quests', open_shop: '/shop', open_forge: '/forge', challenge: '/combat', }; const VILLAGE_LOCATIONS: Record = { village_plaza: { name: 'Place du Village', emoji: '🌸', atmosphere: 'Les nénuphars flottent doucement sous la lumière filtrée. Un air familier résonne.', }, village_arena: { name: 'Arène', emoji: '🏟️', atmosphere: 'L\'écho des combats passés résonne sur les pierres mouillées.', }, village_quests: { name: 'Source aux Quêtes', emoji: '📜', atmosphere: 'Le murmure du savoir coule comme un ruisseau entre les rochers moussus.', }, village_forge: { name: 'La Forge', emoji: '🔨', atmosphere: 'Des étincelles dansent et le métal chante sous les coups du marteau.', }, village_shop: { name: 'L\'Échoppe', emoji: '🏪', atmosphere: 'Des marchandises exotiques s\'étalent sur des feuilles de nénuphar géantes.', }, }; const LOCATION_ORDER = ['village_plaza', 'village_arena', 'village_quests', 'village_forge', 'village_shop']; // ── Components ── function NpcCard({ npc, character }: { npc: NpcView; character: any }) { const navigate = useNavigate(); const qc = useQueryClient(); const roleColor = ROLE_COLORS[npc.role] ?? '#6b7a99'; const roleEmoji = ROLE_EMOJI[npc.role] ?? '🐸'; const roleLabel = ROLE_LABELS[npc.role] ?? npc.role; const healMut = useMutation({ mutationFn: () => characterApi.rest(), onSuccess: (data) => { toast.success(`${npc.name} vous soigne ! +${data.healed} PV`); qc.invalidateQueries({ queryKey: ['character'] }); }, onError: (err: Error) => toast.error(err.message), }); const endurance = character?.enduranceCurrent ?? 0; const needsHeal = character && character.hpCurrent < character.hpMax; const canHeal = needsHeal && endurance >= REST_COST; const handleAction = () => { if (!npc.action) return; if (npc.action === 'heal') { healMut.mutate(); return; } const route = ACTION_ROUTES[npc.action]; if (route) navigate(route); }; const actionInfo = npc.action ? ACTION_LABELS[npc.action] : null; return (
{/* Header */}
{roleEmoji}
{npc.name} {roleLabel}
{npc.lore && (

{npc.lore}

)}
{/* Dialogue bubble */}
« {npc.dialogue} »
{/* Action */} {actionInfo && (
{npc.action === 'heal' ? (
{!needsHeal && (

Vos PV sont au maximum !

)} {needsHeal && !canHeal && (

Endurance insuffisante

)}
) : ( )}
)}
); } function VillageLocation({ locationKey, npcs, character }: { locationKey: string; npcs: NpcView[]; character: any; }) { const loc = VILLAGE_LOCATIONS[locationKey]; if (!loc || npcs.length === 0) return null; return (
{/* Location header */}

{loc.emoji} {loc.name}

{loc.atmosphere}

{/* NPC cards */}
{npcs.map((npc) => ( ))}
); } export function VillagePage() { const { data: npcs, isLoading } = useQuery({ queryKey: ['npcs'], queryFn: npcApi.all, }); const { data: character } = useQuery({ queryKey: ['character'], queryFn: characterApi.me, }); if (isLoading) return
Chargement du village…
; // Group NPCs by location const byLocation = new Map(); for (const npc of (npcs ?? [])) { const list = byLocation.get(npc.location) ?? []; list.push(npc); byLocation.set(npc.location, list); } return (
{/* Village banner */}

Le Village

L'étang murmure doucement. Les grenouilles vaquent à leurs occupations. Un lieu de repos, de rencontres et de préparation avant la prochaine aventure.

{/* Location sections */} {LOCATION_ORDER.map((locKey) => ( ))} {/* Empty state */} {(!npcs || npcs.length === 0) && (
Le village semble désert… Revenez plus tard.
)}
); }