import { useState, useEffect, type MouseEvent, type ChangeEvent } from 'react' import { useTranslation } from 'react-i18next' import type { Profile, WorkbenchTab } from './types' import { getAssetPath } from '../../utils/assetPath' import { getTranslatedText } from '../../utils/i18nUtils' import './Workbench.css' interface WorkbenchProps { showCraftingMenu: boolean showRepairMenu: boolean workbenchTab: WorkbenchTab craftableItems: any[] repairableItems: any[] uncraftableItems: any[] craftFilter: string repairFilter: string uncraftFilter: string craftCategoryFilter: string profile: Profile | null onCloseCrafting: () => void onSwitchTab: (tab: WorkbenchTab) => void onSetCraftFilter: (filter: string) => void onSetRepairFilter: (filter: string) => void onSetUncraftFilter: (filter: string) => void onSetCraftCategoryFilter: (category: string) => void onCraft: (itemId: number) => void onRepair: (uniqueItemId: string, inventoryId: number) => void onUncraft: (uniqueItemId: string, inventoryId: number) => void } function Workbench({ showCraftingMenu, showRepairMenu, workbenchTab, craftableItems, repairableItems, uncraftableItems, craftFilter, repairFilter, uncraftFilter, craftCategoryFilter, profile, onCloseCrafting, onSwitchTab, onSetCraftFilter, onSetRepairFilter, onSetUncraftFilter, onSetCraftCategoryFilter, onCraft, onRepair, onUncraft }: WorkbenchProps) { const { t } = useTranslation() const [selectedItem, setSelectedItem] = useState(null) // Reset selection when tab changes useEffect(() => { setSelectedItem(null) }, [workbenchTab]) // Update selectedItem when items list changes (after repair/craft/salvage) useEffect(() => { if (selectedItem) { const items = getItems() // Find the updated item by unique_item_id or inventory_id const updatedItem = items.find(item => { if (selectedItem.unique_item_id && item.unique_item_id) { return item.unique_item_id === selectedItem.unique_item_id } if (selectedItem.inventory_id && item.inventory_id) { return item.inventory_id === selectedItem.inventory_id } return item.item_id === selectedItem.item_id }) if (updatedItem) { setSelectedItem(updatedItem) } else { // Item no longer exists (e.g., was salvaged) setSelectedItem(null) } } }, [craftableItems, repairableItems, uncraftableItems]) if (!showCraftingMenu && !showRepairMenu) return null const getItems = () => { switch (workbenchTab) { case 'craft': return craftableItems.filter(item => getTranslatedText(item.name).toLowerCase().includes(craftFilter.toLowerCase()) && (craftCategoryFilter === 'all' || item.category === craftCategoryFilter) ) case 'repair': return repairableItems .filter(item => getTranslatedText(item.name).toLowerCase().includes(repairFilter.toLowerCase())) .sort((a, b) => { if (a.needs_repair && !b.needs_repair) return -1 if (!a.needs_repair && b.needs_repair) return 1 return 0 }) case 'uncraft': return uncraftableItems.filter(item => getTranslatedText(item.name).toLowerCase().includes(uncraftFilter.toLowerCase()) ) default: return [] } } const items = getItems() const renderItemDetails = () => { if (!selectedItem) { return (
🔧

{t('crafting.selectItem')}

{t('crafting.chooseFromList')}

) } const item = selectedItem const imagePath = getAssetPath(item.image_path || `images/items/${item.item_id || item.id}.webp`) return ( <>
{imagePath ? ( {getTranslatedText(item.name)} { (e.target as HTMLImageElement).style.display = 'none'; const icon = (e.target as HTMLImageElement).nextElementSibling; if (icon) icon.classList.remove('hidden'); }} /> ) : null}
{item.emoji || '📦'}

{item.emoji} {getTranslatedText(item.name)}

