/* global React, Icon, Button, Badge, Avatar, Card, StatusDot, BarChart, LineChart, ProgressBar, Modal, SectionTitle, Segmented, selectStyle, STATUS_META, DATA */
// ============================================================
// Admin — Team / Roles / iPhones / Tasks / Analytics
// ============================================================
const { useState } = React;

// store flotte (persiste devices + groupes à travers la navigation)
const FleetStore = (function () {
  let devices = DATA.DEVICES.map(d => ({ ...d }));
  let pools = [...new Set(DATA.DEVICES.map(d => d.pool))];
  const ls = new Set();
  const emit = () => ls.forEach(f => f());
  return {
    sub(cb) { ls.add(cb); return () => ls.delete(cb); },
    get devices() { return devices; },
    get pools() { return pools; },
    addDevice(d) { devices = [d, ...devices]; if (d.pool && !pools.includes(d.pool)) pools = [...pools, d.pool]; emit(); },
    addPool(p) { if (p && !pools.includes(p)) { pools = [...pools, p]; emit(); } },
    assign(opId, ids) { const op = DATA.opById(opId); devices = devices.map(d => ids.includes(d.id) ? { ...d, operator: op } : d); emit(); },
  };
})();
function useFleet() { const [, f] = React.useReducer(x => x + 1, 0); React.useEffect(() => FleetStore.sub(f), []); return FleetStore; }

// store des permissions par rôle (persiste + réactif → pilote l'accès aux sections)
const RoleStore = (function () {
  const map = {}; DATA.ROLES.forEach(r => map[r.name] = { ...r.perms });
  try { const s = JSON.parse(localStorage.getItem("tampro.roleperms") || "null"); if (s) Object.keys(s).forEach(k => { if (map[k]) Object.assign(map[k], s[k]); }); } catch (e) {}
  const ls = new Set();
  return {
    sub(cb) { ls.add(cb); return () => ls.delete(cb); },
    can(roleName, key) { return !!(map[roleName] && map[roleName][key]); },
    all(roleName) { return map[roleName] || {}; },
    set(roleName, key, val) { if (map[roleName]) { map[roleName][key] = val; try { localStorage.setItem("tampro.roleperms", JSON.stringify(map)); } catch (e) {} ls.forEach(f => f()); } },
  };
})();
function useRoles() { const [, f] = React.useReducer(x => x + 1, 0); React.useEffect(() => RoleStore.sub(f), []); return RoleStore; }

function PageHead({ title, sub, children }) {
  return (
    <div style={{ display: "flex", justifyContent: "space-between", alignItems: "flex-end", flexWrap: "wrap", gap: 14, marginBottom: 22 }}>
      <SectionTitle sub={sub}>{title}</SectionTitle>
      <div style={{ display: "flex", gap: 10 }}>{children}</div>
    </div>
  );
}

function Toolbar({ q, setQ, placeholder, children }) {
  return (
    <div style={{ display: "flex", gap: 10, marginBottom: 16, flexWrap: "wrap" }}>
      <div style={{ position: "relative", flex: 1, minWidth: 200 }}>
        <span style={{ position: "absolute", left: 12, top: "50%", transform: "translateY(-50%)", color: "var(--faint)", display: "grid" }}>{React.createElement(Icon.search, { size: 16 })}</span>
        <input value={q} onChange={e => setQ(e.target.value)} placeholder={placeholder} style={{ width: "100%", padding: "9px 12px 9px 36px", background: "var(--surface)", border: "1px solid var(--border)", borderRadius: 9, color: "var(--text)", fontSize: 13, outline: "none" }} />
      </div>
      {children}
    </div>
  );
}

// table primitives
const Th = ({ children, style }) => <th style={{ textAlign: "left", padding: "11px 14px", fontSize: 11, fontWeight: 700, letterSpacing: ".05em", textTransform: "uppercase", color: "var(--faint)", whiteSpace: "nowrap", ...style }}>{children}</th>;
const Td = ({ children, style }) => <td style={{ padding: "13px 14px", fontSize: 13, borderTop: "1px solid var(--border)", verticalAlign: "middle", ...style }}>{children}</td>;
function Table({ head, children }) {
  return (
    <div style={{ background: "var(--surface)", border: "1px solid var(--border)", borderRadius: "var(--r-lg)", overflow: "hidden" }}>
      <div style={{ overflowX: "auto" }}>
        <table style={{ width: "100%", minWidth: 640, borderCollapse: "collapse" }}>
          <thead style={{ background: "var(--surface-2)" }}><tr>{head}</tr></thead>
          <tbody>{children}</tbody>
        </table>
      </div>
    </div>
  );
}
function RowActions({ items }) {
  const { useState, useRef } = React;
  const [open, setOpen] = useState(false);
  const [pos, setPos] = useState({ top: 0, left: 0 });
  const btnRef = useRef(null);
  const W = 190;
  const toggle = () => {
    if (!open && btnRef.current) {
      const r = btnRef.current.getBoundingClientRect();
      setPos({ top: r.bottom + 6, left: Math.max(8, Math.min(r.right - W, window.innerWidth - W - 8)) });
    }
    setOpen(o => !o);
  };
  return (
    <div style={{ textAlign: "right" }}>
      <button ref={btnRef} onClick={toggle} style={{ background: "var(--surface-2)", border: "1px solid var(--border)", borderRadius: 8, width: 30, height: 30, display: "grid", placeItems: "center", color: "var(--muted)" }}>{React.createElement(Icon.dots, { size: 16 })}</button>
      {open && (<>
        <div onClick={() => setOpen(false)} style={{ position: "fixed", inset: 0, zIndex: 60 }} />
        <div style={{ position: "fixed", top: pos.top, left: pos.left, zIndex: 61, width: W, background: "var(--surface)", border: "1px solid var(--border-2)", borderRadius: 10, boxShadow: "var(--shadow-lg)", padding: 6 }}>
          {items.map((it, i) => (
            <button key={i} onClick={() => { setOpen(false); it.onClick && it.onClick(); }} style={{ display: "flex", alignItems: "center", gap: 10, width: "100%", padding: "8px 10px", background: "none", border: "none", borderRadius: 7, fontSize: 12.5, color: it.danger ? "var(--danger)" : "var(--text)", textAlign: "left" }}
              onMouseEnter={e => e.currentTarget.style.background = "var(--surface-2)"} onMouseLeave={e => e.currentTarget.style.background = "none"}>
              {React.createElement(Icon[it.icon], { size: 15, color: it.danger ? "var(--danger)" : "var(--muted)" })}{it.label}
            </button>
          ))}
        </div>
      </>)}
    </div>
  );
}

const memberStatusTone = { active: ["green", "Actif"], invited: ["cyan", "Invité"], suspended: ["gray", "Suspendu"], pending: ["amber", "En attente"], rejected: ["red", "Refusé"] };
const memberTone = (s) => memberStatusTone[s] || ["gray", s || "—"];

