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>
  );
}