/* ============================================================
   drive.jsx — Drive-based ingestion (Finance-first Step 1).
   Connect a Google Drive folder, list ALL its files (PDF/Excel/image),
   select the debit-note PDFs, and push them into the Lekha pipeline.
   Reads use the logged-in finance user's drive.readonly token (browser-side;
   no client secret). Backend only stores the folder + ingests uploaded PDFs.
   ============================================================ */

const DRIVE_SCOPE = "https://www.googleapis.com/auth/drive.readonly";
const FOLDER_MIME = "application/vnd.google-apps.folder";

// --- Drive access token (GIS token client) ---------------------------------
// Persisted in sessionStorage so a PAGE REFRESH reuses the still-valid token (~1h)
// and the documents load immediately — no popup, no click. sessionStorage is
// per-tab and clears when the tab closes, so it's not stored long-term. Only when
// the token is missing/expired do we request a new one (silent first; interactive
// = a user click, which can pop the consent window).
const _TOK_KEY = "lekha_drive_tok", _TOK_EXP = "lekha_drive_tok_exp";
let _driveTok = null, _driveTokExp = 0;
try {
  _driveTok = sessionStorage.getItem(_TOK_KEY) || null;
  _driveTokExp = parseInt(sessionStorage.getItem(_TOK_EXP) || "0", 10) || 0;
} catch (_) {}

function _saveDriveTok(tok, expiresIn) {
  _driveTok = tok;
  _driveTokExp = Date.now() + ((expiresIn || 3000) - 60) * 1000;
  try {
    sessionStorage.setItem(_TOK_KEY, tok);
    sessionStorage.setItem(_TOK_EXP, String(_driveTokExp));
  } catch (_) {}
}

// Forget the cached Drive token so the NEXT connect re-prompts (account chooser) — used
// to switch to a different Google account.
function clearDriveToken() {
  _driveTok = null; _driveTokExp = 0;
  try { sessionStorage.removeItem(_TOK_KEY); sessionStorage.removeItem(_TOK_EXP); } catch (_) {}
}

function getDriveToken(clientId, interactive) {
  if (_driveTok && Date.now() < _driveTokExp) return Promise.resolve(_driveTok);  // reused across refresh
  // DESKTOP: Google blocks OAuth in the webview → native system-browser flow (Rust) that
  // returns a drive.readonly access token via the backend exchange.
  if (window.isDesktopApp && window.isDesktopApp()) {
    return window.desktopGoogleOAuth("openid email " + DRIVE_SCOPE).then((t) => {
      if (t && t.access_token) { _saveDriveTok(t.access_token, t.expires_in); return _driveTok; }
      throw new Error("no drive token");
    });
  }
  return new Promise((resolve, reject) => {
    const g = window.google && google.accounts && google.accounts.oauth2;
    if (!g) return reject(new Error("Google sign-in script not loaded yet"));
    const client = google.accounts.oauth2.initTokenClient({
      client_id: clientId,
      scope: DRIVE_SCOPE,
      callback: (resp) => {
        if (resp && resp.access_token) {
          _saveDriveTok(resp.access_token, resp.expires_in);
          resolve(_driveTok);
        } else { reject(resp || new Error("no token")); }
      },
      error_callback: (err) => reject(err || new Error("token error")),
    });
    // interactive → show the account chooser so a different Google account can be picked
    client.requestAccessToken({ prompt: interactive ? "select_account" : "none" });
  });
}

// --- Drive REST calls (browser, Bearer token; googleapis supports CORS) -----
async function driveList(token, q, fields) {
  const url = "https://www.googleapis.com/drive/v3/files?q=" + encodeURIComponent(q) +
    "&fields=" + encodeURIComponent("files(" + fields + ")") +
    "&pageSize=200&orderBy=folder,name&supportsAllDrives=true&includeItemsFromAllDrives=true";
  const r = await fetch(url, { headers: { Authorization: "Bearer " + token } });
  if (!r.ok) throw new Error("Drive list failed (" + r.status + ")");
  return (await r.json()).files || [];
}
const listFolders = (token, parent) =>
  driveList(token, `'${parent}' in parents and mimeType='${FOLDER_MIME}' and trashed=false`,
    "id,name");
const listFiles = (token, folder) =>
  driveList(token, `'${folder}' in parents and trashed=false`,
    "id,name,mimeType,modifiedTime");