function WhatsAppButton({ user, label }) {
  const num = user.whatsapp;
  const href = num ? "https://wa.me/" + num.replace(/[^\d]/g, "") + "?text=" + encodeURIComponent(`Salut ${user.name.split(" ")[0]}, c'est le manager Tempro-Corp 👋`) : null;
  if (!num) return (
    <span title="Pas de numéro WhatsApp" style={{ display: "inline-flex", alignItems: "center", gap: 7, padding: label ? "8px 13px" : 0, width: label ? "auto" : 30, height: 30, justifyContent: "center", borderRadius: 8, background: "var(--surface-2)", border: "1px solid var(--border)", color: "var(--faint)", fontSize: 12.5, opacity: .6 }}>
      {React.createElement(Icon.whatsapp, { size: 16 })}{label && "Aucun numéro"}
    </span>
  );
  return (
    <a href={href} target="_blank" rel="noopener" title={`WhatsApp ${num}`}
      onClick={(e) => e.stopPropagation()}
      style={{ display: "inline-flex", alignItems: "center", gap: 8, padding: label ? "8px 14px" : 0, width: label ? "auto" : 30, height: 30, justifyContent: "center",
        borderRadius: 8, background: "rgba(37,211,102,.13)", border: "1px solid rgba(37,211,102,.32)", color: "#25D366", fontSize: 12.5, fontWeight: 600, transition: "all .14s" }}
      onMouseEnter={(e) => { e.currentTarget.style.background = "rgba(37,211,102,.22)"; }}
      onMouseLeave={(e) => { e.currentTarget.style.background = "rgba(37,211,102,.13)"; }}>
      {React.createElement(Icon.whatsapp, { size: 16 })}{label && "WhatsApp"}
    </a>
  );
}

// ---------- TEAM ----------
function AdminTeam({ onMessage, onWatch, me }) {
  const splitP = useSplitPerm();
  const farm = useLiveDevices();
  const { refresh: refreshTeam } = useRegUsers(6000);   // garde l'équipe à jour (ajouts/validations)
  const myRank = window.ROLE_RANK ? window.ROLE_RANK(me && me.role) : 3;
  const [q, setQ] = useState("");
  const [invite, setInvite] = useState(false);
  const [assignFor, setAssignFor] = useState(null); // membre pour lequel on assigne des iPhones
  const [roleFor, setRoleFor] = useState(null);     // membre dont on change le rôle
  const setStatus = async (o, status) => { await window.Backend.patchUser(o.id, { status }); refreshTeam(); };
  // équipe réelle uniquement (plus de personnes fictives)
  const team = DATA.realTeam();
  const rows = team.filter(o => o.name.toLowerCase().includes(q.toLowerCase()) || o.email.toLowerCase().includes(q.toLowerCase()));
  // iPhones réellement assignés (backend) — sait qui a accès à quoi
  const liveDevs = (farm.online && farm.list) ? farm.list : [];
  const devicesOf = (id) => liveDevs.filter(d => d.operator && d.operator.id === id);
  return (
    <div>
      <PageHead title="Team" sub={`${team.length} membres · ${farm.online ? "● parc synchronisé" : "parc non synchronisé"} · ajoutez des opérateurs par email`}>
        <Button variant="outline" icon="download">Exporter</Button>
        <Button variant="primary" icon="plus" onClick={() => setInvite(true)}>Ajouter un membre</Button>
      </PageHead>
      <Toolbar q={q} setQ={setQ} placeholder="Rechercher par nom ou email…">
        <select style={selectStyle}><option>Tous les rôles</option><option>Admin</option><option>Manager</option><option>Opérateur</option></select>
      </Toolbar>
      <Table head={<><Th>Membre</Th><Th>Rôle</Th><Th>iPhones assignés</Th><Th>Split</Th><Th>Statut</Th><Th>Dernière activité</Th><Th style={{ textAlign: "right" }}>Actions</Th></>}>
        {rows.map(o => {
          const [tone, label] = memberTone(o.status);
          const devs = devicesOf(o.id);
          return (
            <tr key={o.id}>
              <Td>
                <div style={{ display: "flex", alignItems: "center", gap: 11 }}>
                  <Avatar user={o} size={34} />
                  <div><div style={{ fontWeight: 600 }}>{o.name}</div><div className="mono" style={{ fontSize: 11, color: "var(--faint)" }}>{o.email}</div></div>
                </div>
              </Td>
              <Td><Badge tone={o.role === "Admin" ? "green" : o.role === "Manager" ? "cyan" : "amber"}>{o.role}</Badge></Td>
              <Td>{devs.length
                ? <div style={{ display: "flex", flexWrap: "wrap", gap: 5, maxWidth: 220 }}>
                    {devs.map(d => <span key={d.id} className="mono" title={d.udid} style={{ display: "inline-flex", alignItems: "center", gap: 5, padding: "2px 8px", borderRadius: 99, fontSize: 11, background: "var(--accent-soft)", border: "1px solid var(--accent-line)", color: "var(--accent)" }}><StatusDot status={d.status} />{d.tag}</span>)}
                  </div>
                : <span style={{ color: "var(--faint)" }}>—</span>}</Td>
              <Td>{o.role === "Opérateur" && o.status !== "invited"
                ? <Toggle on={!!o.split_allowed} onClick={async () => { await window.Backend.patchUser(o.id, { split_allowed: o.split_allowed ? 0 : 1 }); refreshTeam(); }} />
                : <span style={{ color: "var(--faint)" }}>—</span>}</Td>
              <Td><Badge tone={tone}>{label}</Badge></Td>
              <Td><span className="mono" style={{ fontSize: 12, color: "var(--muted)" }}>{o.lastActive}</span></Td>
              <Td>
                <div style={{ display: "flex", alignItems: "center", justifyContent: "flex-end", gap: 8 }}>
                  {o.role !== "Admin" && o.status !== "invited" && (
                    <button onClick={() => onWatch && onWatch(o.id)} title="Voir son écran" style={{ display: "grid", placeItems: "center", width: 30, height: 30, borderRadius: 8, background: "var(--cyan-soft)", color: "var(--accent-2)", border: "1px solid rgba(34,211,238,.3)" }}>{React.createElement(Icon.eye, { size: 15 })}</button>
                  )}
                  {o.role !== "Admin" && o.status !== "invited" && (
                    <button onClick={() => onMessage && onMessage(o.id)} title="Message in-app" style={{ display: "grid", placeItems: "center", width: 30, height: 30, borderRadius: 8, background: "var(--accent-soft)", color: "var(--accent)", border: "1px solid var(--accent-line)" }}>{React.createElement(Icon.send, { size: 15 })}</button>
                  )}
                  <WhatsAppButton user={o} />
                  <TelegramBtn user={o} variant="icon" />
                  {(() => {
                    // hiérarchie : on n'agit que sur un membre de rang INFÉRIEUR (jamais soi-même ni un rang ≥)
                    const canAct = (me && o.id !== me.id) && (window.ROLE_RANK(o.role) < myRank);
                    if (!canAct) return <span className="mono" style={{ fontSize: 10.5, color: "var(--faint)" }}>{o.id === (me && me.id) ? "vous" : "—"}</span>;
                    return <RowActions items={[
                      { icon: "shield", label: "Changer le rôle", onClick: () => setRoleFor(o) },
                      { icon: "phone", label: "Assigner un device", onClick: () => setAssignFor(o) },
                      (o.status === "suspended"
                        ? { icon: "check", label: "Réactiver", onClick: () => setStatus(o, "active") }
                        : { icon: "lock", label: "Suspendre", onClick: () => setStatus(o, "suspended") }),
                      { icon: "x", label: "Retirer l'accès", danger: true, onClick: () => setStatus(o, "rejected") },
                    ]} />;
                  })()}
                </div>
              </Td>
            </tr>
          );
        })}
      </Table>
      {invite && <InviteModal onClose={() => setInvite(false)} onDone={refreshTeam} />}
      {roleFor && <ChangeRoleModal member={roleFor} myRank={myRank} onClose={() => setRoleFor(null)} onSave={async (role) => { const r = await window.Backend.patchUser(roleFor.id, { role }); setRoleFor(null); refreshTeam(); if (r && r.status >= 400) alert(r.error || "action refusée"); }} />}
      {assignFor && <AssignToMemberModal member={assignFor} devices={liveDevs} online={farm.online}
        onClose={() => setAssignFor(null)}
        onSave={async (changes) => {
          for (const [udid, give] of changes) await window.Backend.patchMeta(udid, { operator_id: give ? assignFor.id : null });
          setAssignFor(null); setTimeout(farm.refresh, 300);
        }} />}
    </div>
  );
}

