feat(web): implement weapons section with add/edit/detail modals and notes

Adds WeaponCard with inline ammo stepper and status dropdown, AddWeaponModal,
WeaponDetailModal with full editing, and debounced NotesSection.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Bas van Rossem
2026-02-19 16:26:47 +01:00
parent 88e9bf7f05
commit 1ef2f6338c
7 changed files with 499 additions and 2 deletions

View File

@@ -0,0 +1,100 @@
import { useState } from 'react';
import Modal from '../ui/Modal';
interface Props {
open: boolean;
onClose: () => void;
onCreate: (weapon: Record<string, unknown>) => void;
}
export default function AddWeaponModal({ open, onClose, onCreate }: Props) {
const [name, setName] = useState('');
const [damage, setDamage] = useState('');
const [attackMod, setAttackMod] = useState('');
const [range, setRange] = useState('');
const [ammoMax, setAmmoMax] = useState('');
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
if (!name.trim()) return;
const aMax = parseInt(ammoMax) || null;
onCreate({
name: name.trim(),
damage: damage.trim() || null,
attack_mod: parseInt(attackMod) || null,
range: range.trim() || null,
ammo_max: aMax,
ammo_current: aMax,
status: 'ok',
});
setName('');
setDamage('');
setAttackMod('');
setRange('');
setAmmoMax('');
onClose();
};
return (
<Modal open={open} onClose={onClose} title="Add Weapon">
<form onSubmit={handleSubmit} className="modal-form">
<label className="form-label">
Name *
<input
type="text"
value={name}
onChange={(e) => setName(e.target.value)}
placeholder="e.g. Ballista"
autoFocus
required
/>
</label>
<label className="form-label">
Damage
<input
type="text"
value={damage}
onChange={(e) => setDamage(e.target.value)}
placeholder="e.g. 3d10"
/>
</label>
<label className="form-label">
Attack Modifier
<input
type="number"
value={attackMod}
onChange={(e) => setAttackMod(e.target.value)}
/>
</label>
<label className="form-label">
Range
<input
type="text"
value={range}
onChange={(e) => setRange(e.target.value)}
placeholder="e.g. 120/480"
/>
</label>
<label className="form-label">
Ammo Max (leave empty for unlimited)
<input
type="number"
value={ammoMax}
onChange={(e) => setAmmoMax(e.target.value)}
min="0"
/>
</label>
<div className="modal-actions">
<button type="button" className="btn-secondary" onClick={onClose}>
Cancel
</button>
<button type="submit" className="btn-primary" disabled={!name.trim()}>
Add Weapon
</button>
</div>
</form>
</Modal>
);
}