/* global React, MZ, L */
const { useState, useEffect, useRef } = React;

const ROLE_PL = { admin: 'administrator', moderator: 'moderator', trusted: 'zaufany', member: 'użytkownik' };

function Account({ userId, handle, setRoute, profile: myProfile, onToast }) {
  const [u, setU] = useState(null);
  const [videos, setVideos] = useState([]);
  const [tab, setTab] = useState('videos');
  const [loading, setLoading] = useState(true);
  const isMe = myProfile?.id === (u?.id || userId);

  useEffect(() => {
    (async () => {
      setLoading(true);
      try {
        let prof;
        if (userId) prof = await MZ.getProfile(userId);
        else if (handle) {
          const { data } = await MZ.sb.from('profiles').select('*').eq('handle', handle).single();
          prof = data;
        }
        if (!prof) throw new Error('not found');
        const vids = await MZ.listUserVideos(prof.id);
        setU(prof);
        setVideos(vids);
        if (window.MZ_SEO) window.MZ_SEO.forAccount(prof.handle);
      } catch (e) { onToast('Nie znaleziono użytkownika'); }
      setLoading(false);
    })();
  }, [userId, handle]);

  if (loading || !u) return <div className="page"><p style={{padding:40, color:'var(--ink-2)'}}>Ładowanie profilu…</p></div>;

  const publishedVideos = videos.filter(v => v.status === 'published');
  const totalVotes = publishedVideos.reduce((s, v) => s + (v.up||0) - (v.down||0), 0);
  const totalViews = publishedVideos.reduce((s, v) => s + (v.views||0), 0);

  return (
    <div className="page">
      <div className="vp-breadcrumb">
        <a href="#" onClick={e => { e.preventDefault(); setRoute({name:'home'}); }}>START</a> / PROFIL / @{u.handle.toUpperCase()}
      </div>
      <div className="account-grid">
        <aside>
          <div className="profile-card">
            <div className="avatar avatar-lg" style={{width:80, height:80, fontSize:24}}>{initials(u.name || u.handle)}</div>
            <h3>{u.name || u.handle}</h3>
            <div className="handle">@{u.handle}</div>
            <div style={{marginTop:10}}>
              <span className="pill">{(ROLE_PL[u.role] || u.role).toUpperCase()}</span>
            </div>
            <div className="profile-stats">
              <div><div className="v">{publishedVideos.length}</div><div className="l">nagrań</div></div>
              <div><div className="v">{fmt(u.karma)}</div><div className="l">karma</div></div>
              <div><div className="v">{fmt(totalViews)}</div><div className="l">wyświetleń</div></div>
            </div>
          </div>

          <div style={{marginTop:16, padding:14, border:'1px solid var(--line)', borderRadius:6, background:'var(--bg-2)'}}>
            <div style={{fontFamily:'var(--mono)', fontSize:11, color:'var(--ink-3)', textTransform:'uppercase', letterSpacing:'0.06em', marginBottom: 8}}>Konto</div>
            <div style={{display:'flex', flexDirection:'column', gap:4, fontSize:12, color:'var(--ink-2)'}}>
              <span>Dołączył {fmtDate(u.joined)}</span>
              <span>{publishedVideos.length} opublikowanych</span>
              <span style={{color: totalVotes > 0 ? 'var(--up)' : totalVotes < 0 ? 'var(--down)' : 'var(--ink-2)'}}>{totalVotes > 0 ? '+' : ''}{fmt(totalVotes)} głosów netto</span>
            </div>
          </div>
        </aside>

        <div>
          <div className="profile-tabs">
            <button className={tab==='videos'?'active':''} onClick={() => setTab('videos')}>Nagrania ({publishedVideos.length})</button>
            {isMe && <button className={tab==='pending'?'active':''} onClick={() => setTab('pending')}>Oczekujące ({videos.filter(v=>v.status==='pending').length})</button>}
            {isMe && <button className={tab==='settings'?'active':''} onClick={() => setTab('settings')}>Ustawienia</button>}
          </div>

          {tab === 'videos' && (
            publishedVideos.length > 0 ? (
              <div className="video-grid" style={{gridTemplateColumns:'repeat(3, 1fr)'}}>
                {publishedVideos.map(v => <VideoCard key={v.id} video={{...v, author_profile: u}} onOpen={(id) => setRoute({name:'video', id})} />)}
              </div>
            ) : <p style={{color:'var(--ink-3)', padding:'40px 0'}}>Brak opublikowanych nagrań.</p>
          )}
          {tab === 'pending' && isMe && (
            <div>
              {videos.filter(v=>v.status!=='published').length === 0 ? <p style={{color:'var(--ink-3)', padding:'40px 0'}}>Brak oczekujących nagrań.</p> :
                videos.filter(v=>v.status!=='published').map(v => (
                  <div key={v.id} style={{padding:'14px 0', borderBottom:'1px solid var(--line)', display:'flex', justifyContent:'space-between', alignItems:'center'}}>
                    <div>
                      <div style={{fontSize:14, fontWeight:500}}>{v.title}</div>
                      <div style={{fontSize:12, color:'var(--ink-3)', marginTop:4}}>{v.city || '—'} · {timeAgo(v.created_at)}</div>
                    </div>
                    <span className={'pill ' + (v.status==='rejected'?'red':v.status==='removed'?'red':'amber')}>{v.status==='pending'?'moderacja':v.status==='rejected'?'odrzucone':'usunięte'}</span>
                  </div>
                ))
              }
            </div>
          )}
          {tab === 'settings' && isMe && <AccountSettings profile={u} setU={setU} onToast={onToast} />}
        </div>
      </div>
    </div>
  );
}