// Modal : changer le rôle d'un membre déjà validé.
function ChangeRoleModal({ member, myRank, onClose, onSave }) {
  const [role, setRole] = useState(member.role || "Opérateur");
  const [busy, setBusy] = useState(false);
  // on ne propose QUE des rôles strictement inférieurs au sien (hiérarchie)
  const ROLES = [
    ["Owner", "Propriétaire — accès total"],
    ["Manager", "Manager — supervision (sans facturation sauf autorisation)"],
    ["Opérateur", "Opérateur — pilotage de ses iPhones assignés"],
  ].filter(([r]) => window.ROLE_RANK(r) < (myRank || 3));
  return (
    <Modal title={`Rôle de ${member.name}`} onClose={onClose} width={460}
      footer={<><Button variant="ghost" onClick={onClose}>Annuler</Button>
        <Button variant="primary" icon="check" disabled={busy || role === member.role} onClick={async () => { setBusy(true); await onSave(role); }}>{busy ? "…" : "Enregistrer le rôle"}</Button></>}>
      <div style={{ display: "grid", gap: 8 }}>
        {ROLES.map(([r, desc]) => {
          const on = role === r;
          return (
            <button key={r} onClick={() => setRole(r)} style={{ display: "flex", alignItems: "center", gap: 11, padding: "12px 14px", borderRadius: 11, textAlign: "left",
              background: on ? "var(--accent-soft)" : "var(--surface-2)", border: "1px solid " + (on ? "var(--accent-line)" : "var(--border)"), cursor: "pointer" }}>
              <span style={{ width: 20, height: 20, borderRadius: 99, display: "grid", placeItems: "center", background: on ? "var(--accent)" : "var(--surface-3)", color: "#06281c", border: on ? "none" : "1px solid var(--border-2)" }}>{on && React.createElement(Icon.check, { size: 13 })}</span>
              <div><div style={{ fontSize: 13.5, fontWeight: 600 }}>{r}{r === member.role ? " · actuel" : ""}</div><div style={{ fontSize: 11.5, color: "var(--faint)" }}>{desc}</div></div>
            </button>
          );
        })}
      </div>
    </Modal>
  );
}

// Modal : cocher les iPhones du parc réel à assigner à CE membre.
// Décocher = libérer l'iPhone (plus personne ne le voit côté opérateur).
function AssignToMemberModal({ member, devices, online, onClose, onSave }) {
  const [picked, setPicked] = useState(() => new Set(devices.filter(d => d.operator && d.operator.id === member.id).map(d => d.udid)));
  const [busy, setBusy] = useState(false);
  const toggle = (udid) => setPicked(p => { const n = new Set(p); n.has(udid) ? n.delete(udid) : n.add(udid); return n; });
  const save = async () => {
    setBusy(true);
    const changes = [];
    devices.forEach(d => {
      const was = !!(d.operator && d.operator.id === member.id);
      const now2 = picked.has(d.udid);
      if (was !== now2) changes.push([d.udid, now2]);
    });
    await onSave(changes);
  };
  return (
    <Modal title={`Assigner des iPhones — ${member.name}`} onClose={onClose} width={520}
      footer={<><Button variant="ghost" onClick={onClose}>Annuler</Button><Button variant="primary" icon="check" disabled={busy || !online} onClick={save}>{busy ? "Enregistrement…" : "Enregistrer"}</Button></>}>
      {!online
        ? <div style={{ color: "var(--faint)", fontSize: 13, textAlign: "center", padding: "20px 0" }}>Parc non synchronisé — synchronise le backend d'abord.</div>
        : devices.length === 0
          ? <div style={{ color: "var(--faint)", fontSize: 13, textAlign: "center", padding: "20px 0" }}>Aucun iPhone détecté dans le parc.</div>
          : <div style={{ display: "grid", gap: 8, maxHeight: 360, overflow: "auto" }}>
              {devices.map(d => {
                const mine = picked.has(d.udid);
                const other = d.operator && d.operator.id !== member.id ? d.operator : null;
                return (
                  <button key={d.udid} onClick={() => toggle(d.udid)} style={{ display: "flex", alignItems: "center", gap: 11, padding: "11px 13px", borderRadius: 10, textAlign: "left",
                    background: mine ? "var(--accent-soft)" : "var(--surface-2)", border: "1px solid " + (mine ? "var(--accent-line)" : "var(--border)"), cursor: "pointer" }}>
                    <StatusDot status={d.status} />
                    <div style={{ flex: 1, minWidth: 0 }}>
                      <div style={{ fontSize: 13.5, fontWeight: 600 }}>{d.tag}</div>
                      <div className="mono" style={{ fontSize: 10, color: "var(--faint)" }}>{d.udid}</div>
                    </div>
                    {other && !mine && <span className="mono" style={{ fontSize: 10.5, color: "var(--busy)" }}>déjà à {other.name.split(" ")[0]}</span>}
                    <span style={{ width: 20, height: 20, borderRadius: 6, display: "grid", placeItems: "center", background: mine ? "var(--accent)" : "var(--surface-3)", color: "#06281c", border: mine ? "none" : "1px solid var(--border-2)" }}>{mine && React.createElement(Icon.check, { size: 13 })}</span>
                  </button>
                );
              })}
            </div>}
      <div className="mono" style={{ marginTop: 12, fontSize: 11, color: "var(--faint)" }}>Cocher = ce membre voit et pilote l'iPhone. Décocher = iPhone libéré. (Un iPhone déjà assigné à quelqu'un d'autre sera réassigné.)</div>
    </Modal>
  );
}