{item.description &&

{getTranslatedText(item.description)}

} {/* Base Stats Display for Crafting */} {workbenchTab === 'craft' && (item.base_stats || item.stats) && (
{Object.entries(item.base_stats || item.stats).map(([key, value]) => { const icons: Record = { weight_capacity: `⚖️ ${t('game.weight')}`, volume_capacity: `📦 ${t('game.volume')}`, armor: `🛡️ ${t('stats.armor')}`, hp_max: `❤️ ${t('stats.maxHp')}`, stamina_max: `⚡ ${t('stats.maxStamina')}`, damage_min: `⚔️ ${t('stats.damage')} Min`, damage_max: `⚔️ ${t('stats.damage')} Max` } const label = icons[key] || key.replace('_', ' ') const unit = key.includes('weight') ? 'kg' : key.includes('volume') ? 'L' : '' return (
{label}: +{Math.round(Number(value))}{unit}
) })}

* {t('crafting.potentialBaseStats')}

)} {/* Stats Display for Repair/Salvage */} {workbenchTab !== 'craft' && (item.unique_item_data?.unique_stats || item.unique_item_data || item.base_stats || item.stats) && (
{Object.entries(item.unique_item_data?.unique_stats ?? item.unique_item_data ?? item.base_stats ?? item.stats ?? {}).filter(([k]) => !['id', 'item_id', 'durability', 'max_durability', 'created_at', 'tier'].includes(k)).map(([key, value]) => { const icons: Record = { weight_capacity: `⚖️ ${t('game.weight')}`, volume_capacity: `📦 ${t('game.volume')}`, armor: `🛡️ ${t('stats.armor')}`, hp_max: `❤️ ${t('stats.maxHp')}`, stamina_max: `⚡ ${t('stats.maxStamina')}`, damage_min: `⚔️ ${t('stats.damage')} Min`, damage_max: `⚔️ ${t('stats.damage')} Max` } const label = icons[key] || key.replace('_', ' ') const unit = key.includes('weight') ? 'kg' : key.includes('volume') ? 'L' : '' return (
{label}: +{Math.round(Number(value))}{unit}
) })}
)}
{workbenchTab === 'craft' && ( <>

{t('crafting.requirements')}

{t('crafting.levelRequired', { level: item.craft_level })} {item.meets_level ? '✅' : `❌ (Lv. ${profile?.level || 1})`}
{item.tools && item.tools.length > 0 && ( <>
{t('crafting.tools')}
{item.tools.map((tool: any, i: number) => (
{tool.emoji} {getTranslatedText(tool.name)} {tool.has_tool ? `✅ ${tool.tool_durability}/${tool.max_durability} (${t('crafting.cost')}: ${tool.durability_cost})` : `❌ ${t('crafting.missing')} (${t('crafting.cost')}: ${tool.durability_cost})`}
))} )}
{t('crafting.materials')}
{item.materials && item.materials.length > 0 ? ( item.materials.map((mat: any, i: number) => (
{mat.emoji} {getTranslatedText(mat.name)} {mat.available} / {mat.required}
)) ) : (
{t('crafting.noMaterialsRequired')}
)}
)} {workbenchTab === 'repair' && ( <>

🔧 {workbenchTab === 'repair' ? t('game.repair') : t('game.salvage')}

{!item.needs_repair ? (

{t('crafting.perfectCondition')}

) : ( <>
Current: {item.durability_percent}% After Repair: {Math.min(100, item.durability_percent + (item.repair_percentage || 0))}%
{item.current_durability}/{item.max_durability} +{Math.round((item.max_durability || 0) * ((item.repair_percentage || 0) / 100))} durability
)} {item.needs_repair && ( <> {item.tools && item.tools.length > 0 && ( <>
Tools
{item.tools.map((tool: any, i: number) => (
{tool.emoji} {getTranslatedText(tool.name)} {tool.has_tool ? `✅ ${tool.tool_durability}/${tool.tool_max_durability} (Cost: ${tool.durability_cost})` : `❌ Missing (Cost: ${tool.durability_cost})`}
))} )}
Materials
{item.materials.map((mat: any, i: number) => (
{mat.emoji} {getTranslatedText(mat.name)} {mat.available} / {mat.quantity}
))} )}
)} {workbenchTab === 'uncraft' && ( <>

♻️ {t('game.salvage')}

{/* Show durability bar if we have durability data */} {(item.unique_item_data || item.durability_percent !== undefined) && (
Condition: {item.unique_item_data?.durability_percent || item.durability_percent || 0}%
)}
{(() => { const durabilityRatio = item.unique_item_data?.durability_percent !== undefined ? (item.unique_item_data.durability_percent || 0) / 100 : item.durability_percent !== undefined ? (item.durability_percent || 0) / 100 : 1.0 const adjustedYield = (item.uncraft_yield || item.base_yield || []).map((mat: any) => ({ ...mat, adjusted_quantity: Math.round((mat.quantity || 0) * durabilityRatio) })) return ( <> {durabilityRatio < 1.0 && (
{t('crafting.yieldReduced', { percent: Math.round((1 - durabilityRatio) * 100) })}
)} {item.loss_chance && (
⚠️ {Math.round(item.loss_chance * 100)}% chance to lose each material
)} {adjustedYield.map((mat: any, i: number) => (
{mat.emoji} {getTranslatedText(mat.name)} x{mat.adjusted_quantity}
))} ) })()}
)}
) } const categories = [ { id: 'all', label: t('categories.all'), icon: '🎒' }, { id: 'weapon', label: t('categories.weapon'), icon: '⚔️' }, { id: 'armor', label: t('categories.armor'), icon: '🛡️' }, { id: 'clothing', label: t('categories.clothing'), icon: '👕' }, { id: 'tool', label: t('categories.tool'), icon: '🛠️' }, { id: 'consumable', label: t('categories.consumable'), icon: '🍖' }, { id: 'resource', label: t('categories.resource'), icon: '📦' }, { id: 'misc', label: t('categories.misc'), icon: '📦' } ] return (
) => { if (e.target === e.currentTarget) onCloseCrafting() }}>