async function driveDownload(token, fileId) {
  const r = await fetch("https://www.googleapis.com/drive/v3/files/" + fileId +
    "?alt=media&supportsAllDrives=true", { headers: { Authorization: "Bearer " + token } });
  if (!r.ok) throw new Error("Download failed (" + r.status + ")");
  return await r.blob();
}

async function ingestToLekha(blob, fileId, fileName) {
  const fd = new FormData();
  fd.append("file", new File([blob], fileName, { type: "application/pdf" }));
  fd.append("file_id", fileId);
  fd.append("file_name", fileName);
  const r = await fetch("/api/drive/ingest", {
    method: "POST",
    headers: { Authorization: "Bearer " + (localStorage.getItem("lekha_token") || "") },
    body: fd,
  });
  if (!r.ok) {
    let msg = "Import failed";
    try { msg = (await r.json()).detail || msg; } catch (_) {}
    throw new Error(msg);
  }
  return await r.json();
}

const isPdf = (f) => (f.mimeType === "application/pdf") || /\.pdf$/i.test(f.name || "");
function fileKind(f) {
  if (isPdf(f)) return "PDF";
  const m = f.mimeType || "";
  if (/sheet|excel|spreadsheet/i.test(m) || /\.(xlsx?|csv)$/i.test(f.name)) return "Excel";
  if (/image\//.test(m) || /\.(png|jpe?g|gif|webp|tiff?)$/i.test(f.name)) return "Image";
  return "Other";
}

// --- folder browser (no Picker API needed; pure Drive REST) ----------------
function FolderBrowser({ clientId, onPick, onClose }) {
  const [stack, setStack] = React.useState([{ id: "root", name: "My Drive" }]);
  const [folders, setFolders] = React.useState([]);
  const [busy, setBusy] = React.useState(true);
  const [err, setErr] = React.useState("");
  const here = stack[stack.length - 1];

  const load = async (parent) => {
    setBusy(true); setErr("");
    try {
      const tok = await getDriveToken(clientId, true);
      setFolders(await listFolders(tok, parent));
    } catch (e) { setErr(e.message || "Could not read Drive"); }
    finally { setBusy(false); }
  };
  React.useEffect(() => { load(here.id); }, [here.id]);

  return (
    <div style={{ position: "fixed", inset: 0, background: "rgba(0,0,0,.45)", display: "grid", placeItems: "center", zIndex: 50 }}>
      <div className="card card-pad" style={{ width: "min(560px,92vw)", maxHeight: "80vh", display: "flex", flexDirection: "column" }}>
        <div className="row" style={{ justifyContent: "space-between", alignItems: "center", marginBottom: 10 }}>
          <div style={{ fontWeight: 800, fontSize: 16 }}>Choose your documents folder</div>
          <button className="btn btn-ghost btn-sm" onClick={onClose}>Close</button>
        </div>
        <div className="tiny" style={{ marginBottom: 10 }}>
          {stack.map((s, i) => (
            <span key={s.id}>
              {i > 0 && <span style={{ color: "var(--text-3)" }}> / </span>}
              <a style={{ cursor: "pointer", color: "var(--lime-ink)" }}
                 onClick={() => setStack(stack.slice(0, i + 1))}>{s.name}</a>
            </span>
          ))}
        </div>
        <div className="stack gap8" style={{ overflow: "auto", flex: 1, minHeight: 120 }}>
          {busy && <div className="tiny">Loading…</div>}
          {err && <div className="hint"><Ic.x /><div>{err}</div></div>}
          {!busy && !err && folders.length === 0 && <div className="tiny">No sub-folders here.</div>}
          {folders.map((f) => (
            <button key={f.id} className="row gap10" style={{ alignItems: "center", textAlign: "left", padding: "9px 11px", borderRadius: 10, border: "1.5px solid var(--border)", background: "#fff", cursor: "pointer" }}
              onClick={() => setStack([...stack, { id: f.id, name: f.name }])}>
              <Ic.folder /><span style={{ fontWeight: 600, fontSize: 14 }}>{f.name}</span>
              <span className="tiny" style={{ marginLeft: "auto", color: "var(--text-3)" }}>open ›</span>
            </button>
          ))}
        </div>
        <div className="row" style={{ justifyContent: "flex-end", marginTop: 12 }}>
          <button className="btn btn-primary" disabled={here.id === "root"}
            onClick={() => onPick(here)}>
            Use “{here.name}”
          </button>
        </div>
      </div>
    </div>
  );
}

// Human labels for Lekha's categories. `unclassified` ≠ `unrecognised`:
// unrecognised = Gemini decided it's not a finance doc; unclassified = Gemini
// didn't run (no key / failure) — surfaced so you can see the intelligence is off.
const CAT_LABEL = {
  debit_note: "Debit note", credit_note: "Credit note", invoice: "Invoice",
  sales_register: "Sales register", bank_statement: "Bank statement",
  purchase_invoice: "Purchase invoice", unrecognised: "Unrecognised",
  unclassified: "Couldn’t read — retry",
};

// Files are grouped by category; debit notes lead (they're the actionable ones).
const GROUP_ORDER = ["debit_note", "credit_note", "invoice", "purchase_invoice",
  "sales_register", "bank_statement", "unrecognised", "unclassified",
  "__reading", "__other"];
const GROUP_TITLE = {
  debit_note: "Debit notes", credit_note: "Credit notes", invoice: "Invoices",
  purchase_invoice: "Purchase invoices", sales_register: "Sales registers",
  bank_statement: "Bank statements", unrecognised: "Unrecognised",
  unclassified: "Couldn’t read (retry)", __reading: "Reading…",
  __other: "Other files (view only)",
};

// shared rounded-pill style for the row status badges
const pill = (bg, fg, bd) => ({
  display: "inline-flex", alignItems: "center", gap: 5, background: bg, color: fg,
  border: "1px solid " + bd, borderRadius: 999, padding: "3px 11px",
  fontSize: 11.5, fontWeight: 700, whiteSpace: "nowrap",
});

async function classifyToLekha(blob, f, force) {
  const fd = new FormData();
  fd.append("file", new File([blob], f.name, { type: "application/pdf" }));
  fd.append("file_id", f.id); fd.append("file_name", f.name);
  fd.append("modified_time", f.modifiedTime || "");
  if (force) fd.append("force", "true");
  const r = await fetch("/api/drive/classify", { method: "POST",
    headers: { Authorization: "Bearer " + (localStorage.getItem("lekha_token") || "") }, body: fd });
  if (!r.ok) throw new Error("classify failed");
  return await r.json();   // {category, ingestible, reason, retryable}
}

// --- main: connect state + file list + classify + select & import ----------
function DriveDocs({ who }) {
  const [clientId, setClientId] = React.useState("");
  const [status, setStatus] = React.useState(null);
  const [files, setFiles] = React.useState(null);
  const [cats, setCats] = React.useState({});          // fileId -> {category, ingestible, reason}
  const [reading, setReading] = React.useState({});    // fileId -> true while classifying
  const [sel, setSel] = React.useState({});
  const [browsing, setBrowsing] = React.useState(false);
  const [busy, setBusy] = React.useState(false);
  const [msg, setMsg] = React.useState("");
  const [err, setErr] = React.useState("");
  const [openGroups, setOpenGroups] = React.useState({ debit_note: true, invoice: true });  // ingestible groups open by default
  const [needsAuth, setNeedsAuth] = React.useState(false);  // token gone after reload → one-click reconnect

  const refreshStatus = () => api.get("/api/drive/status").then((s) => {
    setStatus(s); setCats((c) => ({ ...(s.classifications || {}), ...c })); return s;
  }).catch(() => {});
  React.useEffect(() => {
    api.get("/api/auth/config").then((c) => setClientId(c.google_client_id || "")).catch(() => {});
    refreshStatus();
  }, []);

  // Lekha's intelligence: download each not-yet-classified PDF and classify it
  // (sequential — gentle on the API). Cached server-side, so this is one-time.
  const classifyOne = async (token, f, force) => {
    setReading((r) => ({ ...r, [f.id]: true }));
    try {
      const blob = await driveDownload(token, f.id);
      const v = await classifyToLekha(blob, f, force);
      setCats((c) => ({ ...c, [f.id]: v }));
    } catch (_) {
      setCats((c) => ({ ...c, [f.id]: { category: "unclassified", ingestible: false, reason: "could not read", retryable: true } }));
    } finally {
      setReading((r) => { const n = { ...r }; delete n[f.id]; return n; });
    }
  };

  // Read-once: classify ONLY files we haven't seen (new id) or that changed
  // (new modifiedTime). Everything already classified is served from cache.
  const needsRead = (f, known) => {
    if (!isPdf(f)) return false;
    const k = known[f.id];
    return !k || (k.modified_time || "") !== (f.modifiedTime || "");
  };

  const loadFiles = async (st, interactive) => {
    if (!st || !st.connected || !clientId) return;
    setBusy(true); setErr("");
    let tok;
    try {
      tok = await getDriveToken(clientId, interactive);
    } catch (_) {
      setBusy(false); setNeedsAuth(true);              // token needs a user click
      return;
    }
    setNeedsAuth(false);
    try {
      const list = await listFiles(tok, st.folder_id);
      setFiles(list);
      const known = st.classifications || {};
      for (const f of list) {                          // only the new / changed ones
        if (needsRead(f, known)) await classifyOne(tok, f);
      }
    } catch (e) { setErr(e.message || "Could not read folder"); }
    finally { setBusy(false); }
  };

  const retryOne = async (f) => {                      // force re-read one file
    if (!clientId) return;
    try { await classifyOne(await getDriveToken(clientId, true), f, true); }
    catch (e) { setErr(e.message || "Retry failed"); }
  };
  // On load, try a SILENT token; if it needs interaction we show a button.
  React.useEffect(() => { if (status && status.connected && clientId) loadFiles(status, false); }, [status && status.folder_id, clientId]);

  const pickFolder = async (folder) => {
    setBrowsing(false); setBusy(true); setErr(""); setMsg("");
    try {
      const st = await api.req("PUT", "/api/drive/folder",
        { folder_id: folder.id, folder_name: folder.name });
      setFiles(null); setSel({});
      await refreshStatus();
    } catch (e) { setErr(e.message || "Could not save folder"); }
    finally { setBusy(false); }
  };

  const isIngestible = (f) => cats[f.id] && cats[f.id].ingestible;   // debit notes + sales invoices

  const importSelected = async () => {
    const chosen = (files || []).filter((f) => sel[f.id] && isIngestible(f));
    if (!chosen.length) return;
    setBusy(true); setErr(""); setMsg("");
    let ok = 0;
    try {
      const tok = await getDriveToken(clientId, true);
      for (const f of chosen) {
        const blob = await driveDownload(tok, f.id);
        await ingestToLekha(blob, f.id, f.name);
        ok++;
      }
      setMsg(`Imported ${ok} document${ok === 1 ? "" : "s"} — now with Lekha for review. If any issues or discrepancies, Lekha will reach out here.`);
      setSel({});
      await refreshStatus();
    } catch (e) { setErr(`Imported ${ok}. ${e.message || "Import stopped."}`); }
    finally { setBusy(false); }
  };

  // --- not connected: single center-aligned CTA ---
  if (status && !status.connected) {
    return (
      <>
        {browsing && <FolderBrowser clientId={clientId} onPick={pickFolder} onClose={() => setBrowsing(false)} />}
        <div style={{ display: "grid", placeItems: "center", minHeight: "52vh", textAlign: "center" }}>
          <div>
            <div style={{ fontFamily: "var(--serif)", fontWeight: 800, fontSize: 24, marginBottom: 6 }}>Connect your Drive</div>
            <div className="tiny" style={{ maxWidth: 380, margin: "0 auto 20px" }}>
              Pick the Google Drive folder that holds your documents. Lekha will list
              them here so you can send your debit notes &amp; sales invoices through for review.
            </div>
            <button className="btn btn-primary btn-lg" disabled={!clientId || busy}
              onClick={() => setBrowsing(true)}>
              <Ic.folder /> Connect your Drive
            </button>
            {!clientId && <div className="tiny" style={{ marginTop: 12, color: "var(--text-3)" }}>Google connection isn’t configured.</div>}
            {err && <div className="hint" style={{ marginTop: 14, justifyContent: "center" }}><Ic.x /><div>{err}</div></div>}
          </div>
        </div>
      </>
    );
  }

  // --- connected: folder header + grouped, collapsible file list ---
  const list = files || [];
  const ingested = new Set((status && status.ingested_file_ids) || []);
  const selectableCount = list.filter((f) => sel[f.id] && isIngestible(f) && !ingested.has(f.id)).length;
  const stillReading = Object.keys(reading).length;

  const groupOf = (f) => {
    if (!isPdf(f)) return "__other";
    if (reading[f.id] || (!cats[f.id] && !ingested.has(f.id))) return "__reading";
    return (cats[f.id] && cats[f.id].category) || "__reading";
  };
  const groups = {};
  for (const f of list) (groups[groupOf(f)] ||= []).push(f);
  const shownGroups = GROUP_ORDER.filter((g) => groups[g] && groups[g].length);
  const readyCount = list.filter((f) => isIngestible(f) && !ingested.has(f.id)).length;
  const toggle = (g) => setOpenGroups((o) => ({ ...o, [g]: !o[g] }));

  const renderRow = (f) => {
    const pdf = isPdf(f), done = ingested.has(f.id), cat = cats[f.id];
    const isReading = !!reading[f.id] || (pdf && !cat && !done);
    const ing = pdf && ((cat && cat.ingestible) || done);
    const selectable = ing && !done && !isReading;
    let badge;
    if (done) badge = <span style={pill("#eef6dd", "#3f6212", "var(--lime-deep)")}><Ic.check /> Imported</span>;
    else if (isReading) badge = <span style={pill("#fff7e6", "#9a6700", "#f0d9a8")}><span className="lna-flow" style={{ width: 7, height: 7, borderRadius: 999, background: "#c98a00" }} /> Reading…</span>;
    else if (pdf && cat && cat.category === "unclassified") badge = <button type="button" title={cat.reason || ""} style={{ ...pill("#fff3f0", "#b23b1e", "#f0cfc9"), cursor: "pointer" }} onClick={(e) => { e.preventDefault(); e.stopPropagation(); retryOne(f); }}><Ic.refresh /> Retry</button>;
    else if (selectable) badge = <span style={pill("#eef6dd", "#3f6212", "var(--lime-deep)")}>Ready</span>;
    else badge = <span style={pill("var(--bg)", "var(--text-3)", "var(--border)")}>View only</span>;
    return (
      <label key={f.id} className="row" style={{ alignItems: "center", gap: 12, padding: "12px 14px", borderTop: "1px solid var(--border)", cursor: selectable ? "pointer" : "default", background: sel[f.id] ? "var(--lime)" : "transparent", opacity: (pdf && (ing || isReading)) ? 1 : 0.62 }}>
        <input type="checkbox" disabled={!selectable} checked={!!sel[f.id]}
          onChange={(e) => setSel({ ...sel, [f.id]: e.target.checked })}
          style={{ width: 18, height: 18, accentColor: "var(--lime-deep)", cursor: selectable ? "pointer" : "default", flex: "0 0 auto" }} />
        <span style={{ minWidth: 0, flex: 1 }}>
          <span style={{ display: "block", fontWeight: 600, fontSize: 14, whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>{f.name}</span>
          <span className="tiny" style={{ color: "var(--text-3)" }}>{fileKind(f)}{f.modifiedTime ? " · " + new Date(f.modifiedTime).toLocaleDateString() : ""}</span>
        </span>
        {badge}
      </label>
    );
  };

  return (
    <>
      {browsing && <FolderBrowser clientId={clientId} onPick={pickFolder} onClose={() => setBrowsing(false)} />}
      <div className="page-head" style={{ alignItems: "center" }}>
        <div>
          <div className="ph-title">Documents from Drive</div>
          <div className="ph-sub">Folder: <b>{(status && status.folder_name) || "—"}</b> · Lekha reads each file; tap a section to open it.</div>
        </div>
        <div className="row gap8">
          <button className="btn btn-sm" title="Sign in with a different Google account"
            onClick={() => { clearDriveToken(); setFiles(null); loadFiles(status, true); }}>Change account</button>
          <button className="btn btn-sm" onClick={() => setBrowsing(true)}>Change folder</button>
        </div>
      </div>

      {/* token gone after page reload → one click to view (no folder re-pick) */}
      {needsAuth && (
        <div className="card card-pad mb12" style={{ display: "flex", alignItems: "center", gap: 14, background: "var(--lime)", border: "1.5px solid var(--lime-deep)" }}>
          <Ic.folder />
          <div style={{ flex: 1 }}>
            <div style={{ fontWeight: 700, color: "var(--lime-ink)" }}>Show your documents</div>
            <div className="tiny" style={{ color: "var(--lime-ink)" }}>Reconnect to Google Drive to load <b>{(status && status.folder_name) || "your folder"}</b> — your folder choice is saved.</div>
          </div>
          <button className="btn btn-primary btn-sm" disabled={busy} onClick={() => loadFiles(status, true)}>Show my documents</button>
        </div>
      )}

      {/* at-a-glance summary */}
      <div className="card card-pad mb12" style={{ display: "flex", gap: 22, alignItems: "center", flexWrap: "wrap" }}>
        <div><div style={{ fontWeight: 800, fontSize: 20, color: "var(--lime-ink)" }}>{readyCount}</div><div className="tiny" style={{ color: "var(--text-3)" }}>ready to import</div></div>
        <div><div style={{ fontWeight: 800, fontSize: 20 }}>{list.length}</div><div className="tiny" style={{ color: "var(--text-3)" }}>files in folder</div></div>
        {stillReading > 0 && <div><div style={{ fontWeight: 800, fontSize: 20 }}>{stillReading}</div><div className="tiny" style={{ color: "var(--text-3)" }}>Lekha is reading…</div></div>}
        <div style={{ marginLeft: "auto", display: "flex", gap: 10 }}>
          <button className="btn btn-primary btn-sm" disabled={busy || !selectableCount} onClick={importSelected}>
            {busy ? "Working…" : `Import${selectableCount ? ` (${selectableCount})` : ""}`}
          </button>
          <button className="btn btn-ghost btn-sm" disabled={busy} onClick={() => loadFiles(status, true)}>Refresh</button>
        </div>
      </div>

      {msg && <div className="card card-pad mb12" style={{ background: "var(--lime)", border: "1.5px solid var(--lime-deep)", color: "var(--lime-ink)", fontWeight: 600 }}>{msg}</div>}
      {err && <div className="hint mb12"><Ic.x /><div>{err}</div></div>}

      {busy && !files && <div className="card card-pad tiny">Loading folder…</div>}
      {files && files.length === 0 && <div className="card card-pad tiny">This folder is empty.</div>}

      {/* collapsible category sections — closed by default (debit notes open) */}
      <div className="stack gap10">
        {shownGroups.map((g) => {
          const open = !!openGroups[g], isDN = g === "debit_note" || g === "invoice";   // ingestible groups
          return (
            <div key={g} className="card" style={{ overflow: "hidden", border: isDN ? "1.5px solid var(--lime-deep)" : "1px solid var(--border)" }}>
              <button type="button" onClick={() => toggle(g)}
                style={{ width: "100%", display: "flex", alignItems: "center", gap: 10, padding: "13px 14px", background: isDN ? "var(--lime)" : "var(--card,#fff)", border: "none", cursor: "pointer", textAlign: "left" }}>
                <span style={{ display: "inline-flex", transform: open ? "rotate(90deg)" : "none", transition: "transform .15s", color: isDN ? "var(--lime-ink)" : "var(--text-2)" }}><Ic.chevron /></span>
                <span style={{ fontWeight: 800, fontSize: 14, color: isDN ? "var(--lime-ink)" : "var(--text-1)" }}>{GROUP_TITLE[g] || g}</span>
                <span style={{ ...pill(isDN ? "var(--lime-deep)" : "var(--bg)", isDN ? "#fff" : "var(--text-2)", isDN ? "var(--lime-deep)" : "var(--border)"), fontSize: 11, padding: "1px 9px", minWidth: 18, justifyContent: "center" }}>{groups[g].length}</span>
                {isDN && <span className="tiny" style={{ color: "var(--lime-ink)" }}>· tap to select & import</span>}
                <span style={{ marginLeft: "auto", display: "inline-flex", alignItems: "center", gap: 5, fontSize: 11, fontWeight: 700, color: "var(--text-2)", background: "var(--bg)", border: "1px solid var(--border)", borderRadius: 999, padding: "3px 11px" }}>
                  <Ic.chevron style={{ transform: open ? "rotate(90deg)" : "none", width: 11, height: 11 }} />{open ? "Hide" : "Show"}
                </span>
              </button>
              {open && <div>{groups[g].map(renderRow)}</div>}
            </div>
          );
        })}
      </div>
    </>
  );
}

Object.assign(window, { DriveDocs });