function InviteModal({ onClose, onDone }) {
  const farm = useLiveDevices();
  const liveDevs = (farm.online && farm.list) ? farm.list : [];
  const [role, setRole] = useState("Opérateur");
  const [picked, setPicked] = useState([]);  // liste d'udid réels
  const [name, setName] = useState("");
  const [email, setEmail] = useState("");
  const [wa, setWa] = useState("");
  const [tg, setTg] = useState("");
  const [busy, setBusy] = useState(false);
  const [done, setDone] = useState(null);     // user créé
  const [err, setErr] = useState("");
  const toggle = (udid) => setPicked(p => p.includes(udid) ? p.filter(x => x !== udid) : [...p, udid]);
  const field = { width: "100%", padding: "11px 13px", background: "var(--bg-2)", border: "1px solid var(--border-2)", borderRadius: 9, color: "var(--text)", fontSize: 13.5, outline: "none" };

  const create = async () => {
    setErr(""); setBusy(true);
    const r = await window.Backend.createMember({ name: name.trim(), email: email.trim(), role, whatsapp: wa.trim() || null, telegram: tg.trim() || null });
    if (!r || !r.user) { setBusy(false); setErr((r && r.error) || "échec — backend joignable ?"); return; }
    // assigner les iPhones réels choisis
    for (const udid of picked) await window.Backend.patchMeta(udid, { operator_id: r.user.id });
    setBusy(false); setDone(r.user);
    if (onDone) onDone();
  };

  if (done) {
    return (
      <Modal title="Membre ajouté" onClose={onClose} width={500}
        footer={<Button variant="primary" icon="check" onClick={onClose}>Terminé</Button>}>
        <div style={{ display: "grid", gap: 16 }}>
          <div style={{ display: "flex", alignItems: "center", gap: 12, padding: "14px 16px", borderRadius: 12, background: "var(--accent-soft)", border: "1px solid var(--accent-line)" }}>
            <span style={{ width: 38, height: 38, borderRadius: 99, background: "var(--accent)", color: "#06281c", display: "grid", placeItems: "center", flex: "none" }}>{React.createElement(Icon.check, { size: 20 })}</span>
            <div style={{ fontSize: 13.5, color: "var(--text)", lineHeight: 1.5 }}>
              <b>{done.name}</b> est ajouté en <b style={{ color: "var(--accent)" }}>{done.role}</b>{picked.length ? ` avec ${picked.length} iPhone(s)` : ""}. <b>Compte actif tout de suite</b> — pas de validation à faire.
            </div>
          </div>
          <div style={{ padding: "12px 14px", borderRadius: 11, background: "var(--surface-2)", border: "1px solid var(--border)", fontSize: 12.5, color: "var(--muted)", lineHeight: 1.6 }}>
            Pour se connecter, <b>{done.name.split(" ")[0]}</b> utilise l'email <span className="mono" style={{ color: "var(--text)" }}>{done.email}</span> :
            <div style={{ marginTop: 6 }}>• <b>« Continuer avec Google »</b> (si c'est une adresse Google) → accès direct.</div>
            <div>• ou <b>« S'inscrire »</b> avec ce même email pour choisir un mot de passe → accès direct (compte déjà validé).</div>
          </div>
        </div>
      </Modal>
    );
  }

  return (
    <Modal title="Ajouter un membre" onClose={onClose} width={520}
      footer={<><Button variant="ghost" onClick={onClose}>Annuler</Button>
        <Button variant="primary" icon="check" disabled={busy || !name.trim() || !email.trim()} onClick={create}>{busy ? "Ajout…" : "Ajouter le membre"}</Button></>}>
      <div style={{ display: "grid", gap: 16 }}>
        <div style={{ fontSize: 12.5, color: "var(--muted)", lineHeight: 1.5, padding: "10px 12px", borderRadius: 9, background: "var(--surface-2)", border: "1px solid var(--border)" }}>
          Ajout manuel = <b style={{ color: "var(--accent)" }}>compte actif direct</b> (la personne n'a pas besoin de validation). Elle se connectera avec cet email (Google ou mot de passe).
        </div>
        {err && <div style={{ padding: "9px 12px", borderRadius: 9, background: "rgba(255,92,108,.1)", border: "1px solid rgba(255,92,108,.32)", color: "var(--danger)", fontSize: 12.5 }}>{err}</div>}
        <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 12 }}>
          <div><label style={lab}>Nom complet</label>
            <input value={name} onChange={e => setName(e.target.value)} placeholder="Prénom Nom" style={{ ...field, marginTop: 7 }} autoFocus /></div>
          <div><label style={lab}>Email</label>
            <input value={email} onChange={e => setEmail(e.target.value)} placeholder="nouvel.operateur@email.io" style={{ ...field, marginTop: 7 }} /></div>
        </div>
        <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 12 }}>
          <div><label style={{ ...lab, display: "flex", alignItems: "center", gap: 6 }}><span style={{ color: "#25D366", display: "grid" }}>{React.createElement(Icon.whatsapp, { size: 14 })}</span> WhatsApp <span className="mono" style={{ color: "var(--faint)", fontWeight: 400 }}>· optionnel</span></label>
            <input value={wa} onChange={e => setWa(e.target.value)} placeholder="+33 6 12 34 56 78" inputMode="tel" style={{ ...field, marginTop: 7 }} /></div>
          <div><label style={{ ...lab, display: "flex", alignItems: "center", gap: 6 }}><span style={{ color: "#2AABEE", display: "grid" }}>{React.createElement(Icon.telegram, { size: 14 })}</span> Telegram <span className="mono" style={{ color: "var(--faint)", fontWeight: 400 }}>· optionnel</span></label>
            <input value={tg} onChange={e => setTg(e.target.value)} placeholder="@pseudo" style={{ ...field, marginTop: 7 }} /></div>
        </div>
        <div>
          <label style={lab}>Rôle</label>
          <div style={{ display: "flex", gap: 8, marginTop: 7 }}>
            {["Manager", "Opérateur"].map(r => (
              <button key={r} onClick={() => setRole(r)} style={{ flex: 1, padding: "10px", borderRadius: 9, fontSize: 13, fontWeight: 600,
                background: role === r ? "var(--accent-soft)" : "var(--surface-2)", border: "1px solid " + (role === r ? "var(--accent-line)" : "var(--border)"), color: role === r ? "var(--accent)" : "var(--text)" }}>{r}</button>
            ))}
          </div>
        </div>
        <div>
          <label style={lab}>Assigner des iPhones <span className="mono" style={{ color: "var(--faint)", fontWeight: 400 }}>· {picked.length} sélectionné(s)</span></label>
          {!farm.online
            ? <div style={{ marginTop: 9, padding: "11px 13px", borderRadius: 9, background: "var(--surface-2)", border: "1px dashed var(--border-2)", color: "var(--faint)", fontSize: 12.5 }}>Parc non synchronisé — tu pourras assigner les iPhones ensuite via Team → ⋯ → Assigner un device.</div>
            : liveDevs.length === 0
              ? <div style={{ marginTop: 9, padding: "11px 13px", borderRadius: 9, background: "var(--surface-2)", border: "1px dashed var(--border-2)", color: "var(--faint)", fontSize: 12.5 }}>Aucun iPhone détecté dans le parc.</div>
              : <div style={{ display: "grid", gridTemplateColumns: "repeat(2,1fr)", gap: 8, marginTop: 9, maxHeight: 150, overflow: "auto" }}>
                  {liveDevs.map(d => {
                    const on = picked.includes(d.udid);
                    const other = d.operator ? d.operator : null;
                    return (
                      <button key={d.udid} onClick={() => toggle(d.udid)} title={d.udid} style={{ display: "flex", alignItems: "center", gap: 8, padding: "9px 11px", borderRadius: 9, textAlign: "left",
                        background: on ? "var(--accent-soft)" : "var(--surface-2)", border: "1px solid " + (on ? "var(--accent-line)" : "var(--border)") }}>
                        <StatusDot status={d.status} /><span style={{ fontSize: 12.5, fontWeight: 600, flex: 1, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{d.tag}</span>
                        {other && !on && <span className="mono" style={{ fontSize: 9.5, color: "var(--busy)" }}>à {other.name.split(" ")[0]}</span>}
                        {on && <span style={{ color: "var(--accent)", display: "grid" }}>{React.createElement(Icon.check, { size: 15 })}</span>}
                      </button>
                    );
                  })}
                </div>}
        </div>
      </div>
    </Modal>
  );
}
const inviteChan = (fg, bg, bd) => ({ flex: 1, display: "flex", alignItems: "center", justifyContent: "center", gap: 7, padding: "10px", borderRadius: 9, background: bg, color: fg, border: `1px solid ${bd}`, fontSize: 12.5, fontWeight: 600, textDecoration: "none" });
const lab = { fontSize: 12.5, fontWeight: 600, color: "var(--muted)" };