{t('game.workbench')}

{/* Column 1: Categories Sidebar */}

{t('location.lootableItems').replace(':', '')}

{categories.map(cat => ( ))}
{/* Column 2: Items List */}
🔍 ) => { if (workbenchTab === 'craft') onSetCraftFilter(e.target.value) else if (workbenchTab === 'repair') onSetRepairFilter(e.target.value) else onSetUncraftFilter(e.target.value) }} className="game-search-input" />
{items.filter(item => { // Text search filter const searchFilter = workbenchTab === 'craft' ? craftFilter : workbenchTab === 'repair' ? repairFilter : uncraftFilter const matchesSearch = !searchFilter || getTranslatedText(item.name).toLowerCase().includes(searchFilter.toLowerCase()) // Category filter (apply to all tabs) let matchesCategory = true if (craftCategoryFilter !== 'all') { // Assuming item has a 'type' property that matches category IDs matchesCategory = item.type === craftCategoryFilter } return matchesSearch && matchesCategory }).length === 0 ? (
{t('game.noItemsFound')}
) : ( items .filter(item => { // Text search filter const searchFilter = workbenchTab === 'craft' ? craftFilter : workbenchTab === 'repair' ? repairFilter : uncraftFilter const matchesSearch = !searchFilter || getTranslatedText(item.name).toLowerCase().includes(searchFilter.toLowerCase()) // Category filter (apply to all tabs) let matchesCategory = true if (craftCategoryFilter !== 'all') { // Assuming item has a 'type' property that matches category IDs matchesCategory = item.type === craftCategoryFilter } return matchesSearch && matchesCategory }) .map((item: any, idx: number) => { const imagePath = getAssetPath(item.image_path || `images/items/${item.item_id || item.id}.webp`) return (
setSelectedItem(item)} > {/* Item Image/Icon */}
{imagePath ? ( {getTranslatedText(item.name)} { (e.target as HTMLImageElement).style.display = 'none'; const icon = (e.target as HTMLImageElement).nextElementSibling; if (icon) icon.classList.remove('hidden'); }} /> ) : null}
{item.emoji || '📦'}
{getTranslatedText(item.name)} {item.location === 'equipped' && {t('game.equipped')}}
{/* Stats display for repair/salvage items */} {(workbenchTab === 'repair' || workbenchTab === 'uncraft') && (() => { const statsSource = item.unique_item_data?.unique_stats ?? item.unique_item_data ?? item.base_stats ?? item.stats ?? {}; const damage_min = statsSource.damage_min; const damage_max = statsSource.damage_max; const armor = statsSource.armor; return (damage_min || armor) ? (
{damage_min && ( ⚔️ {damage_min}-{damage_max} )} {armor && ( 🛡️ {armor} )}
) : null; })()} {/* Condition bar for Salvage tab */} {workbenchTab === 'uncraft' && item.durability_percent !== undefined && (
{(item.current_durability !== undefined && item.current_durability !== null) && ( 🔧 {item.current_durability}/{item.max_durability} )}
)} {/* Progress Bar for Repair tab */} {workbenchTab === 'repair' && item.durability_percent !== undefined && (
{(item.current_durability !== undefined && item.current_durability !== null) && ( 🔧 {item.current_durability}/{item.max_durability} )}
)}
) }) )}
{/* Column 3: Details */}
{renderItemDetails()}
) } export default Workbench