diff --git a/src/App.js b/src/App.js index 7af9204..bd75e94 100755 --- a/src/App.js +++ b/src/App.js @@ -5,6 +5,7 @@ import RequisitionShop from './components/RequisitionShop'; import PlayerTab from './components/PlayerTab'; import RulesTab from './components/RulesTab'; import BestiaryTab from './components/BestiaryTab'; +import WeaponsTab from './components/WeaponsTab'; import GMKit from './components/GMKit'; import PlayerManagement from './components/PlayerManagement'; import { useState, useEffect } from 'react'; @@ -283,12 +284,18 @@ function App() { > Character Sheet - + {authedPlayer === 'gm' && ( <> + +
+ {fields.filter(([, v]) => v && v !== '-' && v !== '—').map(([label, value]) => ( +
+
{label}
+
{value}
+
+ ))} +
+ + + ) +} + +const CATS = ['All', 'Ranged Weapon', 'Melee Weapon', 'Grenade', 'Armour', 'Other'] + +export default function WeaponsTab() { + const [weapons, setWeapons] = useState([]) + const [loading, setLoading] = useState(true) + const [q, setQ] = useState('') + const [cat, setCat] = useState('All') + const [selected, setSelected] = useState(null) + const [error, setError] = useState(null) + + useEffect(() => { + fetch('/api/weapons') + .then(r => r.json()) + .then(data => { setWeapons(data); setLoading(false) }) + .catch(e => { setError('Failed to load weapons'); setLoading(false) }) + }, []) + + const filtered = useMemo(() => { + let list = weapons + if (cat !== 'All') list = list.filter(w => w.category === cat) + if (q.trim()) { + const term = q.toLowerCase() + list = list.filter(w => + w.name.toLowerCase().includes(term) || + (w.stats?.damage || '').toLowerCase().includes(term) || + (w.stats?.special || '').toLowerCase().includes(term) || + (w.source || '').toLowerCase().includes(term) + ) + } + return list + }, [weapons, cat, q]) + + return ( +
+
+
+

Weapons

+

{weapons.length} weapons from all Deathwatch sourcebooks

+
+ + {/* Filters */} +
+ setQ(e.target.value)} + placeholder="Search weapons..." + className="px-4 py-2 rounded-lg bg-slate-800 border border-slate-600 text-white placeholder-slate-400 focus:outline-none focus:ring-2 focus:ring-blue-500 w-64" + /> +
+ {CATS.map(c => ( + + ))} +
+
+ + {loading &&
Loading weapons...
} + {error &&
{error}
} + + {!loading && !error && ( +
+ + + + + + + + + + + + + + + + + {filtered.map(w => ( + + ))} + +
NameClassRangeRoFClipRldDamagePenSpecialSource
+ {filtered.length === 0 && ( +
No weapons match your filter.
+ )} +
+ )} + +
+ Showing {filtered.length} of {weapons.length} weapons · Click a row for details +
+
+ + {selected && setSelected(null)} />} +
+ ) +}