// ---------- ROLES ----------
function AdminRoles() {
  const roles = useRoles();
  const [sel, setSel] = useState(DATA.ROLES[0].id);
  const role = DATA.ROLES.find(r => r.id === sel);
  const perms = roles.all(role.name);
  return (
    <div>
      <PageHead title="Roles" sub="rôles & matrice de permissions">
        <Button variant="primary" icon="plus">Créer un rôle</Button>
      </PageHead>
      <div style={{ display: "grid", gridTemplateColumns: "260px 1fr", gap: 18 }} className="ov-grid">
        <div style={{ display: "grid", gap: 10, alignContent: "start" }}>
          {DATA.ROLES.map(r => (
            <button key={r.id} onClick={() => setSel(r.id)} style={{ textAlign: "left", padding: 15, borderRadius: "var(--r-lg)", background: "var(--surface)",
              border: "1px solid " + (sel === r.id ? "var(--accent-line)" : "var(--border)"), boxShadow: sel === r.id ? "0 0 0 1px var(--accent-line)" : "none" }}>
              <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between" }}>
                <div style={{ display: "flex", alignItems: "center", gap: 9 }}><span style={{ width: 9, height: 9, borderRadius: 3, background: r.color }} /><span style={{ fontWeight: 700, fontSize: 14 }}>{r.name}</span></div>
                <Badge tone="neutral"><span className="num">{r.members}</span></Badge>
              </div>
              <p style={{ margin: "9px 0 0", fontSize: 12, color: "var(--muted)", lineHeight: 1.5 }}>{r.desc}</p>
            </button>
          ))}
        </div>
        <Card pad={22}>
          <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", marginBottom: 4 }}>
            <h3 style={{ margin: 0, fontSize: 16 }}>Permissions · {role.name}</h3>
            <span className="mono" style={{ fontSize: 11, color: "var(--accent)" }}>● appliqué en direct</span>
          </div>
          <p style={{ margin: "0 0 18px", fontSize: 12.5, color: "var(--muted)" }}>Cochez ce que ce rôle peut faire — ex. donner la <b>Facturation</b> au Manager.</p>
          <div style={{ display: "grid", gap: 2 }}>
            {DATA.PERM_LABELS.map(([key, label]) => (
              <label key={key} style={{ display: "flex", alignItems: "center", justifyContent: "space-between", padding: "13px 14px", borderRadius: 10, cursor: "pointer" }}
                onMouseEnter={e => e.currentTarget.style.background = "var(--surface-2)"} onMouseLeave={e => e.currentTarget.style.background = "transparent"}>
                <span style={{ fontSize: 13.5 }}>{label}</span>
                <Toggle on={!!perms[key]} onClick={() => roles.set(role.name, key, !perms[key])} />
              </label>
            ))}
          </div>
        </Card>
      </div>
    </div>
  );
}
function Toggle({ on, onClick }) {
  return (
    <button onClick={onClick} style={{ width: 42, height: 24, borderRadius: 99, border: "none", padding: 3, background: on ? "var(--accent)" : "var(--surface-3)", transition: "background .16s" }}>
      <span style={{ display: "block", width: 18, height: 18, borderRadius: 99, background: "#fff", transform: on ? "translateX(18px)" : "none", transition: "transform .16s" }} />
    </button>
  );
}