function AccountSettings({ profile, setU, onToast }) {
  const [name, setName] = useState(profile.name || '');
  const [busy, setBusy] = useState(false);
  const save = async (e) => {
    e.preventDefault();
    setBusy(true);
    try {
      await MZ.updateProfile({ name });
      setU({ ...profile, name });
      onToast('Profil zapisany');
    } catch (er) { onToast(er.message); }
    setBusy(false);
  };
  const logoutAll = async () => {
    if (!confirm('Wylogować ze wszystkich urządzeń?')) return;
    await MZ.signOut();
    location.reload();
  };
  return (
    <form onSubmit={save} style={{maxWidth: 500}}>
      <div className="field"><label>Imię (publiczne)</label><input value={name} onChange={e => setName(e.target.value)} /></div>
      <div className="field"><label>Nazwa użytkownika</label><input value={profile.handle} disabled /><div className="hint">NAZWY NIE MOŻNA ZMIENIĆ</div></div>
      <div style={{display:'flex', gap:8, marginTop:8}}>
        <button type="submit" className="btn btn-primary" disabled={busy}>{busy?'…':'Zapisz zmiany'}</button>
        <button type="button" className="btn" onClick={logoutAll}>Wyloguj</button>
      </div>
      <div style={{marginTop:24, padding:14, background:'var(--bg-2)', border:'1px solid var(--line)', borderRadius:6, fontSize:12, color:'var(--ink-2)'}}>
        <b style={{color:'var(--ink)'}}>Twoje dane</b>
        <p style={{marginTop:6}}>Zgodnie z RODO masz prawo do dostępu, sprostowania, usunięcia i przeniesienia swoich danych. Skontaktuj się z nami: kontakt@miejscezdarzenia.pl</p>
      </div>
    </form>
  );
}

function Admin({ setRoute, onToast }) {
  const [section, setSection] = useState('overview');
  const [counts, setCounts] = useState({ videos: 0, queue: 0, reports: 0, users: 0 });
  useEffect(() => { MZ.stats().then(setCounts); }, [section]);

  const sections = [
    { id:'overview', label:'Przegląd' },
    { id:'queue', label:'Kolejka moderacji', count: counts.queue },
    { id:'videos', label:'Wszystkie nagrania', count: counts.videos },
    { id:'reports', label:'Zgłoszenia', count: counts.reports },
    { id:'users', label:'Użytkownicy', count: counts.users },
  ];

  return (
    <div className="page">
      <div className="admin-layout">
        <nav className="admin-nav">
          <div className="admin-nav-group">Moderacja</div>
          {sections.map(s => (
            <a key={s.id} href="#" className={section===s.id?'active':''} onClick={e => { e.preventDefault(); setSection(s.id); }}>
              <span>{s.label}</span>
              {s.count != null && <span className="count">{s.count}</span>}
            </a>
          ))}
        </nav>

        <div>
          {section === 'overview' && <AdminOverview counts={counts} />}
          {section === 'queue' && <AdminQueue onToast={onToast} />}
          {section === 'videos' && <AdminVideos setRoute={setRoute} onToast={onToast} />}
          {section === 'reports' && <AdminReports onToast={onToast} />}
          {section === 'users' && <AdminUsers setRoute={setRoute} onToast={onToast} />}
        </div>
      </div>
    </div>
  );
}

