// view-factory.jsx — Dashboard Usine (/factory/:token) — dense, orienté action.

// ——— Vue Usine façon Admin : liste des jeux à gauche, prints en cartes repliables ———
const FACTORY_NAV_KEY = "kodama-factory-nav-v1";

function buildFactoryGames(db) {
  const actives = activePrintIds(db);
  const printNum = (lbl) => {const m = String(lbl || "").match(/\d+/);return m ? Number(m[0]) : 0;};
  const byGame = {};
  db.prints.forEach((p) => {
    if (!byGame[p.gameId]) byGame[p.gameId] = { gameId: p.gameId, game: p.game, prints: [], activeCount: 0 };
    byGame[p.gameId].prints.push(p);
  });
  const printGame = {};
  db.prints.forEach((p) => {printGame[p.id] = p.gameId;});
  db.productions.forEach((p) => {
    const g = byGame[printGame[p.printId]];
    if (g && p.statut !== "Terminé") g.activeCount++;
  });
  const all = Object.values(byGame);
  all.forEach((g) => g.prints.sort((a, b) => printNum(b.label) - printNum(a.label)));
  const alpha = (a, b) => a.game.localeCompare(b.game);
  return { rows: all.filter((g) => g.activeCount > 0).sort(alpha), done: all.filter((g) => g.activeCount === 0).sort(alpha), actives };
}