// ---------- IPHONES ----------
function AdminIPhones() {
  const fleet = useFleet();
  const farm = useLiveDevices();
  const isLive = farm.online;
  const [q, setQ] = useState("");
  const [pool, setPool] = useState("all");
  const [addOpen, setAddOpen] = useState(false);
  const [groupOpen, setGroupOpen] = useState(false);
  const [assign, setAssign] = useState(null); // null | true | deviceId
  const [rename, setRename] = useState(null); // device à renommer
  // plus de données démo : uniquement les vrais iPhones du backend
  const devices = (isLive && farm.list) ? farm.list : [];
  const allPools = [...new Set(devices.map(d => d.pool).filter(Boolean))];
  const pools = ["all", ...allPools];
  const rows = devices.filter(d => (pool === "all" || d.pool === pool) && (d.tag.toLowerCase().includes(q.toLowerCase()) || (d.name || "").toLowerCase().includes(q.toLowerCase())));

  const addPool = (name) => fleet.addPool(name);
  // assignation réelle -> PATCH backend par UDID (persistant)
  const doAssign = (opId, ids) => { ids.forEach(id => window.Backend.patchMeta(id, { operator_id: opId })); setTimeout(farm.refresh, 300); };

  // sync manuelle : re-interroge le backend tout de suite + feedback (le poll auto continue derrière)
  const [sync, setSync] = useState(""); // "" | "busy" | message résultat
  const syncBackend = async () => {
    setSync("busy");
    const d = await farm.refresh();
    setSync(d ? `✓ ${d.length} iPhone(s) synchronisé(s)` : "✗ Backend injoignable");
    setTimeout(() => setSync(""), 3000);
  };

  return (
    <div>
      <PageHead title="iPhones" sub={`${isLive ? "● LIVE (backend) · " : "non synchronisé · "}${devices.length} devices · ${allPools.length} groupes · attribuez les iPhones aux opérateurs`}>
        {sync && sync !== "busy" && <span className="mono" style={{ alignSelf: "center", fontSize: 12, fontWeight: 600, color: sync.startsWith("✓") ? "var(--ok, #34d399)" : "var(--danger, #f87171)" }}>{sync}</span>}
        <Button variant="outline" icon="refresh" onClick={syncBackend} disabled={sync === "busy"}>{sync === "busy" ? "Synchronisation…" : "Synchroniser le backend"}</Button>
        {isLive && <>
          <Button variant="outline" icon="grid" onClick={() => setGroupOpen(true)}>Nouveau groupe</Button>
          <Button variant="primary" icon="link" onClick={() => setAssign(true)}>Assigner</Button>
        </>}
      </PageHead>

      {!isLive ? <SyncNotice online={false} ready={farm.ready} variant="admin" onSync={farm.refresh} /> : <>
      <Toolbar q={q} setQ={setQ} placeholder="Rechercher un iPhone (tag, ID)…">
        <select value={pool} onChange={e => setPool(e.target.value)} style={selectStyle}>{pools.map(p => <option key={p} value={p}>{p === "all" ? "Tous les pods" : p}</option>)}</select>
      </Toolbar>
      <Table head={<><Th>Pod / Groupe</Th><Th>iPhone</Th><Th>Modèle · iOS</Th><Th>Now used by</Th><Th>Statut</Th><Th>Dernière connexion</Th><Th style={{ textAlign: "right" }}>Actions</Th></>}>
        {rows.map(d => (
          <tr key={d.id}>
            <Td><span className="mono" style={{ fontSize: 12, color: "var(--muted)" }}>{d.pool}</span></Td>
            <Td><div style={{ display: "flex", alignItems: "center", gap: 10 }}>
              <span style={{ width: 26, height: 38, borderRadius: 5, border: "1.5px solid var(--border-2)", display: "grid", placeItems: "center", flex: "none", background: "var(--surface-2)" }}>{React.createElement(Icon.phone, { size: 14, color: "var(--faint)" })}</span>
              <div><div style={{ fontWeight: 600 }}>{d.tag}</div><div className="mono" style={{ fontSize: 10.5, color: "var(--faint)" }}>{d.udid}</div></div>
            </div></Td>
            <Td><span style={{ color: "var(--muted)" }}>{d.model}</span> <span className="mono" style={{ fontSize: 11, color: "var(--faint)" }}>· {d.ios}</span></Td>
            <Td>{d.operator ? <div style={{ display: "flex", alignItems: "center", gap: 8 }}><Avatar user={d.operator} size={24} /><span style={{ fontSize: 12.5 }}>{d.operator.name}</span></div> : <span style={{ color: "var(--faint)", fontSize: 12.5 }}>— non assigné —</span>}</Td>
            <Td><Badge tone={d.status === "online" ? "green" : d.status === "busy" ? "amber" : d.status === "locked" ? "gray" : "red"}><StatusDot status={d.status} /> {STATUS_META[d.status].label}</Badge></Td>
            <Td>{d.status === "offline"
              ? <span style={{ display: "inline-flex", alignItems: "center", gap: 6, color: "var(--busy)" }}>{React.createElement(Icon.clock, { size: 13 })}<span className="mono" style={{ fontSize: 11.5 }}>{d.lastSeen}</span></span>
              : <span className="mono" style={{ fontSize: 11.5, color: d.status === "online" ? "var(--accent)" : "var(--muted)" }}>{d.lastSeen}</span>}</Td>
            <Td><RowActions items={[{ icon: "settings", label: "Renommer", onClick: () => setRename(d) }, { icon: "link", label: "Assigner à un opérateur", onClick: () => setAssign(d.id) }, { icon: "chart", label: "Data & Usage" }, { icon: "play", label: "Activer / Désactiver" }, { icon: "x", label: "Retirer du pod", danger: true }]} /></Td>
          </tr>
        ))}
      </Table>
      </>}
      {groupOpen && <GroupModal existing={allPools} onClose={() => setGroupOpen(false)} onCreate={(name) => { addPool(name); setGroupOpen(false); }} />}
      {assign && <AssignPhonesModal devices={devices} initialDevice={typeof assign === "string" ? assign : null} onClose={() => setAssign(null)} onAssign={doAssign} />}
      {rename && <RenameModal device={rename} live={isLive} onClose={() => setRename(null)} onRename={(name) => { window.Backend.patchMeta(rename.udid, { label: name }); setTimeout(farm.refresh, 300); setRename(null); }} />}
    </div>
  );
}

function RenameModal({ device, live, onClose, onRename }) {
  const [name, setName] = useState(device.tag || device.label || "");
  const field = { width: "100%", padding: "11px 13px", background: "var(--bg-2)", border: "1px solid var(--border-2)", borderRadius: 9, color: "var(--text)", fontSize: 13.5, outline: "none" };
  return (
    <Modal title={`Renommer — ${device.tag}`} onClose={onClose} width={420}
      footer={<><Button variant="ghost" onClick={onClose}>Annuler</Button>
        <Button variant="primary" icon="check" disabled={!name.trim()} onClick={() => onRename(name.trim())}>Enregistrer</Button></>}>
      <div style={{ display: "grid", gap: 12 }}>
        <div><label style={{ fontSize: 12.5, fontWeight: 600, color: "var(--muted)" }}>Nom du device</label>
          <input value={name} onChange={e => setName(e.target.value)} placeholder="ex. Cobra" style={{ ...field, marginTop: 7 }} autoFocus /></div>
        <div className="mono" style={{ fontSize: 11, color: "var(--faint)", display: "flex", alignItems: "center", gap: 7 }}>
          {React.createElement(Icon.phone, { size: 13 })} UDID {device.udid} <span style={{ opacity: .7 }}>· permanent {live ? "· enregistré dans le backend" : ""}</span>
        </div>
      </div>
    </Modal>
  );
}

