/* screen-export.jsx — the configurable Export screen (PLAN §2). Replaces the
 * old DownloadSheet. Filter pills (client / project / status / type / date
 * basis+range / currency) drive LIB.selectInvoices; a reviewable checkbox list
 * lets the owner hand-pick exact documents; a sticky footer shows the
 * per-currency LIB.selectionSummary total + a format-aware CTA. Real formats
 * (CSV ledger, Payments CSV, Accountant pack, Print selected) wire through LIB
 * + Q.downloadFile / api.printInvoice; Merged PDF / ZIP are greyed coming-soon.
 *
 * Money wears gold; nothing here is AI (no sage). Light mode.
 * → window.QScreens.Export
 */
(function () {
  const { useState, useMemo } = React;
  const Q = window.Q, LIB = window.LIB, Icons = window.Icons;
  const { Icon } = Icons;
  const { AppBar, Card, Money, Kicker, Button, StatusPill, Avatar, EmptyState, IconButton, Sheet, Row, Segmented } = Q;
  const t = window.t;

  /* Local YYYY-MM-DD (display-only; the pinned api.today, never wall clock). */
  function dayStr(d) {
    const x = new Date(d);
    const p = n => String(n).padStart(2, "0");
    return x.getFullYear() + "-" + p(x.getMonth() + 1) + "-" + p(x.getDate());
  }

  /* The status values effectiveStatus can yield for an INVOICES-tab row
     (invoices + credits; quotes live on their own tab and aren't a default
     export type). draft/sent/partial/paid/overdue mirror the list chips;
     'issued' is the credit-note effective status. */
  const STATUS_OPTS = ["draft", "sent", "partial", "paid", "overdue", "issued"];
  const TYPE_OPTS = ["invoice", "credit", "quote"];
  /* Doc-kind labels reuse the keys the rest of the app already ships (the A4
     header / manual-type picker), so a "credit" reads "Credit note" / "Avoir". */
  const TYPE_KEY = { invoice: "man.type.invoice", credit: "paper.avoir", quote: "man.type.quote" };

  /* A small generic multi-select sheet: a list of {value,label} rows with a
     check, plus an "All" reset row. Returns the chosen array (empty = all). */
  function MultiPicker({ open, onClose, title, options, selected, onChange, allLabel }) {
    const sel = selected || [];
    const has = v => sel.indexOf(v) >= 0;
    const toggle = v => onChange(has(v) ? sel.filter(x => x !== v) : [...sel, v]);
    return (
      <Sheet open={open} onClose={onClose} height="auto" title={title}>
        <div style={{ padding: "2px 8px 24px" }}>
          <Card pad={0} style={{ overflow: "hidden" }}>
            <Row onClick={() => { onChange([]); onClose(); }} last={!options.length}>
              <div style={{ flex: 1, fontSize: 14, fontWeight: 600, color: sel.length ? "var(--color-text-1)" : "var(--color-primary)" }}>{allLabel}</div>
              {!sel.length && <Icon name="check" size={18} color="var(--color-primary)" />}
            </Row>
            {options.map((o, i) => (
              <Row key={o.value} onClick={() => toggle(o.value)} last={i === options.length - 1}>
                <div style={{ flex: 1, fontSize: 14, fontWeight: 600 }}>{o.label}</div>
                {has(o.value) && <Icon name="check" size={18} color="var(--color-primary)" />}
              </Row>
            ))}
          </Card>
        </div>
      </Sheet>
    );
  }

  /* Single-select sheet (client / project / currency). value null = "All …". */
  function SinglePicker({ open, onClose, title, options, value, onChange, allLabel }) {
    const pick = v => { onChange(v); onClose(); };
    return (
      <Sheet open={open} onClose={onClose} height="auto" title={title}>
        <div style={{ padding: "2px 8px 24px" }}>
          <Card pad={0} style={{ overflow: "hidden" }}>
            <Row onClick={() => pick(null)} last={!options.length}>
              <div style={{ flex: 1, fontSize: 14, fontWeight: 600, color: value == null ? "var(--color-primary)" : "var(--color-text-1)" }}>{allLabel}</div>
              {value == null && <Icon name="check" size={18} color="var(--color-primary)" />}
            </Row>
            {options.map((o, i) => (
              <Row key={o.value} onClick={() => pick(o.value)} last={i === options.length - 1}>
                {o.avatar && <Avatar name={o.label} size={30} />}
                <div style={{ flex: 1, minWidth: 0, fontSize: 14, fontWeight: 600, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{o.label}</div>
                {value === o.value && <Icon name="check" size={18} color="var(--color-primary)" />}
              </Row>
            ))}
          </Card>
        </div>
      </Sheet>
    );
  }

  /* A filter pill: shows its active value, opens its picker. Active (non-default)
     pills get a primary-tinted border so the owner sees what's narrowing. */
  function Pill({ icon, label, active, disabled, onClick }) {
    return (
      <button onClick={disabled ? undefined : onClick} disabled={disabled} className="q-tap" style={{
        display: "inline-flex", alignItems: "center", gap: 6, height: 36, padding: "0 12px",
        borderRadius: 999, cursor: disabled ? "not-allowed" : "pointer", whiteSpace: "nowrap",
        fontFamily: "var(--font-sans)", fontSize: 13, fontWeight: 600, maxWidth: "100%",
        background: active ? "color-mix(in oklab, var(--color-primary) 12%, transparent)" : "var(--color-surface-0)",
        border: "1.4px solid " + (active ? "var(--color-primary)" : "var(--color-border-strong)"),
        color: active ? "var(--color-primary)" : "var(--color-text-2)", opacity: disabled ? 0.45 : 1,
      }}>
        {icon && <Icon name={icon} size={15} />}
        <span style={{ overflow: "hidden", textOverflow: "ellipsis" }}>{label}</span>
        {!disabled && <Icon name="chevronDown" size={14} style={{ opacity: 0.7 }} />}
      </button>
    );
  }

  function Export({ api, seed }) {
    const { db, today } = api;
    const s = seed || {};
    const homeCur = (db.COMPANY && db.COMPANY.currency) || "EUR";

    /* ── Filter state (seeded from the seed prop; every key optional) ─── */
    const [clientId, setClientId] = useState(s.clientId || null);
    const [projectId, setProjectId] = useState(s.projectId || null);
    const [status, setStatus] = useState(Array.isArray(s.status) ? s.status : []);
    const [types, setTypes] = useState(Array.isArray(s.types) ? s.types : []);
    const [basis, setBasis] = useState(s.basis === "paid" ? "paid" : "issued");
    const [range, setRange] = useState(() => {
      if (s.from || s.to) return { key: "custom", from: s.from || null, to: s.to || null };
      return { key: "all", from: null, to: null };
    });
    const [currency, setCurrency] = useState(s.currency || null);
    /* Hand-pick override: ids the user has TICKED. null = "follow the filter"
       (everything selected); an array = the explicit ticked set. */
    const [picked, setPicked] = useState(Array.isArray(s.ids) ? s.ids.slice() : null);
    const [format, setFormat] = useState("csv");
    const [open, setOpen] = useState(null); // which picker sheet is open

    /* Range presets: an "All" reset (dl.scope.all — the family the contract
       tells us to render), then the quarters-of-this-year + YTD that
       LIB.taxPeriods already labels (tax.period.q / tax.period.ytd), so the
       Export date presets read identically to the Taxes screen. A seeded
       custom span (Taxes deep-links pass {from,to}) shows as a synthetic row. */
    const rangePresets = useMemo(() => {
      const taxPs = LIB.taxPeriods(today).map(p => ({
        key: p.key, from: p.from, to: p.to,
        label: p.key === "ytd" ? t("tax.period.ytd") : t("tax.period.q", p.labelParams),
      }));
      return [{ key: "all", label: t("dl.scope.all"), from: null, to: null }, ...taxPs];
    }, [today]);

    /* The query the engine consumes. status/types empty array → null (no
       constraint), so an empty multi-select means "all", never "none". */
    const q = {
      clientId, projectId,
      status: status.length ? status : null,
      types: types.length ? types : null,
      basis, from: range.from, to: range.to, currency, today,
    };
    /* The full filter match (no hand-pick). picked intersects on top. */
    const matches = useMemo(() => LIB.selectInvoices(db, q),
      [db, clientId, projectId, status.join(","), types.join(","), basis, range.from, range.to, currency, today]);
    const matchIds = useMemo(() => matches.map(i => i.id), [matches]);

    /* Resolve picked against the current matches: a ticked id that no longer
       matches is dropped silently. null picked = all matches ticked. */
    const selectedIds = picked == null ? matchIds : matchIds.filter(id => picked.indexOf(id) >= 0);
    const selectedSet = useMemo(() => new Set(selectedIds), [selectedIds.join(",")]);
    const selectedDocs = matches.filter(i => selectedSet.has(i.id));

    const sum = LIB.selectionSummary(db, selectedDocs, today);
    const summaryStr = Object.keys(sum.byCurrency).length
      ? Object.keys(sum.byCurrency).map(c => window.fmtMoney(sum.byCurrency[c], c, { cents: false })).join(" · ")
      : window.fmtMoney(0, homeCur, { cents: false });

    const allTicked = matchIds.length > 0 && selectedIds.length === matchIds.length;

    /* ── Filter helpers ─────────────────────────────────────────────── */
    function clearAll() {
      setClientId(null); setProjectId(null); setStatus([]); setTypes([]);
      setBasis("issued"); setRange({ key: "all", from: null, to: null });
      setCurrency(null); setPicked(null);
    }
    const anyFilter = clientId || projectId || status.length || types.length || range.from || range.to || currency;

    /* Selecting a client clears any project that no longer belongs to it. */
    function chooseClient(id) {
      setClientId(id);
      if (id !== clientId) setProjectId(null);
      setPicked(null);
    }
    /* Any filter change resets the hand-pick (the ticked set was built against
       a different match set). */
    function onFilter(fn) { return (...a) => { fn(...a); setPicked(null); }; }

    function toggleRow(id) {
      const base = picked == null ? matchIds.slice() : matchIds.filter(x => (picked.indexOf(x) >= 0));
      const next = base.indexOf(id) >= 0 ? base.filter(x => x !== id) : [...base, id];
      setPicked(next);
    }
    function selectAll() { setPicked(null); }            // null = all matches
    function deselectAll() { setPicked([]); }            // empty = none

    /* ── Options for the pickers ────────────────────────────────────── */
    const clients = (db.CLIENTS || []).slice().sort((a, b) => a.name.localeCompare(b.name));
    const clientOpts = clients.map(c => ({ value: c.id, label: c.name, avatar: true }));
    const activeClient = clientId ? clients.find(c => c.id === clientId) : null;
    const projectOpts = activeClient ? (activeClient.projects || []).map(p => ({ value: p.id, label: p.name })) : [];
    const activeProject = projectId && activeClient ? (activeClient.projects || []).find(p => p.id === projectId) : null;
    const statusOpts = STATUS_OPTS.map(v => ({ value: v, label: window.t("st." + v) }));
    const typeOpts = TYPE_OPTS.map(v => ({ value: v, label: window.t(TYPE_KEY[v]) }));
    const curList = (db.config && db.config.currencies) || Object.values(window.CURRENCIES || {});
    const currencyOpts = curList.map(c => ({ value: c.code, label: c.code + " · " + (c.name || c.symbol || c.code) }));

    /* Pill labels (active value, or the dimension name when at default). */
    const clientLabel = activeClient ? activeClient.name : t("export.filter.client");
    const projectLabel = activeProject ? activeProject.name : t("export.filter.project");
    const statusLabel = status.length ? status.map(v => window.t("st." + v)).join(", ") : t("export.filter.status");
    const typeLabel = types.length ? types.map(v => window.t(TYPE_KEY[v])).join(", ") : t("export.filter.type");
    const rangeLabel = (rangePresets.find(p => p.key === range.key) || {}).label
      || (range.from || range.to ? (range.from ? window.fmtDate(range.from, "short") : "…") + " – " + (range.to ? window.fmtDate(range.to, "short") : "…") : t("export.filter.date"));
    const currencyLabel = currency || t("export.filter.currency");

    /* ── Formats ────────────────────────────────────────────────────── */
    const needRange = !(range.from && range.to);
    const FORMATS = [
      { id: "csv", label: t("export.format.csv"), sub: t("export.csv.invoices"), icon: "file" },
      { id: "payments", label: t("export.format.payments"), sub: t("export.csv.payments"), icon: "coins" },
      { id: "pack", label: t("export.format.pack"), sub: t("export.pack.sub"), icon: "folder", needRange: true },
      { id: "print", label: t("export.format.print"), sub: t("det.viewpdf"), icon: "eye" },
      { id: "merged", label: t("export.format.merged"), sub: t("export.format.soon"), icon: "copy", soon: true },
    ];
    const activeFormat = FORMATS.find(f => f.id === format) || FORMATS[0];

    /* Disable the CTA when nothing is selected, or the pack lacks a range. */
    const noSelection = selectedIds.length === 0;
    const packBlocked = format === "pack" && needRange;
    const ctaDisabled = noSelection || packBlocked;
    const PRINT_CAP = 5;

    function runExport() {
      if (ctaDisabled) return;
      const ids = selectedIds;
      if (format === "csv") {
        Q.downloadFile("invoices-" + dayStr(today) + ".csv", LIB.csvInvoices(db, { today, ids }), "text/csv;charset=utf-8");
        Q.toast(t("dl.done"), "download");
      } else if (format === "payments") {
        Q.downloadFile("payments-" + dayStr(today) + ".csv", LIB.csvPayments(db, { ids, from: range.from, to: range.to }), "text/csv;charset=utf-8");
        Q.toast(t("dl.done"), "download");
      } else if (format === "pack") {
        const files = LIB.accountantPack(db, { from: range.from, to: range.to, today });
        files.forEach((f, i) => setTimeout(() => Q.downloadFile(f.name, f.content, f.mime), i * 350));
        setTimeout(() => Q.toast(t("toast.packExported", { count: files.length }), "download"), files.length * 350);
      } else if (format === "print") {
        /* Loop the existing single-invoice print path, one document at a time;
           stagger so each print dialog resolves before the next mounts. */
        const toPrint = ids.slice(0, PRINT_CAP);
        toPrint.forEach((id, i) => setTimeout(() => api.printInvoice(id), i * 1200));
      }
    }

    /* ── Render ─────────────────────────────────────────────────────── */
    const ctaLabel = t("export.cta", { count: selectedIds.length, format: activeFormat.label });

    return (
      <div style={{ display: "flex", flexDirection: "column", minHeight: "100%" }}>
        <AppBar onBack={() => api.back()} title={t("export.title")}
          right={<IconButton name="x" label={window.t("g.close")} onClick={() => api.back()} />} />

        <div style={{ flex: 1, padding: "4px 16px 0", display: "flex", flexDirection: "column", gap: 14 }}>
          {/* Intro */}
          <div>
            <Kicker>{t("export.title")}</Kicker>
            <div style={{ fontSize: 14, color: "var(--color-text-2)", marginTop: 6, lineHeight: 1.45 }}>{t("export.sub")}</div>
          </div>

          {/* ── Filter pills ───────────────────────────────────────── */}
          <Card pad={14} style={{ display: "flex", flexDirection: "column", gap: 12 }}>
            <div style={{ display: "flex", flexWrap: "wrap", gap: 8 }}>
              <Pill icon="user" label={clientLabel} active={!!clientId} onClick={() => setOpen("client")} />
              <Pill icon="folder" label={projectLabel} active={!!projectId} disabled={!clientId} onClick={() => setOpen("project")} />
              <Pill icon="tag" label={statusLabel} active={!!status.length} onClick={() => setOpen("status")} />
              <Pill icon="list" label={typeLabel} active={!!types.length} onClick={() => setOpen("type")} />
              <Pill icon="calendar" label={rangeLabel} active={!!(range.from || range.to)} onClick={() => setOpen("date")} />
              <Pill icon="coins" label={currencyLabel} active={!!currency} onClick={() => setOpen("currency")} />
            </div>
            {/* Date basis: Issued vs Paid — only meaningful when a range is set,
                but always visible so the toggle is discoverable. */}
            <div style={{ display: "flex", alignItems: "center", gap: 10 }}>
              <span style={{ fontSize: 12, color: "var(--color-text-3)", fontWeight: 600, flexShrink: 0 }}>{t("export.filter.date")}</span>
              <Segmented value={basis} onChange={onFilter(setBasis)} style={{ flex: 1 }} options={[
                { value: "issued", label: t("export.basis.issued") },
                { value: "paid", label: t("export.basis.paid") },
              ]} />
              {anyFilter ? (
                <button onClick={clearAll} className="q-tap" style={{
                  display: "inline-flex", alignItems: "center", gap: 4, border: "none", background: "transparent",
                  color: "var(--color-accent)", fontSize: 12.5, fontWeight: 600, cursor: "pointer", padding: "6px 2px", flexShrink: 0,
                }}>
                  <Icon name="x" size={13} />{t("export.filter.clear")}
                </button>
              ) : null}
            </div>
          </Card>

          {/* ── Matching count + select-all ────────────────────────── */}
          <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", gap: 12, paddingTop: 2 }}>
            <span style={{ fontSize: 13, fontWeight: 600, color: "var(--color-text-2)" }}>
              {t("export.matching", { sel: selectedIds.length, total: matchIds.length })}
            </span>
            {matchIds.length > 0 && (
              <button onClick={allTicked ? deselectAll : selectAll} className="q-tap" style={{
                border: "none", background: "transparent", color: "var(--color-primary)",
                fontSize: 12.5, fontWeight: 600, cursor: "pointer", padding: "6px 2px",
              }}>{allTicked ? t("export.deselectAll") : t("export.selectAll")}</button>
            )}
          </div>

          {/* ── Reviewable list ────────────────────────────────────── */}
          {matches.length === 0 ? (
            <Card pad={0} style={{ overflow: "hidden" }}>
              {/* export.empty already carries the guidance ("adjust the
                  filters"); offer the one-tap clear when any filter is active. */}
              <EmptyState icon="search" title={t("export.empty")} body={t("export.sub")}
                action={anyFilter ? <Button variant="outline" size="md" icon="x" onClick={clearAll}>{t("export.filter.clear")}</Button> : null} />
            </Card>
          ) : (
            <Card pad={0} style={{ overflow: "hidden" }}>
              {matches.map((inv, i) => {
                const tot = LIB.totals(inv);
                const eff = LIB.effectiveStatus(inv, today);
                const checked = selectedSet.has(inv.id);
                const client = LIB.getClient(db, inv.clientId);
                const proj = inv.projectId ? LIB.getProject(db, inv.projectId) : null;
                return (
                  <div key={inv.id} className="q-row" style={{
                    display: "flex", alignItems: "center", gap: 10, padding: "10px 14px",
                    borderBottom: i === matches.length - 1 ? "none" : "1px solid var(--color-border)",
                  }}>
                    {/* Checkbox — toggles inclusion (stops the row-tap preview). */}
                    <button onClick={(e) => { e.stopPropagation(); toggleRow(inv.id); }} aria-pressed={checked}
                      aria-label={inv.id} className="q-tap" style={{
                        width: 24, height: 24, borderRadius: 7, flexShrink: 0, cursor: "pointer", padding: 0,
                        display: "flex", alignItems: "center", justifyContent: "center",
                        background: checked ? "var(--color-primary)" : "var(--color-surface-0)",
                        border: "1.6px solid " + (checked ? "var(--color-primary)" : "var(--color-border-strong)"),
                        color: "var(--color-primary-fg)", transition: "background .12s, border-color .12s",
                      }}>
                      {checked && <Icon name="check" size={15} />}
                    </button>
                    {/* Row body — tap (not the checkbox) opens the preview. */}
                    <div onClick={() => api.go("invoice-preview", { id: inv.id })} className="q-tap" style={{ flex: 1, minWidth: 0, display: "flex", alignItems: "center", gap: 10, cursor: "pointer" }}>
                      <div style={{ flex: 1, minWidth: 0 }}>
                        <div style={{ fontFamily: "var(--font-mono)", fontSize: 13, fontWeight: 600, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{inv.id}</div>
                        <div style={{ fontSize: 12, color: "var(--color-text-3)", marginTop: 1, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>
                          {(client ? client.name : (inv.newClientName || ((inv.buyer && inv.buyer.name) || "—")))}
                          {proj ? " · " + proj.project.name : ""} · {window.fmtDate(inv.issued, "short")}
                        </div>
                      </div>
                      <div style={{ display: "flex", flexDirection: "column", alignItems: "flex-end", gap: 4, flexShrink: 0 }}>
                        <Money amount={LIB.docSign(inv) * tot.total} currency={inv.currency} size={13.5} cents={false} />
                        <StatusPill status={eff} solid />
                      </div>
                    </div>
                  </div>
                );
              })}
            </Card>
          )}

          {/* ── Format picker ──────────────────────────────────────── */}
          <div style={{ marginTop: 4 }}>
            <Kicker style={{ marginBottom: 8 }}>{t("export.format")}</Kicker>
            <Card pad={0} style={{ overflow: "hidden" }}>
              {FORMATS.map((f, i) => {
                const active = format === f.id;
                const disabled = !!f.soon;
                const blocked = f.needRange && needRange;
                return (
                  <button key={f.id} onClick={disabled ? undefined : () => setFormat(f.id)} disabled={disabled} className="q-row" style={{
                    width: "100%", textAlign: "left", border: "none", background: "transparent", cursor: disabled ? "not-allowed" : "pointer",
                    display: "flex", alignItems: "center", gap: 12, padding: "12px 14px",
                    borderBottom: i === FORMATS.length - 1 ? "none" : "1px solid var(--color-border)",
                    opacity: disabled ? 0.5 : 1,
                  }}>
                    {/* Radio */}
                    <span style={{
                      width: 20, height: 20, borderRadius: "50%", flexShrink: 0,
                      border: "2px solid " + (active ? "var(--color-primary)" : "var(--color-border-strong)"),
                      display: "flex", alignItems: "center", justifyContent: "center",
                    }}>
                      {active && <span style={{ width: 10, height: 10, borderRadius: "50%", background: "var(--color-primary)" }} />}
                    </span>
                    <Icon name={f.icon} size={18} color="var(--color-text-3)" style={{ flexShrink: 0 }} />
                    <div style={{ flex: 1, minWidth: 0 }}>
                      <div style={{ fontSize: 14, fontWeight: 600, display: "flex", alignItems: "center", gap: 7 }}>
                        {f.label}
                        {disabled && <span style={{ fontFamily: "var(--font-mono)", fontSize: 10, fontWeight: 600, padding: "1px 6px", borderRadius: 999, background: "var(--color-surface-2)", color: "var(--color-text-3)" }}>{t("export.format.soon")}</span>}
                      </div>
                      <div style={{ fontSize: 12, color: blocked ? "var(--color-accent)" : "var(--color-text-3)", marginTop: 2, lineHeight: 1.4 }}>
                        {blocked ? t("export.needRange") : f.sub}
                      </div>
                    </div>
                  </button>
                );
              })}
            </Card>
            {/* Print cap warning when the selection exceeds the cap. */}
            {format === "print" && selectedIds.length > PRINT_CAP && (
              <div style={{ fontSize: 12, color: "var(--color-accent)", marginTop: 8, lineHeight: 1.4 }}>{t("export.printCap")}</div>
            )}
          </div>
          <div style={{ height: 8 }} />
        </div>

        {/* ── Sticky footer: per-currency summary + format-aware CTA ─── */}
        <div className="q-glass-2" style={{
          position: "sticky", bottom: 0, zIndex: 10,
          padding: "12px 16px calc(14px + env(safe-area-inset-bottom))",
          borderTop: "1px solid var(--color-border)", boxShadow: "var(--elev-up)",
          display: "flex", flexDirection: "column", gap: 10,
        }}>
          <div style={{ display: "flex", alignItems: "baseline", justifyContent: "space-between", gap: 12 }}>
            {/* dl.scope.selected — the long-abandoned "Selected" scope label,
                finally rendered (PLAN §1): the kicker over the live summary. */}
            <Kicker>{t("dl.scope.selected")}</Kicker>
            <span style={{ fontSize: 13, fontWeight: 600, color: "var(--color-text-2)" }}>
              {t("export.summary", { count: selectedIds.length, total: summaryStr })}
            </span>
          </div>
          <Button variant="primary" size="lg" full
            icon={format === "print" ? "printer" : "download"}
            disabled={ctaDisabled}
            onClick={runExport}>{ctaLabel}</Button>
          {packBlocked && (
            <div style={{ fontSize: 12, color: "var(--color-accent)", textAlign: "center", marginTop: -2 }}>{t("export.needRange")}</div>
          )}
        </div>

        {/* ── Picker sheets ──────────────────────────────────────────── */}
        <SinglePicker open={open === "client"} onClose={() => setOpen(null)} title={t("export.filter.client")}
          options={clientOpts} value={clientId} onChange={chooseClient} allLabel={t("dl.scope.all") + " · " + t("export.filter.client")} />
        <SinglePicker open={open === "project"} onClose={() => setOpen(null)} title={t("export.filter.project")}
          options={projectOpts} value={projectId} onChange={onFilter(setProjectId)} allLabel={t("export.filter.project")} />
        <MultiPicker open={open === "status"} onClose={() => setOpen(null)} title={t("export.filter.status")}
          options={statusOpts} selected={status} onChange={onFilter(setStatus)} allLabel={t("export.filter.status")} />
        <MultiPicker open={open === "type"} onClose={() => setOpen(null)} title={t("export.filter.type")}
          options={typeOpts} selected={types} onChange={onFilter(setTypes)} allLabel={t("export.filter.type")} />
        <SinglePicker open={open === "currency"} onClose={() => setOpen(null)} title={t("export.filter.currency")}
          options={currencyOpts} value={currency} onChange={onFilter(setCurrency)} allLabel={t("export.filter.currency")} />

        {/* Date-range preset sheet. */}
        <Sheet open={open === "date"} onClose={() => setOpen(null)} height="auto" title={t("export.filter.date")}>
          <div style={{ padding: "2px 8px 24px" }}>
            <Card pad={0} style={{ overflow: "hidden" }}>
              {rangePresets.map((p, i) => (
                <Row key={p.key} onClick={() => { setRange({ key: p.key, from: p.from, to: p.to }); setPicked(null); setOpen(null); }} last={i === rangePresets.length - 1 && range.key !== "custom"}>
                  <div style={{ flex: 1, fontSize: 14, fontWeight: 600 }}>{p.label}</div>
                  {range.key === p.key && <Icon name="check" size={18} color="var(--color-primary)" />}
                </Row>
              ))}
              {range.key === "custom" && (
                <Row last>
                  <div style={{ flex: 1, fontSize: 14, fontWeight: 600 }}>
                    {(range.from ? window.fmtDate(range.from, "short") : "…") + " – " + (range.to ? window.fmtDate(range.to, "short") : "…")}
                  </div>
                  <Icon name="check" size={18} color="var(--color-primary)" />
                </Row>
              )}
            </Card>
          </div>
        </Sheet>
      </div>
    );
  }

  window.QScreens = Object.assign(window.QScreens || {}, { Export });
})();
