// screens-admin.jsx — admin orders dashboard. Exports: AdminScreen function statusLabel(status, t) { return status === "shipped" ? t("admin_status_shipped") : status === "printing" ? t("admin_status_printing") : t("admin_status_new"); } function statusClass(status) { return "badge badge--" + (status || "new"); } // Download every keychain's QR (PNG + SVG) for an order, staggered so the // browser doesn't block the batch. SVG is vector = ideal for 3D printing. function downloadOrderAll(order) { let i = 0; order.items.forEach((item) => { (item.units || []).forEach((u, ui) => { const plateF = window.findFilament(u.plate); const codeF = window.findFilament(u.code); const payload = window.buildQRPayload(u); const base = order.id + "-" + item.productId + "-" + (ui + 1); setTimeout(() => window.downloadQRPng(payload, { plate: plateF.hex, code: codeF.hex, filename: base + ".png", px: 2048 }), i++ * 350); setTimeout(() => window.downloadQRSvg(payload, { plate: plateF.hex, code: codeF.hex, filename: base + ".svg" }), i++ * 350); setTimeout(() => window.download3mfTag(payload, { filename: base + ".3mf" }), i++ * 350); }); }); } function AdminDetail({ order, lang, t, onClose }) { return React.createElement( "div", { className: "modal-overlay", onClick: onClose }, React.createElement("div", { className: "modal card", onClick: (e) => e.stopPropagation() }, React.createElement("div", { className: "modal-head" }, React.createElement("div", null, React.createElement("div", { style: { fontWeight: 700, fontSize: 18 } }, order.id), React.createElement("div", { className: "muted", style: { fontSize: 13, marginTop: 2 } }, new Date(order.date).toLocaleString(lang === "en" ? "en-GB" : "fr-FR"))), React.createElement("div", { style: { display: "flex", gap: 8, alignItems: "center" } }, React.createElement("button", { className: "btn btn--primary", style: { padding: "9px 16px", fontSize: 13 }, onClick: () => downloadOrderAll(order) }, "↓ " + t("admin_download_all")), React.createElement("button", { className: "icon-btn", onClick: onClose }, "✕"), ), ), React.createElement("hr", { className: "hr" }), React.createElement("div", { className: "modal-body" }, order.items.map((item) => { const prod = window.PRODUCTS.find((p) => p.id === item.productId) || window.PRODUCTS[0]; const units = item.units || []; return React.createElement("div", { key: item.lineId, style: { display: "flex", flexDirection: "column", gap: 14 } }, React.createElement("div", { style: { fontWeight: 600, fontSize: 15 } }, (lang === "en" ? prod.name_en : prod.name_fr), React.createElement("span", { className: "muted", style: { fontWeight: 400 } }, " · " + t("qty") + " " + item.qty)), units.map((u, ui) => { const plateF = window.findFilament(u.plate); const codeF = window.findFilament(u.code); return React.createElement("div", { key: ui, className: "detail-item" }, React.createElement("div", { style: { background: "var(--surface-2)", borderRadius: 14, padding: 10 } }, React.createElement(window.QRPreview, { text: window.buildQRPayload(u), plate: plateF.hex, code: codeF.hex, size: 110 })), React.createElement("div", { style: { flex: 1 } }, React.createElement("div", { style: { fontWeight: 600, fontSize: 14 } }, prod.count > 1 ? (t("unit_label") + " " + (ui + 1)) : (lang === "en" ? "Keychain" : "Porte-clé")), React.createElement("div", { className: "muted", style: { fontSize: 13, marginTop: 4, display: "flex", alignItems: "center", gap: 7 } }, React.createElement(window.TwoTone, { plate: plateF.hex, code: codeF.hex, size: 16 }), (lang === "en" ? plateF.en : plateF.fr) + " · " + (lang === "en" ? codeF.en : codeF.fr)), React.createElement("div", { style: { marginTop: 10, fontSize: 12.5, fontWeight: 600, color: "var(--ink-soft)" } }, t("admin_qr_content")), React.createElement("pre", { className: "code-block" }, window.qrContentLabel(u, t)), React.createElement("div", { style: { display: "flex", gap: 8, marginTop: 12, flexWrap: "wrap" } }, React.createElement("button", { className: "btn btn--ghost", style: { padding: "9px 16px", fontSize: 13.5 }, onClick: () => window.downloadQRPng(window.buildQRPayload(u), { plate: plateF.hex, code: codeF.hex, filename: order.id + "-" + item.productId + "-" + (ui + 1) + ".png", px: 2048 }) }, "↓ PNG"), React.createElement("button", { className: "btn btn--ghost", style: { padding: "9px 16px", fontSize: 13.5 }, onClick: () => window.downloadQRSvg(window.buildQRPayload(u), { plate: plateF.hex, code: codeF.hex, filename: order.id + "-" + item.productId + "-" + (ui + 1) + ".svg" }) }, "↓ " + t("admin_download_svg")), React.createElement("button", { className: "btn btn--ghost", style: { padding: "9px 16px", fontSize: 13.5 }, onClick: () => window.download3mfTag(window.buildQRPayload(u), { filename: order.id + "-" + item.productId + "-" + (ui + 1) + ".3mf" }) }, "↓ 3MF · 1 face"), React.createElement("button", { className: "btn btn--primary", style: { padding: "9px 16px", fontSize: 13.5 }, onClick: () => window.download3mfDouble(window.buildQRPayload(u), { filename: order.id + "-" + item.productId + "-" + (ui + 1) + ".3mf" }) }, "↓ 3MF · recto-verso (2 demi-coques)"), ), ), ); }), ); }), React.createElement("hr", { className: "hr", style: { margin: "4px 0" } }), React.createElement("div", { className: "detail-grid" }, React.createElement("div", null, React.createElement("div", { className: "detail-label" }, t("admin_col_customer")), React.createElement("div", { style: { fontSize: 14 } }, order.customer.name), React.createElement("div", { className: "muted", style: { fontSize: 13 } }, order.customer.email)), React.createElement("div", null, React.createElement("div", { className: "detail-label" }, t("admin_ship_to")), React.createElement("div", { style: { fontSize: 14, lineHeight: 1.5 } }, order.customer.address, order.customer.address2 ? ", " + order.customer.address2 : "", React.createElement("br"), order.customer.zip + " " + order.customer.city, React.createElement("br"), order.customer.country)), ), ), ), ); } function AdminScreen({ t, lang, orders, setStatus, clearAll, onBack }) { const [detail, setDetail] = React.useState(null); return React.createElement( "div", { className: "screen container", style: { paddingTop: 28, paddingBottom: 90 } }, React.createElement("div", { style: { display: "flex", alignItems: "flex-end", justifyContent: "space-between", gap: 16, flexWrap: "wrap" } }, React.createElement("div", null, React.createElement("div", { className: "kicker" }, t("admin_sub")), React.createElement("h1", { className: "h2", style: { margin: "8px 0 0" } }, t("admin_title")), React.createElement("div", { className: "muted", style: { fontSize: 14, marginTop: 6 } }, orders.length + " " + (orders.length === 1 ? (lang === "en" ? "order" : "commande") : (lang === "en" ? "orders" : "commandes"))), orders.length > 0 ? React.createElement("div", { className: "pill", style: { marginTop: 10 } }, React.createElement("span", { className: "dot" }), t("admin_archived")) : null), orders.length > 0 && React.createElement("button", { className: "btn btn--ghost", style: { fontSize: 13.5, padding: "9px 16px" }, onClick: clearAll }, t("admin_clear")), ), orders.length === 0 ? React.createElement("div", { className: "card", style: { padding: 60, textAlign: "center", marginTop: 26 } }, React.createElement("div", { className: "muted" }, t("admin_empty"))) : React.createElement("div", { className: "admin-table card", style: { marginTop: 26 } }, React.createElement("div", { className: "admin-row admin-row--head" }, React.createElement("div", null, t("admin_col_order")), React.createElement("div", null, t("admin_col_customer")), React.createElement("div", null, t("admin_col_model")), React.createElement("div", { style: { textAlign: "center" } }, t("admin_col_qr")), React.createElement("div", { style: { textAlign: "right" } }, t("admin_col_total")), React.createElement("div", null, t("admin_col_status")), React.createElement("div", null, ""), ), orders.map((o) => { const it = o.items[0]; const u0 = (it.units && it.units[0]) || {}; const prod = window.PRODUCTS.find((p) => p.id === it.productId) || window.PRODUCTS[0]; const extra = o.items.length - 1; return React.createElement("div", { className: "admin-row", key: o.id }, React.createElement("div", { "data-label": t("admin_col_order") }, React.createElement("div", { style: { fontWeight: 600 } }, o.id), React.createElement("div", { className: "faint", style: { fontSize: 12 } }, new Date(o.date).toLocaleDateString(lang === "en" ? "en-GB" : "fr-FR"))), React.createElement("div", { "data-label": t("admin_col_customer") }, React.createElement("div", { style: { fontSize: 14 } }, o.customer.name), React.createElement("div", { className: "faint", style: { fontSize: 12.5, whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis", maxWidth: 160 } }, o.customer.email)), React.createElement("div", { "data-label": t("admin_col_model"), style: { fontSize: 13.5 } }, (lang === "en" ? prod.name_en : prod.name_fr), extra > 0 ? React.createElement("span", { className: "faint" }, " +" + extra) : null), React.createElement("div", { "data-label": t("admin_col_qr"), style: { display: "flex", justifyContent: "center" } }, React.createElement("div", { style: { border: "1px solid var(--line)", borderRadius: 8, overflow: "hidden", lineHeight: 0 } }, React.createElement(window.QRPreview, { text: window.buildQRPayload(u0), plate: window.findFilament(u0.plate).hex, code: window.findFilament(u0.code).hex, size: 46 }))), React.createElement("div", { "data-label": t("admin_col_total"), style: { textAlign: "right", fontWeight: 600, fontSize: 14 } }, window.fmtPrice(o.total, lang)), React.createElement("div", { "data-label": t("admin_col_status") }, React.createElement("select", { className: "status-select " + statusClass(o.status), value: o.status || "new", onChange: (e) => setStatus(o.id, e.target.value) }, React.createElement("option", { value: "new" }, t("admin_status_new")), React.createElement("option", { value: "printing" }, t("admin_status_printing")), React.createElement("option", { value: "shipped" }, t("admin_status_shipped")))), React.createElement("div", { style: { textAlign: "right", display: "flex", flexDirection: "column", gap: 4, alignItems: "flex-end" } }, React.createElement("button", { className: "linkbtn", onClick: () => setDetail(o) }, t("admin_details")), React.createElement("button", { className: "linkbtn linkbtn--muted", onClick: () => downloadOrderAll(o) }, "↓ PNG + SVG + 3MF")), ); }), ), detail && React.createElement(AdminDetail, { order: detail, lang, t, onClose: () => setDetail(null) }), ); } Object.assign(window, { AdminScreen });