function GroupModal({ existing, onClose, onCreate }) {
  const [name, setName] = useState("");
  const [city, setCity] = useState("");
  const field = { width: "100%", padding: "11px 13px", background: "var(--bg-2)", border: "1px solid var(--border-2)", borderRadius: 9, color: "var(--text)", fontSize: 13.5, outline: "none" };
  const full = city.trim() ? `${name.trim()} · ${city.trim()}` : name.trim();
  const dup = existing.includes(full);
  return (
    <Modal title="Nouveau groupe / pod" onClose={onClose} width={440}
      footer={<><Button variant="ghost" onClick={onClose}>Annuler</Button><Button variant="primary" icon="check" disabled={!name.trim() || dup} onClick={() => onCreate(full)}>Créer le groupe</Button></>}>
      <div style={{ display: "grid", gap: 16 }}>
        <div><label style={lab}>Nom du groupe</label>
          <input value={name} onChange={e => setName(e.target.value)} placeholder="ex. Pod D" style={{ ...field, marginTop: 7 }} autoFocus /></div>
        <div><label style={lab}>Lieu / région <span className="mono" style={{ color: "var(--faint)", fontWeight: 400 }}>· optionnel</span></label>
          <input value={city} onChange={e => setCity(e.target.value)} placeholder="ex. Marseille" style={{ ...field, marginTop: 7 }} /></div>
        <div style={{ display: "flex", alignItems: "center", gap: 9, padding: "10px 13px", borderRadius: 10, background: dup ? "rgba(255,92,108,.1)" : "var(--surface-2)", border: "1px solid " + (dup ? "rgba(255,92,108,.3)" : "var(--border)"), fontSize: 12.5, color: dup ? "var(--danger)" : "var(--muted)" }}>
          {React.createElement(Icon.grid, { size: 15 })} {dup ? "Ce groupe existe déjà." : <>Aperçu : <b style={{ color: "var(--text)", marginLeft: 4 }}>{full || "—"}</b></>}
        </div>
      </div>
    </Modal>
  );
}

const MODELS_LIST = ["iPhone SE (2e gén)", "iPhone SE (3e gén)", "iPhone 12 mini", "iPhone 12", "iPhone 12 Pro", "iPhone 12 Pro Max", "iPhone 13 mini", "iPhone 13", "iPhone 13 Pro", "iPhone 13 Pro Max", "iPhone 14", "iPhone 14 Plus", "iPhone 14 Pro", "iPhone 14 Pro Max", "iPhone 15", "iPhone 15 Plus", "iPhone 15 Pro", "iPhone 15 Pro Max", "iPhone 16", "iPhone 16 Pro", "iPhone 16 Pro Max"];
const IOS_LIST = ["26.5", "26.4", "26.3.1", "26.2", "26.1", "26.0", "18.6.2", "18.6.1", "18.6", "18.5", "18.4.1", "18.3.2", "18.2.1", "18.1.1", "17.7.2", "16.7.10", "15.8.3"];

function AddPhoneModal({ pools, onAddPool, onClose, onAdd }) {
  const [tag, setTag] = useState("");
  const [model, setModel] = useState(MODELS_LIST[7]);
  const [ios, setIos] = useState(IOS_LIST[0]);
  const [pool, setPool] = useState(pools[0] || "Pod A · Paris");
  const [newPool, setNewPool] = useState("");
  const [creatingPool, setCreatingPool] = useState(false);
  const [opId, setOpId] = useState("");
  const ops = DATA.realTeam().filter(o => o.role === "Opérateur" && o.status === "active");
  const field = { width: "100%", padding: "11px 13px", background: "var(--bg-2)", border: "1px solid var(--border-2)", borderRadius: 9, color: "var(--text)", fontSize: 13.5, outline: "none" };

  const submit = () => {
    let finalPool = pool;
    if (creatingPool && newPool.trim()) { finalPool = newPool.trim(); onAddPool && onAddPool(finalPool); }
    const n = Math.floor(Math.random() * 90 + 10);
    onAdd({
      id: "d" + Date.now(), tag: tag.trim() || "Nouveau", name: "iPhone-" + n, pool: finalPool, model, ios,
      status: "online", battery: 100, fps: 5, latency: 48,
      operator: opId ? DATA.opById(opId) : null,
      udid: "00008" + n + "-00" + Math.floor(Math.random() * 9000 + 1000), lastFrame: "il y a 0.2 s", lastSeen: "à l'instant",
    });
    onClose();
  };

  return (
    <Modal title="Ajouter un iPhone" onClose={onClose} width={520}
      footer={<><Button variant="ghost" onClick={onClose}>Annuler</Button><Button variant="primary" icon="plus" onClick={submit}>Ajouter l'iPhone</Button></>}>
      <div style={{ display: "grid", gap: 16 }}>
        <div><label style={lab}>Nom / Tag</label>
          <input value={tag} onChange={e => setTag(e.target.value)} placeholder="ex. Phoenix" style={{ ...field, marginTop: 7 }} autoFocus /></div>
        <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 12 }}>
          <div><label style={lab}>Modèle</label>
            <select value={model} onChange={e => setModel(e.target.value)} style={{ ...field, marginTop: 7 }}>{MODELS_LIST.map(m => <option key={m}>{m}</option>)}</select></div>
          <div><label style={lab}>Version iOS</label>
            <select value={ios} onChange={e => setIos(e.target.value)} style={{ ...field, marginTop: 7 }}>{IOS_LIST.map(v => <option key={v}>{v}</option>)}</select></div>
        </div>
        <div><div style={{ display: "flex", alignItems: "center", justifyContent: "space-between" }}>
            <label style={lab}>Pod / Groupe</label>
            <button onClick={() => setCreatingPool(c => !c)} style={{ display: "flex", alignItems: "center", gap: 5, background: "none", border: "none", color: creatingPool ? "var(--accent)" : "var(--accent-2)", fontSize: 11.5, fontWeight: 600, cursor: "pointer" }}>
              {React.createElement(Icon[creatingPool ? "chevDown" : "plus"], { size: 13 })}{creatingPool ? "Choisir existant" : "Nouveau groupe"}
            </button>
          </div>
          {creatingPool
            ? <input value={newPool} onChange={e => setNewPool(e.target.value)} placeholder="ex. Pod D · Marseille" style={{ ...field, marginTop: 7 }} autoFocus />
            : <select value={pool} onChange={e => setPool(e.target.value)} style={{ ...field, marginTop: 7 }}>{pools.map(p => <option key={p}>{p}</option>)}</select>}
        </div>
        <div><label style={lab}>Assigner à <span className="mono" style={{ color: "var(--faint)", fontWeight: 400 }}>· optionnel</span></label>
          <select value={opId} onChange={e => setOpId(e.target.value)} style={{ ...field, marginTop: 7 }}>
            <option value="">— non assigné —</option>
            {ops.map(o => <option key={o.id} value={o.id}>{o.name}</option>)}
          </select></div>
        <div className="mono" style={{ fontSize: 11, color: "var(--faint)" }}>l'iPhone apparaîtra dans le pod choisi · jumelage HC BOX requis côté device.</div>
      </div>
    </Modal>
  );
}