function AdminOverview({ counts }) {
  return (
    <div>
      <div className="admin-head">
        <div><div className="kicker">Admin</div><h1>Przegląd</h1></div>
      </div>
      <div className="stat-grid">
        <div className="stat-card"><div className="lbl">Opublikowane</div><div className="val">{counts.videos}</div></div>
        <div className="stat-card"><div className="lbl">W kolejce</div><div className="val">{counts.queue}</div></div>
        <div className="stat-card"><div className="lbl">Otwarte zgłoszenia</div><div className="val">{counts.reports}</div></div>
        <div className="stat-card"><div className="lbl">Użytkownicy</div><div className="val">{counts.users}</div></div>
      </div>
    </div>
  );
}

function AdminQueue({ onToast }) {
  const [items, setItems] = useState([]);
  const [loading, setLoading] = useState(true);
  const load = async () => { setLoading(true); setItems(await MZ.adminListQueue()); setLoading(false); };
  useEffect(() => { load(); }, []);
  const act = async (id, status) => {
    try { await MZ.adminSetStatus(id, status); onToast(status==='published'?'Zatwierdzone':'Odrzucone'); load(); } catch(e){ onToast(e.message); }
  };
  return (
    <div>
      <div className="admin-head"><div><div className="kicker">Moderacja</div><h1>Kolejka ({items.length})</h1></div></div>
      <div className="table-wrap">
        <table className="admin-table">
          <thead><tr><th>Tytuł</th><th>Autor</th><th>Miasto</th><th>Dodano</th><th>YT</th><th style={{textAlign:'right'}}>Akcje</th></tr></thead>
          <tbody>
            {loading ? <tr><td colSpan="6">Ładowanie…</td></tr> : items.length === 0 ? <tr><td colSpan="6" style={{color:'var(--ink-3)'}}>Kolejka pusta.</td></tr> :
              items.map(q => (
                <tr key={q.id}>
                  <td className="title-cell">{q.title}</td>
                  <td className="mono">@{q.author_profile?.handle || '—'}</td>
                  <td>{q.city || '—'}</td>
                  <td className="mono">{fmtDate(q.created_at)}</td>
                  <td className="mono">{q.youtube_id}</td>
                  <td><div className="row-actions">
                    <a href={`https://youtube.com/watch?v=${q.youtube_id}`} target="_blank" rel="noopener"><button>Podgląd</button></a>
                    <button onClick={() => act(q.id, 'published')}>Zatwierdź</button>
                    <button className="danger" onClick={() => act(q.id, 'rejected')}>Odrzuć</button>
                  </div></td>
                </tr>
              ))
            }
          </tbody>
        </table>
      </div>
    </div>
  );
}

function AdminVideos({ setRoute, onToast }) {
  const [items, setItems] = useState([]);
  const [sev, setSev] = useState('all');
  const [editing, setEditing] = useState(null);
  const [statusFilter, setStatusFilter] = useState('published');
  const load = async () => setItems(await MZ.listVideos({ status: statusFilter, limit:500 }));
  useEffect(() => { load(); }, [statusFilter]);
  const list = sev === 'all' ? items : items.filter(v => v.severity === sev);
  const remove = async (id) => {
    if (!confirm('Usunąć nagranie?')) return;
    try { await MZ.adminSetStatus(id, 'removed'); onToast('Usunięte'); load(); } catch(e){ onToast(e.message); }
  };
  const restore = async (id) => {
    try { await MZ.adminSetStatus(id, 'published'); onToast('Przywrócone'); load(); } catch(e){ onToast(e.message); }
  };
  return (
    <div>
      <div className="admin-head"><div><div className="kicker">Treści</div><h1>Wszystkie nagrania ({items.length})</h1></div></div>
      <div className="table-wrap">
        <div className="table-toolbar">
          <div className="table-filters">
            {['published','pending','rejected','removed'].map(s => (
              <button key={s} className={statusFilter===s?'active':''} onClick={() => setStatusFilter(s)}>{({published:'OPUBLIKOWANE', pending:'W KOLEJCE', rejected:'ODRZUCONE', removed:'USUNIĘTE'})[s]}</button>
            ))}
          </div>
          <div className="table-filters">
            {['all','collision','near-miss','wildlife','weather','other'].map(s => (
              <button key={s} className={sev===s?'active':''} onClick={() => setSev(s)}>{(SEV_PL[s]||s).toUpperCase()}</button>
            ))}
          </div>
        </div>
        <table className="admin-table">
          <thead><tr><th>Tytuł</th><th>Autor</th><th>Lokalizacja</th><th>Rodzaj</th><th>Głosy</th><th>Wyśw.</th><th style={{textAlign:'right'}}>Akcje</th></tr></thead>
          <tbody>
            {list.map(v => (
              <tr key={v.id}>
                <td className="title-cell"><a href="#" style={{borderBottom:'1px solid var(--line-2)'}} onClick={e => { e.preventDefault(); setRoute({name:'video', id:v.id}); }}>{v.title}</a></td>
                <td className="mono">@{v.author_profile?.handle || '—'}</td>
                <td>{v.city || '—'}</td>
                <td><span className="pill" style={{textTransform:'uppercase'}}>{SEV_PL[v.severity] || v.severity}</span></td>
                <td className="mono">+{fmt((v.up||0) - (v.down||0))}</td>
                <td className="mono">{fmt(v.views)}</td>
                <td><div className="row-actions">
                  <button onClick={() => setEditing(v)}>Edytuj</button>
                  {statusFilter === 'published' && <button className="danger" onClick={() => remove(v.id)}>Usuń</button>}
                  {statusFilter !== 'published' && <button onClick={() => restore(v.id)}>Przywróć</button>}
                </div></td>
              </tr>
            ))}
          </tbody>
        </table>
      </div>
      {editing && <EditVideoModal video={editing} onClose={() => setEditing(null)} onSaved={() => { setEditing(null); load(); onToast('Zapisane'); }} onToast={onToast} />}
    </div>
  );
}