function FactoryGameSidebar({ rows, done, gameId, setGameId }) {
  const [showDone, setShowDone] = React.useState(false);
  const item = (g, dim) => {
    const selected = gameId === g.gameId;
    return (
      <button key={g.gameId} onClick={() => setGameId(g.gameId)} style={{ display: "flex", alignItems: "center", gap: 9, width: "100%", textAlign: "left", border: "none", cursor: "pointer",
        background: selected ? K.black : "transparent", color: selected ? "#FFF" : K.ink, borderRadius: 10, padding: "8px 11px", fontFamily: K.font, fontSize: 13.5, fontWeight: dim ? 500 : 700 }}>
        <span style={{ width: 9, height: 9, borderRadius: 99, background: dim ? gameColor(g.game).soft : gameColor(g.game).mid, flexShrink: 0 }}></span>
        <span style={{ flex: 1, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{g.game}</span>
        {g.activeCount > 0 && <span style={{ fontSize: 10.5, fontWeight: 800, borderRadius: 99, padding: "1px 7px", background: selected ? "rgba(255,255,255,0.25)" : K.amberBg, color: selected ? "#FFF" : K.amber }}>{g.activeCount}</span>}
      </button>);

  };
  return (
    <div style={{ display: "flex", flexDirection: "column", gap: 2 }}>
      <div className="k-label" style={{ padding: "2px 11px 6px" }}>Games · in production</div>
      {rows.map((g) => item(g, false))}
      {rows.length === 0 && <div style={{ padding: "4px 11px", fontSize: 12.5, color: K.sub }}>No production in progress.</div>}
      {done.length > 0 &&
      <button onClick={() => setShowDone(!showDone)} style={{ background: "none", border: "none", cursor: "pointer", textAlign: "left", padding: "7px 11px", fontFamily: K.font, fontSize: 12, fontWeight: 700, color: K.sub }}>
          <span style={{ display: "inline-block", transform: showDone ? "rotate(90deg)" : "none", transition: "transform .15s", marginRight: 7 }}>▸</span>
          {done.length} completed game{done.length > 1 ? "s" : ""}
        </button>
      }
      {showDone && done.map((g) => item(g, true))}
    </div>);

}

// ——— Carte d'un print — même gabarit que la vue Admin (en-tête repliable + table partenaires) ———
function FactoryPrintCard({ print, db, update, push, defaultOpen }) {
  const [open, setOpen] = React.useState(!!defaultOpen);
  const prods = db.productions.filter((p) => p.printId === print.id).map((p) => {
    const partner = db.partners.find((x) => x.id === p.partnerId) || { name: "?" };
    return { ...p, partnerName: partner.name.replace(/ - .*$/, ""), gameLabel: `${print.game} · ${print.label}`, pallets: palletInfo(p.qty, db.basicData[print.gameId]) };
  });
  const counts = stateCounts(prods);
  const waiting = prods.filter((p) => !p.mpcOk && p.statut !== "Terminé" && p.statut !== "Modifications des fichiers");
  const changes = prods.filter((p) => p.statut === "Modifications des fichiers");
  const allDone = prods.length > 0 && prods.every((p) => p.statut === "Terminé");
  return (
    <div className="k-card" style={{ marginTop: 10, overflow: "hidden" }}>
      <button onClick={() => setOpen(!open)} style={{ all: "unset", boxSizing: "border-box", display: "block", width: "100%", padding: "13px 18px 14px", cursor: "pointer" }}>
        <div style={{ display: "flex", justifyContent: "space-between", alignItems: "baseline", gap: 10, flexWrap: "wrap" }}>
          <div className="k-round" style={{ fontWeight: 700, fontSize: 22 }}>
            <span style={{ display: "inline-block", transform: open ? "rotate(90deg)" : "none", transition: "transform .15s", color: K.sub, marginRight: 8 }}>▸</span>
            {print.label} <span style={{ color: K.sub, fontWeight: 500, fontSize: 13.5 }}>· {prods.length} partner{prods.length > 1 ? "s" : ""}</span>
            {allDone && <span style={{ color: K.sub, fontWeight: 600, fontSize: 12, marginLeft: 8 }}>✓ completed</span>}
          </div>
          <span style={{ display: "inline-flex", gap: 14, alignItems: "baseline" }}>
            <StateCounters counts={counts} labels={{ ok: "approved", waiting: "waiting", changes: "changes", done: "completed" }} />
            <span className="k-mono" style={{ fontSize: 12, color: K.sub }}>{prods.reduce((s, p) => s + p.qty, 0).toLocaleString("fr-FR")} units</span>
          </span>
        </div>
        <StateBar counts={counts} style={{ marginTop: 9 }} />
        <div style={{ marginTop: 8, fontSize: 12.5, display: "flex", flexDirection: "column", gap: 2 }}>
          {waiting.length > 0 && <span style={{ color: K.amber, fontWeight: 600 }}>⏳ Waiting for approval: {waiting.map((p) => p.partnerName).join(", ")}</span>}
          {changes.length > 0 && <span style={{ color: K.red, fontWeight: 600 }}>✎ Changes requested: {changes.map((p) => p.partnerName).join(", ")}</span>}
          {!allDone && waiting.length === 0 && changes.length === 0 && <span style={{ color: K.green, fontWeight: 600 }}>✓ All partners approved — production can launch.</span>}
        </div>
      </button>
      {open &&
      <div style={{ borderTop: `1px solid ${K.line}` }}>
          <div style={{ overflowX: "auto" }}>
            <table style={{ width: "100%", borderCollapse: "collapse" }}>
              <thead>
                <tr>
                  {["Partner", "Qty", "Validation", "Pallets", "Pallet cost", "INCOTERM", "MPC status"].map((h, i) =>
                <th key={h} className="k-label" style={{ textAlign: i === 1 ? "right" : "left", padding: "12px 14px", fontSize: 10.5, borderBottom: "none" }}>{h}</th>
                )}
                </tr>
              </thead>
              <tbody>
                {prods.map((p) => <PartnerRow key={p.id} prod={p} partner={db.partners.find((x) => x.id === p.partnerId)} transports={db.transports} update={update} push={push} />)}
              </tbody>
            </table>
          </div>
          <div style={{ padding: "10px 20px 14px", fontSize: 12.5, color: K.sub, display: "flex", gap: 10, alignItems: "center", flexWrap: "wrap", borderTop: `1px solid ${K.line}` }}>
            <span className="k-label" style={{ fontSize: 10.5, whiteSpace: "nowrap" }}>Localized files + Carton Marks</span>
            <div style={{ flex: 1, minWidth: 220 }}>
              <InlineEdit mono value={print.cartonMarksUrl} placeholder="+ paste the shared drive link" width={"100%"} onSave={(v) => {
              const url = v ? extUrl(v) : v;
              update((d) => {
                d.prints.find((x) => x.id === print.id).cartonMarksUrl = url;
                d.log.unshift({ ts: nowStamp(), actor: "Whatz Games", action: "Carton marks link updated", detail: `${print.game} ${print.label}` });
              });
              push("Saved to Airtable.");
            }} />
            </div>
            {print.cartonMarksUrl && <ExtLink style={{ fontSize: 12.5, whiteSpace: "nowrap" }} href={print.cartonMarksUrl}>↗ Open</ExtLink>}
            {print.cartonMarksUrl && <CopyBtn text={print.cartonMarksUrl} />}
          </div>
        </div>
      }
    </div>);

}

function InlineEdit({ value, type = "text", unit, placeholder = "—", onSave, mono, width = 110 }) {
  const [editing, setEditing] = React.useState(false);
  const [draft, setDraft] = React.useState("");
  const [err, setErr] = React.useState(false);
  const start = () => {setDraft(value == null ? "" : String(value));setErr(false);setEditing(true);};
  const commit = () => {
    const v = draft.trim();
    if (type === "number" && v !== "" && isNaN(Number(v.replace(",", ".")))) {setErr(true);return;}
    onSave(type === "number" && v !== "" ? Number(v.replace(",", ".")) : v);
    setEditing(false);
  };
  if (editing) {
    return (
      <input autoFocus className="k-input" value={draft} style={{ width, padding: "4px 8px", fontSize: 13, borderRadius: 7, borderColor: err ? K.red : K.black, fontFamily: mono ? K.mono : K.font }}
      onChange={(e) => {setDraft(e.target.value);setErr(false);}}
      onBlur={commit}
      onKeyDown={(e) => {if (e.key === "Enter") commit();if (e.key === "Escape") setEditing(false);}} />);

  }
  return (
    <button onClick={start} title="Click to edit" style={{ background: "none", border: "none", borderBottom: `1.5px dashed ${K.line}`, cursor: "text", padding: "2px 1px", fontSize: 13, color: value == null || value === "" ? K.sub : K.ink, fontFamily: mono ? K.mono : K.font, textAlign: "left" }}>
      {value == null || value === "" ? placeholder : <span>{String(value)}{unit ? <span style={{ color: K.sub, fontSize: 11.5 }}> {unit}</span> : null}</span>}
    </button>);

}

function UploadZone({ prod, transports, update, push }) {
  const [target, setTarget] = React.useState("packing");
  const [drag, setDrag] = React.useState(false);
  const inputRef = React.useRef();
  const linked = transports.filter((t) => prod.transportIds.includes(t.id));

  const addFiles = (fileList) => {
    const names = [...fileList].map((f) => f.name);
    if (!names.length) return;
    update((db) => {
      const p = db.productions.find((x) => x.id === prod.id);
      names.forEach((name) => {
        if (target === "shipping" && linked.length > 0) {
          const tr = db.transports.find((t) => t.id === linked[0].id);
          tr.docs.push({ name, date: todayIso(), kind: "Shipping doc" });
          db.log.unshift({ ts: nowStamp(), actor: "Whatz Games", action: "Shipping doc uploaded", detail: `${tr.name} — ${name}` });
        } else {
          const noTr = target === "shipping" && linked.length === 0;
          p.files.push({ name, date: todayIso(), kind: noTr ? "Shipping doc (no shipment record yet)" : "Packing list" });
          db.log.unshift({ ts: nowStamp(), actor: "Whatz Games", action: noTr ? "Doc uploaded (no shipment record)" : "Packing list uploaded", detail: `${prod.gameLabel} → ${prod.partnerName} — ${name}` });
        }
      });
    });
    push(`${names.length} file${names.length > 1 ? "s" : ""} uploaded to Airtable.`);
  };

  const allFiles = [...prod.files.map((f) => ({ ...f })), ...linked.flatMap((t) => t.docs.map((d) => ({ ...d, kind: d.kind + " · " + t.ref })))];

  return (
    <div>
      <div style={{ display: "flex", gap: 8, alignItems: "center", flexWrap: "wrap" }}>
        <div style={{ display: "inline-flex", border: `1.5px solid ${K.line}`, borderRadius: 99, overflow: "hidden" }}>
          {[["packing", "Packing list"], ["shipping", "Shipping docs"]].map(([k, lbl]) =>
          <button key={k} onClick={() => setTarget(k)} style={{ border: "none", padding: "5px 13px", fontSize: 12, fontWeight: 700, cursor: "pointer", fontFamily: K.font, background: target === k ? K.black : "transparent", color: target === k ? "#FFF" : K.sub }}>{lbl}</button>
          )}
        </div>
        {target === "shipping" && linked.length === 0 && <span style={{ fontSize: 11.5, color: K.amber, fontWeight: 600 }}>no shipment record yet — will be filed on the production</span>}
        {target === "shipping" && linked.length > 0 && <span style={{ fontSize: 11.5, color: K.teal, fontWeight: 600 }}>→ {linked[0].name}</span>}
      </div>
      <div
        onDragOver={(e) => {e.preventDefault();setDrag(true);}}
        onDragLeave={() => setDrag(false)}
        onDrop={(e) => {e.preventDefault();setDrag(false);addFiles(e.dataTransfer.files);}}
        onClick={() => inputRef.current && inputRef.current.click()}
        style={{ marginTop: 8, border: `1.5px dashed ${drag ? K.blueS : K.line}`, background: drag ? K.blueBg : "#FBFAF8", borderRadius: 10, padding: "14px 16px", textAlign: "center", cursor: "pointer", fontSize: 12.5, color: K.sub }}>
        <strong style={{ color: K.ink }}>Drop files here</strong> or click to browse — PDF, XLS, images · max 5 MB
        <input ref={inputRef} type="file" multiple accept=".pdf,.xls,.xlsx,image/*" style={{ display: "none" }} onChange={(e) => {addFiles(e.target.files);e.target.value = "";}} />
      </div>
      {allFiles.length > 0 &&
      <div style={{ marginTop: 8, display: "flex", flexDirection: "column", gap: 4 }}>
          {allFiles.map((f, i) =>
        <div key={i} style={{ fontSize: 12.5, display: "flex", gap: 8, alignItems: "baseline" }}>
              {f.url ?
          <ExtLink href={f.url}>↓ {f.name}</ExtLink> :
          <a className="k-link" href="#" onClick={(e) => e.preventDefault()}>↓ {f.name}</a>}
              <span style={{ color: f.kind.includes("no shipment") ? K.amber : K.sub, fontSize: 11.5 }}>{f.kind}{f.date ? " · " + fmtDate(f.date) : ""}</span>
            </div>
        )}
        </div>
      }
    </div>);

}

function PalletsCell({ prod }) {
  if (prod.wantsPallets === true) return (
    <div style={{ color: K.green, fontWeight: 600, fontSize: 12.5 }}>✓ Pallets
      <div style={{ color: K.sub, fontWeight: 400, fontSize: 11.5, fontFamily: K.mono }}>{prod.pallets ? `${prod.pallets.cartons} ctn${prod.pallets.pallets ? " · ≈ " + prod.pallets.pallets + " pal" : ""}` : "—"}</div>
    </div>);

  if (prod.wantsPallets === false) return (
    <div style={{ fontWeight: 600, fontSize: 12.5, color: K.ink }}>✕ No pallets
      <div style={{ color: K.sub, fontWeight: 400, fontSize: 11.5 }}>floor-loaded{prod.pallets ? ` · ${prod.pallets.cartons} ctn` : ""}</div>
    </div>);

  return (
    <div style={{ color: K.amber, fontWeight: 600, fontSize: 12.5 }}>? Not set
      <div style={{ color: K.sub, fontWeight: 400, fontSize: 11.5 }}>partner asked via portal</div>
    </div>);

}

function PalletCostCell({ prod }) {
  if (prod.wantsPallets === false) return <span style={{ color: K.sub, fontSize: 12 }}>n/a</span>;
  if (prod.wantsPallets === true && prod.pallets && prod.pallets.cost) return (
    <div>
      <span className="k-mono" style={{ fontSize: 13, fontWeight: 600, color: K.ink }}>$ {prod.pallets.cost.toLocaleString("en-US")}</span>
      <div style={{ color: K.sub, fontWeight: 400, fontSize: 10.5, fontFamily: K.mono }}>{Math.ceil(prod.pallets.pallets)} pal × ${PALLET_COST_USD}</div>
    </div>);

  return <span style={{ color: K.sub, fontSize: 12 }}>—</span>;
}

function IncotermCell({ prod }) {
  if (prod.incoterm) return (
    <div style={{ fontWeight: 700, fontSize: 12.5, color: K.ink }}>{prod.incoterm}
      <div style={{ color: K.sub, fontWeight: 400, fontSize: 11.5 }}>{prod.incoterm === "FOB" ? "Free On Board" : "Ex Works"}</div>
    </div>);

  return (
    <div style={{ color: K.amber, fontWeight: 600, fontSize: 12.5 }}>? Not set
      <div style={{ color: K.sub, fontWeight: 400, fontSize: 11.5 }}>partner asked via portal</div>
    </div>);

}

function MpcCell({ prod }) {
  if (prod.mpcOk) return <div style={{ color: K.green, fontWeight: 600, fontSize: 12.5 }}>✓ Approved by {prod.mpcBy}<div style={{ color: K.sub, fontWeight: 400, fontSize: 11.5 }}>{fmtDate(prod.mpcOn)}</div></div>;
  if (prod.statut === "Modifications des fichiers") return (
    <div style={{ color: K.red, fontWeight: 600, fontSize: 12.5 }}>⚠ Changes requested
      <div title={prod.comment} style={{ color: K.sub, fontWeight: 400, fontSize: 11.5, maxWidth: 340, whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>“{prod.comment}”</div>
    </div>);

  return <div style={{ color: K.amber, fontWeight: 600, fontSize: 12.5 }}>⏳ Waiting{prod.fixedOn && prod.prevComment ? <div style={{ color: K.sub, fontWeight: 400, fontSize: 11 }}>re-approval after changes · {fmtDate(prod.fixedOn)}</div> : null}</div>;
}

function NotesBox({ prod, update, push }) {
  const [draft, setDraft] = React.useState(prod.notes || "");
  React.useEffect(() => setDraft(prod.notes || ""), [prod.notes]);
  const dirty = draft !== (prod.notes || "");
  const save = () => {
    update((db) => {
      const p = db.productions.find((x) => x.id === prod.id);
      p.notes = draft;
      db.log.unshift({ ts: nowStamp(), actor: "Whatz Games", action: "Notes updated", detail: `${prod.gameLabel} → ${prod.partnerName}` });
    });
    push("Saved to Airtable.");
  };
  return (
    <div>
      <textarea className="k-textarea" rows={4} value={draft} placeholder="Free notes — anything useful about this production…" onChange={(e) => setDraft(e.target.value)} style={{ fontSize: 12.5, background: "#FFF" }}></textarea>
      {dirty &&
      <div style={{ display: "flex", gap: 8, marginTop: 8 }}>
          <button className="k-btn k-btn-primary k-btn-sm" onClick={save}>Save notes</button>
          <button className="k-btn k-btn-ghost k-btn-sm" onClick={() => setDraft(prod.notes || "")}>Discard</button>
        </div>
      }
    </div>);

}

function PartnerRow({ prod, partner, transports, update, push }) {
  const [open, setOpen] = React.useState(false);
  const cell = { padding: "12px 14px", borderTop: `1px solid ${K.line}`, verticalAlign: "top", fontSize: 13 };
  return (
    <React.Fragment>
      <tr style={{ background: open ? "#FBFAF8" : "transparent" }}>
        <td style={{ ...cell, whiteSpace: "nowrap" }}>
          <button onClick={() => setOpen(!open)} style={{ background: "none", border: "none", cursor: "pointer", fontSize: 13, fontFamily: K.font, fontWeight: 700, color: K.ink, display: "flex", gap: 8, alignItems: "center", padding: 0 }}>
            <span style={{ display: "inline-block", transform: open ? "rotate(90deg)" : "none", transition: "transform .15s", color: K.sub }}>▸</span>
            <span style={{ fontSize: 17 }}>{partner.flag}</span> {partner.name.replace(/ - .*$/, "")}
          </button>
        </td>
        <td style={{ ...cell, textAlign: "right", fontFamily: K.mono }}>{prod.qty.toLocaleString("fr-FR")}</td>
        <td style={{ ...cell, fontSize: 12.5, color: prod.validation ? K.sub : K.amber, fontWeight: prod.validation ? 400 : 600 }}>{valLabel(prod.validation)}</td>
        <td style={{ ...cell, whiteSpace: "nowrap" }}><PalletsCell prod={prod} update={update} push={push} /></td>
        <td style={{ ...cell, whiteSpace: "nowrap" }}><PalletCostCell prod={prod} /></td>
        <td style={{ ...cell, whiteSpace: "nowrap" }}><IncotermCell prod={prod} /></td>
        <td style={cell}><MpcCell prod={prod} /></td>
      </tr>
      {open &&
      <tr>
          <td colSpan={7} style={{ ...cell, background: "#FBFAF8", padding: "16px 20px 20px" }}>
            {prod.statut === "Modifications des fichiers" && prod.comment ?
            <div style={{ marginBottom: 16, borderRadius: 12, background: K.redBg, border: `1.5px solid ${K.red}40`, padding: "12px 16px" }}>
                <div style={{ fontSize: 11, fontWeight: 800, letterSpacing: ".05em", textTransform: "uppercase", color: K.red }}>✎ Changes requested by {prod.partnerName} — production on hold</div>
                <p style={{ margin: "6px 0 0", fontSize: 13.5, fontWeight: 600, color: K.ink, whiteSpace: "pre-wrap" }}>“{prod.comment}”</p>
                <p style={{ margin: "6px 0 0", fontSize: 11.5, color: K.sub }}>KODAMA is updating the files — do not launch mass production until the partner approves again.</p>
              </div> : null}
            {!prod.mpcOk && prod.statut !== "Modifications des fichiers" && prod.fixedOn && prod.prevComment ?
            <div style={{ marginBottom: 16, borderRadius: 12, background: K.greenBg, border: `1.5px solid ${K.green}40`, padding: "12px 16px" }}>
                <div style={{ fontSize: 11, fontWeight: 800, letterSpacing: ".05em", textTransform: "uppercase", color: K.green }}>✓ Files updated by KODAMA on {fmtDate(prod.fixedOn)} — awaiting partner re-approval</div>
                <p style={{ margin: "6px 0 0", fontSize: 12.5, color: K.sub, whiteSpace: "pre-wrap" }}>Request was: “{prod.prevComment}”</p>
                {prod.fixNote ? <p style={{ margin: "4px 0 0", fontSize: 12.5, color: K.ink, fontWeight: 600, whiteSpace: "pre-wrap" }}>KODAMA: “{prod.fixNote}”</p> : null}
              </div> : null}
            <div style={{ marginBottom: 16 }}>
              <div className="k-label" style={{ marginBottom: 6 }}>📄 Notes</div>
              <NotesBox prod={prod} update={update} push={push} />
            </div>
            <div style={{ display: "flex", gap: 10, alignItems: "baseline", flexWrap: "wrap", marginBottom: 14 }}>
              <span className="k-label" style={{ fontSize: 10.5 }}>MPC tracking · by KODAMA</span>
              {prod.mpcTracking ?
              <span style={{ display: "inline-flex", gap: 8, alignItems: "baseline" }}>
                  <span className="k-mono" style={{ fontSize: 12.5, fontWeight: 600 }}>{prod.mpcTracking}</span>
                  {prod.mpcTrackingOn ? <span style={{ color: K.sub, fontSize: 11.5 }}>added {fmtDate(prod.mpcTrackingOn)}</span> : null}
                  <CopyBtn text={prod.mpcTracking} />
                </span> :

              <span style={{ color: K.sub, fontSize: 11.5 }} title="Tracking numbers are added by KODAMA from the admin view.">— added by KODAMA</span>
              }
            </div>
            <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 18 }}>
              <div>
                <div className="k-label" style={{ display: "flex", justifyContent: "space-between", alignItems: "center" }}>Pre-prod samples address {partner.preprod && <CopyBtn text={partner.preprod} />}</div>
                <pre className="k-mono" style={{ margin: "8px 0 14px", fontSize: 12, whiteSpace: "pre-wrap", background: "#FFF", border: `1px solid ${K.line}`, borderRadius: 8, padding: "10px 12px" }}>{partner.preprod || "Not provided yet"}</pre>
                <div className="k-label" style={{ display: "flex", justifyContent: "space-between", alignItems: "center" }}>Consignee &amp; Notify (read-only) {partner.consignee && <CopyBtn text={partner.consignee} />}</div>
                <pre className="k-mono" style={{ margin: "8px 0 0", fontSize: 12, whiteSpace: "pre-wrap", background: "#FFF", border: `1px solid ${K.line}`, borderRadius: 8, padding: "10px 12px", color: partner.consignee ? K.ink : K.red }}>{partner.consignee || "✕ Missing — KODAMA has asked the partner"}</pre>
              </div>
              <div>
                <div className="k-label" style={{ marginBottom: 8 }}>Upload documents</div>
                <UploadZone prod={prod} transports={transports} update={update} push={push} />
              </div>
            </div>
          </td>
        </tr>
      }
    </React.Fragment>);

}

function LogisticsSection({ gameId, game, db, update, push }) {
  const data = db.basicData[gameId];
  const save = (key) => (v) => {
    update((d) => {
      d.basicData[gameId][key] = v === "" ? null : v;
      d.log.unshift({ ts: nowStamp(), actor: "Whatz Games", action: "Logistics data updated", detail: `${game} — ${key} → ${v}` });
    });
    push("Saved to Airtable.");
  };
  if (!data) {
    return (
      <div className="k-card" style={{ marginTop: 12, padding: "22px", textAlign: "center" }}>
        <p style={{ margin: "0 0 12px", color: K.sub }}>No logistics record exists for <strong style={{ color: K.ink }}>{game}</strong> yet.</p>
        <button className="k-btn k-btn-primary" onClick={() => {
          update((d) => {
            d.basicData[gameId] = {};
            d.log.unshift({ ts: nowStamp(), actor: "Whatz Games", action: "Logistics record created", detail: game });
          });
          push("Logistics record created and linked to the game.");
        }}>+ Add logistics data</button>
      </div>);

  }
  const groups = [...new Set(BASIC_FIELDS.map((f) => f.group))];
  return (
    <div className="k-card" style={{ marginTop: 12, padding: "6px 22px 18px" }}>
      {groups.map((g) =>
      <div key={g}>
          <div className="k-label" style={{ margin: "16px 0 8px" }}>{g}</div>
          <div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fill, minmax(180px, 1fr))", gap: "10px 18px" }}>
            {BASIC_FIELDS.filter((f) => f.group === g).map((f) =>
          <div key={f.key}>
                <div style={{ fontSize: 11.5, color: K.sub, marginBottom: 1 }}>{f.label}{f.unit ? ` (${f.unit})` : ""}</div>
                <InlineEdit value={data[f.key]} type={f.type} unit={f.unit} placeholder="+ fill in" onSave={save(f.key)} mono={f.type === "text"} width={130} />
              </div>
          )}
          </div>
        </div>
      )}
      <p style={{ margin: "16px 0 0", fontSize: 11.5, color: K.sub }}>You are the primary source for this data — click any value to correct it. Every change is saved to Airtable and logged for KODAMA.</p>
    </div>);

}

function FactoryView({ db, update }) {
  const [toasts, push] = useToasts();
  const { rows, done, actives } = buildFactoryGames(db);
  const all = rows.concat(done);
  const [gameIdRaw, setGameIdState] = React.useState(() => {
    try {const v = localStorage.getItem(FACTORY_NAV_KEY);if (v) return v;} catch (e) {}
    return rows[0] ? rows[0].gameId : all[0] && all[0].gameId;
  });
  const setGameId = (v) => {setGameIdState(v);try {localStorage.setItem(FACTORY_NAV_KEY, v);} catch (e) {}};
  // Les ids changent quand les données réelles remplacent la démo — revalider la sélection
  let gameId = gameIdRaw;
  if (!all.find((g) => g.gameId === gameId)) gameId = rows[0] ? rows[0].gameId : all[0] && all[0].gameId;
  const game = all.find((g) => g.gameId === gameId);

  if (!game) {
    return <div style={{ minHeight: "100vh", background: K.bg, display: "flex", alignItems: "center", justifyContent: "center", color: K.sub, fontFamily: K.font }}>Chargement…</div>;
  }
  const gc = gameColor(game.game);

  return (
    <div style={{ minHeight: "100vh", background: K.bg }}>
      <TopBar title="Factory Dashboard" right={
      <div><div style={{ fontWeight: 700, color: "#FFF" }}>WHATZ GAMES</div>
        <div style={{ fontSize: 12, color: "rgba(255,255,255,0.65)" }}>Dongguan, CN 🇨🇳</div></div>
      } />
      <div style={{ display: "grid", gridTemplateColumns: "256px minmax(0, 1fr)", gap: 34, maxWidth: 1520, margin: "0 auto", padding: "24px 44px 48px", alignItems: "start" }}>
        <div style={{ position: "sticky", top: 76 }}>
          <FactoryGameSidebar rows={rows} done={done} gameId={gameId} setGameId={setGameId} />
        </div>
        <div style={{ minWidth: 0 }} data-screen-label={"Usine — " + game.game}>
          <div style={{ display: "flex", alignItems: "center", gap: 14, flexWrap: "wrap" }}>
            <div className="k-round" style={{ fontWeight: 700, padding: "4px 18px", borderRadius: 12, background: gc.fg, color: "#FFF", fontSize: "30px" }}>{game.game}</div>
            {game.activeCount > 0 ?
            <span style={{ fontSize: 12.5, color: K.sub, fontWeight: 600 }}>{game.activeCount} production{game.activeCount > 1 ? "s" : ""} in progress</span> :
            <span style={{ fontSize: 12.5, color: K.sub }}>no production in progress</span>}
          </div>
          <SectionLabel style={{ marginTop: 22 }}>Print runs</SectionLabel>
          {game.prints.map((pr) => <FactoryPrintCard key={pr.id} print={pr} db={db} update={update} push={push} defaultOpen={actives.has(pr.id)} />)}
          {game.prints.length === 0 && <div className="k-card" style={{ marginTop: 12, padding: 20, color: K.sub, textAlign: "center" }}>No print run for this game.</div>}
          <SectionLabel style={{ marginTop: 30 }}>Logistics data — {game.game} <span style={{ fontWeight: 400, textTransform: "none", letterSpacing: 0 }}>(editable — you are the source)</span></SectionLabel>
          <LogisticsSection gameId={game.gameId} game={game.game} db={db} update={update} push={push} />
        </div>
      </div>
      <ToastHost toasts={toasts} />
    </div>);

}
Object.assign(window, { FactoryView, InlineEdit });