Spaces:
Paused
Paused
File size: 3,235 Bytes
17d6f8a 741bf09 17d6f8a 741bf09 17d6f8a 741bf09 17d6f8a 741bf09 17d6f8a 741bf09 17d6f8a 741bf09 17d6f8a 741bf09 17d6f8a 741bf09 17d6f8a | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 | import { useState } from 'react';
const EMPTY = { name: '', callsign: '', device_id: '', group_id: '' };
export default function MembersPanel({ members, groups, sosIds = new Set(), selected, onSelect, onSave, onDelete }) {
const [editing, setEditing] = useState(null); // null | {} (new) | member
const [form, setForm] = useState(EMPTY);
const [error, setError] = useState('');
const start = (m) => {
setEditing(m || {});
setForm({
name: m?.name || '',
callsign: m?.callsign || '',
device_id: m?.device_id || '',
group_id: m?.group_id ?? '',
});
};
const submit = async (e) => {
e.preventDefault();
setError('');
try {
await onSave(editing?.id ?? null, {
...form,
group_id: form.group_id === '' ? null : Number(form.group_id),
});
setEditing(null);
setForm(EMPTY);
} catch (err) {
setError(err.message);
}
};
const remove = async (m) => {
if (!confirm(`Remove ${m.name}?`)) return;
setError('');
try {
await onDelete(m.id);
} catch (err) {
setError(err.message);
}
};
return (
<section className="panel">
<h2>
Members ({members.length})
<button className="icon-btn" style={{ float: 'right' }} onClick={() => (editing ? (setEditing(null), setForm(EMPTY)) : start(null))}>
{editing ? '×' : '+'}
</button>
</h2>
{error && <p className="error">{error}</p>}
{editing && (
<form className="member-form" onSubmit={submit}>
<input placeholder="Name" required value={form.name} onChange={(e) => setForm({ ...form, name: e.target.value })} />
<div className="form-row">
<input placeholder="Callsign" value={form.callsign} onChange={(e) => setForm({ ...form, callsign: e.target.value })} />
<input placeholder="Device ID (unique)" value={form.device_id} onChange={(e) => setForm({ ...form, device_id: e.target.value })} disabled={!!editing.id} />
</div>
<select value={form.group_id ?? ''} onChange={(e) => setForm({ ...form, group_id: e.target.value })}>
<option value="">No group</option>
{groups.map((g) => <option key={g.id} value={g.id}>{g.name}</option>)}
</select>
<div className="form-row">
<button className="primary">{editing.id ? 'Save' : 'Add member'}</button>
{editing.id && <button type="button" className="danger" onClick={() => remove(editing)}>Delete</button>}
</div>
</form>
)}
{members.map((m) => (
<div key={m.id} className={`mrow ${selected === m.id ? 'selected' : ''}`} onClick={() => onSelect(m.id)}>
<span className={`st ${m.status === 'online' ? 'online' : ''} ${sosIds.has(m.id) ? 'sos' : ''}`} />
<span className="nm">
{m.name}
<span className="sub"> · {m.group_name || '—'}{m.callsign ? ` · ${m.callsign}` : ''}</span>
</span>
<span className="bat">{m.battery != null ? `${m.battery}%` : ''}</span>
<button className="icon-btn" onClick={(e) => { e.stopPropagation(); start(m); }}>✎</button>
</div>
))}
</section>
);
} |