function EditMapPicker({ lat, lng, onChange }) {
  const ref = useRef(null);
  const mapRef = useRef(null);
  const markerRef = useRef(null);

  useEffect(() => {
    if (!ref.current || mapRef.current) return;
    const startLat = parseFloat(lat) || 52.07;
    const startLng = parseFloat(lng) || 19.48;
    const map = L.map(ref.current, { zoomControl: true }).setView([startLat, startLng], lat ? 13 : 6);
    L.tileLayer('https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png', {
      attribution: '© OpenStreetMap, © CARTO',
      maxZoom: 19,
    }).addTo(map);
    const marker = L.marker([startLat, startLng], { draggable: true }).addTo(map);
    marker.on('dragend', () => {
      const p = marker.getLatLng();
      onChange(p.lat.toFixed(6), p.lng.toFixed(6));
    });
    map.on('click', (e) => {
      marker.setLatLng(e.latlng);
      onChange(e.latlng.lat.toFixed(6), e.latlng.lng.toFixed(6));
    });
    mapRef.current = map;
    markerRef.current = marker;
    setTimeout(() => map.invalidateSize(), 100);
    return () => { map.remove(); mapRef.current = null; markerRef.current = null; };
  }, []);

  // sync external coord changes (typed into number inputs) → marker
  useEffect(() => {
    if (!markerRef.current) return;
    const la = parseFloat(lat), ln = parseFloat(lng);
    if (!isNaN(la) && !isNaN(ln)) {
      const cur = markerRef.current.getLatLng();
      if (Math.abs(cur.lat - la) > 1e-5 || Math.abs(cur.lng - ln) > 1e-5) {
        markerRef.current.setLatLng([la, ln]);
      }
    }
  }, [lat, lng]);

  return <div ref={ref} style={{ height: 280, borderRadius: 4, border: '1px solid var(--line)', overflow: 'hidden' }} />;
}