function AssignPhonesModal({ devices, initialDevice, onClose, onAssign }) {
  const ops = DATA.realTeam().filter(o => o.role === "Opérateur" && o.status !== "suspended");
  const initOp = initialDevice ? (devices.find(d => d.id === initialDevice)?.operator?.id || ops[0].id) : ops[0].id;
  const [opId, setOpId] = useState(initOp);
  const [picked, setPicked] = useState(initialDevice ? [initialDevice] : []);
  const toggle = (id) => setPicked(p => p.includes(id) ? p.filter(x => x !== id) : [...p, id]);
  const op = DATA.opById(opId);

  return (
    <Modal title="Assigner des iPhones" onClose={onClose} width={560}
      footer={<><Button variant="ghost" onClick={onClose}>Annuler</Button>
        <Button variant="primary" icon="link" disabled={!picked.length} onClick={() => { onAssign(opId, picked); onClose(); }}>Assigner {picked.length} iPhone{picked.length > 1 ? "s" : ""}</Button></>}>
      <div style={{ display: "grid", gap: 18 }}>
        <div>
          <label style={lab}>Attribuer à l'opérateur</label>
          <div style={{ display: "grid", gridTemplateColumns: "repeat(2,1fr)", gap: 8, marginTop: 9 }}>
            {ops.map(o => (
              <button key={o.id} onClick={() => setOpId(o.id)} style={{ display: "flex", alignItems: "center", gap: 9, padding: "9px 11px", borderRadius: 10, textAlign: "left",
                background: opId === o.id ? "var(--accent-soft)" : "var(--surface-2)", border: "1px solid " + (opId === o.id ? "var(--accent-line)" : "var(--border)") }}>
                <Avatar user={o} size={28} /><span style={{ fontSize: 13, fontWeight: 600 }}>{o.name}</span>
                {opId === o.id && <span style={{ marginLeft: "auto", color: "var(--accent)", display: "grid" }}>{React.createElement(Icon.check, { size: 16 })}</span>}
              </button>
            ))}
          </div>
        </div>
        <div>
          <label style={lab}>iPhones à attribuer <span className="mono" style={{ color: "var(--faint)", fontWeight: 400 }}>· {picked.length} sélectionné(s)</span></label>
          <div style={{ display: "grid", gridTemplateColumns: "repeat(2,1fr)", gap: 8, marginTop: 9, maxHeight: 230, overflow: "auto", paddingRight: 2 }}>
            {devices.map(d => {
              const on = picked.includes(d.id);
              return (
                <button key={d.id} onClick={() => toggle(d.id)} style={{ display: "flex", alignItems: "center", gap: 9, padding: "9px 11px", borderRadius: 10, textAlign: "left",
                  background: on ? "var(--accent-soft)" : "var(--surface-2)", border: "1px solid " + (on ? "var(--accent-line)" : "var(--border)") }}>
                  <StatusDot status={d.status} />
                  <div style={{ minWidth: 0, flex: 1 }}>
                    <div style={{ fontSize: 12.5, fontWeight: 600 }}>{d.tag}</div>
                    <div className="mono" style={{ fontSize: 10, color: "var(--faint)", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{d.operator ? "→ " + d.operator.name.split(" ")[0] : "non assigné"}</div>
                  </div>
                  {on && <span style={{ color: "var(--accent)", display: "grid", flex: "none" }}>{React.createElement(Icon.check, { size: 15 })}</span>}
                </button>
              );
            })}
          </div>
        </div>
        <div style={{ display: "flex", alignItems: "center", gap: 9, padding: "10px 13px", borderRadius: 10, background: "var(--accent-soft)", border: "1px solid var(--accent-line)", fontSize: 12.5, color: "var(--accent)" }}>
          {React.createElement(Icon.user, { size: 16 })} {picked.length ? `${picked.length} iPhone(s) → ${op.name}` : "Sélectionnez au moins un iPhone."}
        </div>
      </div>
    </Modal>
  );
}

// ============================================================
// Demandes d'acceptation — les inscrits "en attente" que l'Owner valide (avec rôle) ou refuse
// ============================================================
function AdminRequests({ me }) {
  const { useState } = React;
  const { users, refresh } = useRegUsers(5000);
  const pending = users.filter(u => u.status === "pending");
  const [busy, setBusy] = useState(null);
  const myRank = window.ROLE_RANK ? window.ROLE_RANK(me && me.role) : 3;
  const canMakeManager = myRank > window.ROLE_RANK("Manager");   // owner uniquement

  const decide = async (u, action, role) => {
    setBusy(u.id + action);
    const r = (action === "accept")
      ? await window.Backend.patchUser(u.id, { status: "active", role })
      : await window.Backend.patchUser(u.id, { status: "rejected" });
    await refresh();
    setBusy(null);
    if (r && r.status >= 400) alert(r.error || "action refusée");
  };

  return (
    <div>
      <PageHead title="Demandes d'acceptation" sub={`${pending.length} compte(s) en attente de validation · accepte avec un rôle ou refuse`} />
      {pending.length === 0
        ? <div style={{ textAlign: "center", padding: "70px 0", color: "var(--faint)" }}>
            {React.createElement(Icon.check, { size: 34 })}
            <p style={{ marginTop: 12 }}>Aucune demande en attente. ✓</p>
            <p className="mono" style={{ fontSize: 11.5 }}>Quand quelqu'un s'inscrit (email ou Google), il apparaît ici avant d'avoir accès.</p>
          </div>
        : <div style={{ display: "grid", gap: 11 }}>
            {pending.map(u => (
              <div key={u.id} style={{ display: "flex", alignItems: "center", gap: 14, flexWrap: "wrap", padding: "14px 16px", borderRadius: 13, background: "var(--surface)", border: "1px solid var(--border)" }}>
                <Avatar user={u} size={40} />
                <div style={{ flex: 1, minWidth: 160 }}>
                  <div style={{ fontSize: 14, fontWeight: 600 }}>{u.name}</div>
                  <div className="mono" style={{ fontSize: 11, color: "var(--faint)" }}>{u.email} · inscrit {u.created || ""}{u.google_sub ? " · via Google" : ""}</div>
                </div>
                <div style={{ display: "flex", alignItems: "center", gap: 8, flexWrap: "wrap" }}>
                  <span className="mono" style={{ fontSize: 11, color: "var(--muted)" }}>Accepter comme :</span>
                  <Button variant="primary" icon="check" disabled={!!busy} onClick={() => decide(u, "accept", "Opérateur")}>Opérateur</Button>
                  {canMakeManager && <Button variant="outline" icon="eye" disabled={!!busy} onClick={() => decide(u, "accept", "Manager")}>Manager</Button>}
                  <Button variant="danger" icon="x" disabled={!!busy} onClick={() => decide(u, "reject")}>Refuser</Button>
                </div>
              </div>
            ))}
          </div>}
    </div>
  );
}

Object.assign(window, { AdminTeam, AdminRoles, AdminIPhones, AdminRequests, InviteModal, Toggle, PageHead, Toolbar, Table, Th, Td, RowActions });