function EditVideoModal({ video, onClose, onSaved, onToast }) {
  const [d, setD] = useState({
    title: video.title || '',
    severity: video.severity || 'other',
    city: video.city || '',
    description: video.description || '',
    tags: (video.tags || []).join(', '),
    duration: video.duration || '',
    start_at: video.start_at != null ? String(video.start_at) : '',
    end_at: video.end_at != null ? String(video.end_at) : '',
    youtube_id: video.youtube_id || '',
    lat: video.lat, lng: video.lng,
    recorded_on: video.recorded_on || '',
    status: video.status || 'published',
  });
  const [busy, setBusy] = useState(false);
  const [err, setErr] = useState(null);
  const set = (k, v) => setD(o => ({ ...o, [k]: v }));
  const save = async (e) => {
    e.preventDefault();
    setBusy(true); setErr(null);
    try {
      const payload = {
        title: d.title.trim(),
        severity: d.severity,
        city: d.city.trim() || null,
        description: d.description.trim() || null,
        tags: d.tags.split(',').map(s => s.trim()).filter(Boolean),
        duration: d.duration.trim() || null,
        start_at: MZ.parseTime(d.start_at),
        end_at: MZ.parseTime(d.end_at),
        youtube_id: d.youtube_id.trim(),
        lat: parseFloat(d.lat), lng: parseFloat(d.lng),
        recorded_on: d.recorded_on || null,
        status: d.status,
      };
      await MZ.adminUpdateVideo(video.id, payload);
      onSaved?.();
    } catch (er) {
      setErr(er.message || 'Błąd zapisu');
    }
    setBusy(false);
  };
  // Track where mousedown started so dragging a text-selection over the
  // backdrop does NOT close the modal (only intentional clicks do).
  const downOnBackdrop = useRef(false);
  return (
    <div
      className="modal-backdrop"
      onMouseDown={e => { downOnBackdrop.current = (e.target === e.currentTarget); }}
      onClick={e => { if (e.target === e.currentTarget && downOnBackdrop.current) onClose(); }}
    >
      <form className="modal" onMouseDown={e => e.stopPropagation()} onClick={e => e.stopPropagation()} onSubmit={save} style={{maxWidth: 640}}>
        <div className="modal-head">
          <h2>Edycja nagrania</h2>
          <button type="button" className="close-x" onClick={onClose}>×</button>
        </div>
        <div className="modal-body">
          <div className="field">
            <label>Tytuł</label>
            <input value={d.title} onChange={e => set('title', e.target.value)} required />
          </div>
          <div className="field">
            <label>YouTube ID</label>
            <input value={d.youtube_id} onChange={e => set('youtube_id', e.target.value)} placeholder="np. dQw4w9WgXcQ" />
          </div>
          <div style={{display:'grid', gridTemplateColumns:'1fr 1fr', gap:12}}>
            <div className="field">
              <label>Rodzaj</label>
              <select value={d.severity} onChange={e => set('severity', e.target.value)}>
                <option value="collision">Kolizja</option>
                <option value="near-miss">Prawie-kolizja</option>
                <option value="wildlife">Zwierzęta</option>
                <option value="weather">Pogoda</option>
                <option value="other">Inne</option>
              </select>
            </div>
            <div className="field">
              <label>Status</label>
              <select value={d.status} onChange={e => set('status', e.target.value)}>
                <option value="published">opublikowane</option>
                <option value="pending">w kolejce</option>
                <option value="rejected">odrzucone</option>
                <option value="removed">usunięte</option>
              </select>
            </div>
          </div>
          <div className="field">
            <label>Miasto / lokalizacja</label>
            <input value={d.city} onChange={e => set('city', e.target.value)} />
          </div>
          <div className="field">
            <label>Lokalizacja na mapie (przeciągnij znacznik lub kliknij)</label>
            <EditMapPicker lat={d.lat} lng={d.lng} onChange={(lat, lng) => setD(o => ({ ...o, lat, lng }))} />
          </div>
          <div style={{display:'grid', gridTemplateColumns:'1fr 1fr', gap:12}}>
            <div className="field"><label>Szerokość (lat)</label><input type="number" step="0.000001" value={d.lat} onChange={e => set('lat', e.target.value)} /></div>
            <div className="field"><label>Długość (lng)</label><input type="number" step="0.000001" value={d.lng} onChange={e => set('lng', e.target.value)} /></div>
          </div>
          <div className="field">
            <label>Tagi (przecinki)</label>
            <input value={d.tags} onChange={e => set('tags', e.target.value)} />
          </div>
          <div style={{display:'grid', gridTemplateColumns:'1fr 1fr', gap:12}}>
            <div className="field"><label>Długość nagrania</label><input value={d.duration} onChange={e => set('duration', e.target.value)} placeholder="1:24" /></div>
            <div className="field"><label>Data nagrania</label><input type="date" value={d.recorded_on} onChange={e => set('recorded_on', e.target.value)} /></div>
          </div>
          <div style={{display:'grid', gridTemplateColumns:'1fr 1fr', gap:12}}>
            <div className="field"><label>Start od (sek. lub M:SS)</label><input value={d.start_at} onChange={e => set('start_at', e.target.value)} placeholder="np. 26 lub 0:26" /></div>
            <div className="field"><label>Koniec na (sek. lub M:SS)</label><input value={d.end_at} onChange={e => set('end_at', e.target.value)} placeholder="np. 1:12" /></div>
          </div>
          <div className="field">
            <label>Opis</label>
            <textarea rows="4" value={d.description} onChange={e => set('description', e.target.value)} />
          </div>
          {err && <div style={{padding:'10px 12px', background:'oklch(0.95 0.05 25)', color:'oklch(0.4 0.15 25)', borderRadius:4, fontSize:12, marginBottom:8}}>{err}</div>}
        </div>
        <div className="modal-foot">
          <button type="button" className="btn" onClick={onClose}>Anuluj</button>
          <button type="submit" className="btn btn-primary" disabled={busy}>{busy ? 'Zapisywanie…' : 'Zapisz zmiany'}</button>
        </div>
      </form>
    </div>
  );
}

function AdminReports({ onToast }) {
  const [items, setItems] = useState([]);
  const load = async () => setItems(await MZ.adminListReports());
  useEffect(() => { load(); }, []);
  const act = async (id, patch) => {
    try { await MZ.adminUpdateReport(id, patch); onToast('Zaktualizowane'); load(); } catch(e) { onToast(e.message); }
  };
  return (
    <div>
      <div className="admin-head"><div><div className="kicker">Bezpieczeństwo</div><h1>Zgłoszenia ({items.length})</h1></div></div>
      <div className="table-wrap">
        <table className="admin-table">
          <thead><tr><th>Nagranie</th><th>Zgłaszający</th><th>Powód</th><th>Otwarte</th><th>Status</th><th style={{textAlign:'right'}}>Akcje</th></tr></thead>
          <tbody>
            {items.length === 0 ? <tr><td colSpan="6" style={{color:'var(--ink-3)'}}>Brak zgłoszeń.</td></tr> :
              items.map(r => (
                <tr key={r.id}>
                  <td className="title-cell">{r.video?.title || '—'}</td>
                  <td className="mono">@{r.reporter_profile?.handle || '—'}</td>
                  <td>{r.reason}{r.details ? ` — ${r.details}` : ''}</td>
                  <td className="mono">{fmtDate(r.created_at)}</td>
                  <td><span className={'pill ' + (r.status==='open'?'red':'amber')}>{r.status}</span></td>
                  <td><div className="row-actions">
                    <button onClick={() => act(r.id, {status:'dismissed'})}>Odrzuć</button>
                    <button className="danger" onClick={async () => { if (r.video?.id && confirm('Usunąć nagranie?')) { await MZ.adminSetStatus(r.video.id, 'removed'); act(r.id, {status:'actioned'}); } }}>Usuń nagranie</button>
                  </div></td>
                </tr>
              ))
            }
          </tbody>
        </table>
      </div>
    </div>
  );
}

function AdminUsers({ setRoute, onToast }) {
  const [items, setItems] = useState([]);
  const load = async () => setItems(await MZ.adminListUsers());
  useEffect(() => { load(); }, []);
  const setRole = async (id, role) => {
    try { await MZ.adminUpdateUserRole(id, role); onToast('Rola zmieniona'); load(); } catch(e) { onToast(e.message); }
  };
  return (
    <div>
      <div className="admin-head"><div><div className="kicker">Osoby</div><h1>Użytkownicy ({items.length})</h1></div></div>
      <div className="table-wrap">
        <table className="admin-table">
          <thead><tr><th>Nick</th><th>Imię</th><th>Rola</th><th>Dołączył</th><th>Karma</th><th style={{textAlign:'right'}}>Akcje</th></tr></thead>
          <tbody>
            {items.map(u => (
              <tr key={u.id}>
                <td><a href="#" onClick={e => { e.preventDefault(); setRoute({name:'account', id:u.id}); }} style={{borderBottom:'1px solid var(--line-2)'}} className="mono">@{u.handle}</a></td>
                <td className="title-cell">{u.name || '—'}</td>
                <td>
                  <select value={u.role} onChange={e => setRole(u.id, e.target.value)} style={{fontSize:11, padding:'2px 6px'}}>
                    <option value="member">użytkownik</option>
                    <option value="trusted">zaufany</option>
                    <option value="moderator">moderator</option>
                    <option value="admin">administrator</option>
                  </select>
                </td>
                <td className="mono">{fmtDate(u.joined)}</td>
                <td className="mono">{fmt(u.karma)}</td>
                <td></td>
              </tr>
            ))}
          </tbody>
        </table>
      </div>
    </div>
  );
}

Object.assign(window, { Account, Admin });
