// Adminpaneel — role-aware (Owner / Admin) + permission-gated sections.
// Server-side RLS is the source of truth for what each role can do. The
// frontend reads the caller's admin_users row once on mount and toggles
// section visibility from there.
const { useState: __us, useEffect: __ue, useContext: __uc, useMemo: __um, useRef: __ur } = React;

// Green "Betaald" payment badge — shown wherever payment_status === "paid".
// This reads payment_status only; it never changes order.status (which stays
// "In behandeling" until the pickup-ready/completed flow runs).
const PaidBadge = ({ label }) => (
  <span style={{
    display:"inline-flex", alignItems:"center", gap:6,
    padding:"4px 10px", borderRadius:999,
    background:"#e7f4ec", color:"#1f6b3a", border:"1px solid #bfe0cb",
    fontSize:11.5, fontWeight:600, fontFamily:"var(--f-mono)", letterSpacing:".04em", textTransform:"uppercase",
  }}>✓ {label}</span>
);

// ─────────────────────────────────────────────────────────────
// Shared: order row (existing, with translation)
// ─────────────────────────────────────────────────────────────
const AdminOrderRow = ({ order, onChanged, canOrders, highlight }) => {
  const [open, setOpen] = __us(false);
  const [status, setStatus] = __us(order.status || "in_behandeling");
  const [proofUrl, setProofUrl] = __us(order.proof_url || "");
  const [note, setNote] = __us("");
  const [busy, setBusy] = __us(false);
  const [resendBusy, setResendBusy] = __us(false);
  const [resendSuccess, setResendSuccess] = __us(false);
  const [pickupBusy, setPickupBusy] = __us(false);
  const [pickupMsg, setPickupMsg] = __us(null);
  const [pickupNote, setPickupNote] = __us(order.pickup_note || "");
  const [shipBusy, setShipBusy] = __us(false);
  const [shipMsg, setShipMsg] = __us(null);
  const [trackingUrl, setTrackingUrl] = __us(order.tracking_url || "");
  const [completedBusy, setCompletedBusy] = __us(false);
  const [completedMsg, setCompletedMsg] = __us(null);
  const [cashBusy, setCashBusy] = __us(false);
  const [cashMsg, setCashMsg] = __us(null);
  const [cashNote, setCashNote] = __us(order.cash_note || "");
  const [cashAmountReceived, setCashAmountReceived] = __us("");
  const [cashResendBusy, setCashResendBusy] = __us(false);
  const [cashResendMsg, setCashResendMsg] = __us(null);
  const [savedAt, setSavedAt] = __us(null);
  const [err, setErr] = __us(null);
  const langCtx = __uc(window.LangContext || React.createContext({ lang:"nl", t: (nl)=>nl }));
  const t = langCtx.t || ((nl) => nl);
  const LP = window.LP || ((o, f) => o && o[f]);
  const lang = langCtx.lang || "nl";

  const products = Array.isArray(order.products) ? order.products : [];
  const locale = langCtx.lang === "en" ? "en-GB" : "nl-NL";
  const dt = order.created_at ? new Date(order.created_at).toLocaleString(locale, { dateStyle:"medium", timeStyle:"short" }) : "";
  const paidAt = order.paid_at ? new Date(order.paid_at).toLocaleString(locale, { dateStyle:"medium", timeStyle:"short" }) : "—";
  const confirmationSentAt = order.confirmation_sent_at ? new Date(order.confirmation_sent_at).toLocaleString(locale, { dateStyle:"medium", timeStyle:"short" }) : null;
  const paymentStatus = (window.zsb && window.zsb.paymentStatusLabel)
    ? window.zsb.paymentStatusLabel(order.payment_status, lang)
    : (order.payment_status || "pending_payment");
  // Cash-on-pickup ("contant betalen bij ophalen") flags + display values.
  // Dutch date/time notation the user asked for: "29 mei 2026, 14:35".
  // Intl's dateStyle+timeStyle can render "om" instead of a comma, so we build
  // the string from a long date + short time joined by ", ".
  const fmtCashDateTime = (iso) => {
    if (!iso) return null;
    const d = new Date(iso);
    if (isNaN(d.getTime())) return null;
    return d.toLocaleDateString(locale, { day:"numeric", month:"long", year:"numeric" })
      + ", " + d.toLocaleTimeString(locale, { hour:"2-digit", minute:"2-digit" });
  };
  const isCashOrder = order.payment_method === "cash";
  const isCashPending = isCashOrder && order.payment_status === "pending_cash";
  const cashReceivedAt = fmtCashDateTime(order.cash_received_at);
  const cashPaymentEmailSentAt = fmtCashDateTime(order.cash_payment_email_sent_at);
  // Live wisselgeld (change) calculation for the pending-cash action block. The
  // admin types how much cash they received; we preview the change here, but the
  // server ALWAYS recomputes it — this value is display-only.
  const orderTotalNum = Number(order.total) || 0;
  const cashReceivedNum = (() => {
    const raw = String(cashAmountReceived).replace(",", ".").trim();
    if (raw === "") return NaN;
    return parseFloat(raw);
  })();
  const hasCashReceived = Number.isFinite(cashReceivedNum);
  const cashBelowTotal = hasCashReceived && cashReceivedNum < orderTotalNum;
  const cashChangePreview = hasCashReceived && !cashBelowTotal
    ? Math.round((cashReceivedNum - orderTotalNum) * 100) / 100
    : null;
  const cashConfirmDisabled = cashBusy || !hasCashReceived || cashBelowTotal;

  const save = async () => {
    setBusy(true); setErr(null);
    const r = await window.zsb.updateOrder(order.id, {
      status, proof_url: proofUrl || null, note: note || null,
    });
    setBusy(false);
    if (r.error) setErr(r.error);
    else { setSavedAt(Date.now()); setNote(""); onChanged && onChanged(); }
  };

  const quickStatus = async (next) => {
    setStatus(next);
    setBusy(true); setErr(null);
    const r = await window.zsb.updateOrder(order.id, { status: next });
    setBusy(false);
    if (r.error) setErr(r.error);
    else { setSavedAt(Date.now()); onChanged && onChanged(); }
  };

  const handleResend = async () => {
    setResendBusy(true); setErr(null); setResendSuccess(false);
    const r = await window.zsb.resendConfirmationEmail(order.id);
    setResendBusy(false);
    if (r.error) {
      setErr(r.error);
    } else {
      setResendSuccess(true);
      onChanged && onChanged();
    }
  };

  const handleReadyForPickup = async () => {
    setPickupBusy(true); setErr(null); setPickupMsg(null);
    console.log("[admin] ready for pickup click", { order_id: order.id, pickup_note: pickupNote || null });
    const r = await window.zsb.markOrderReadyForPickup(order.id, pickupNote || null);
    setPickupBusy(false);
    if (r.error) {
      setErr(r.error);
    } else if (r.alreadySent) {
      setPickupMsg(t("Ophaalmail is al verzonden","Pickup email already sent"));
    } else {
      setPickupMsg(t("Ophaalbericht verzonden","Pickup notification sent"));
      setStatus("klaar_voor_ophalen");
      onChanged && onChanged();
    }
  };

  const handleCompleted = async () => {
    setCompletedBusy(true); setErr(null); setCompletedMsg(null);
    console.log("[admin] completed click", { order_id: order.id });
    const r = await window.zsb.markOrderCompleted(order.id);
    setCompletedBusy(false);
    if (r.error) {
      setErr(r.error);
    } else if (r.alreadySent) {
      setCompletedMsg(t("Afgerond-mail is al verzonden","Completed email already sent"));
    } else {
      setCompletedMsg(t("Afgerond-mail verzonden","Completed email sent"));
      setStatus("afgerond");
      onChanged && onChanged();
    }
  };

  const handleOutForDelivery = async () => {
    setShipBusy(true); setErr(null); setShipMsg(null);
    const r = await window.zsb.markOrderOutForDelivery(order.id, trackingUrl || null);
    setShipBusy(false);
    if (r.error) {
      setErr(r.error);
    } else if (r.alreadySent) {
      setShipMsg(t("Bezorgmail is al verzonden","Shipment email already sent"));
    } else {
      setShipMsg(t("Bezorgbericht verzonden","Shipment notification sent"));
      setStatus("onderweg");
      onChanged && onChanged();
    }
  };

  // Confirm cash + auto-complete the pickup order (via /api/mark-cash-received).
  // Server-side this sets payment_status="paid" AND status="afgerond", stamps the
  // completion metadata + invoice_number, and sends the completed/invoice email
  // with PDF. No separate cash-receipt mail and no manual "Afgerond" step.
  const handleCashReceived = async () => {
    if (cashConfirmDisabled) return;
    setCashBusy(true); setErr(null); setCashMsg(null);
    console.log("[admin] cash received & complete click", { order_id: order.id });
    const r = await window.zsb.markCashReceived(order.id, cashReceivedNum, cashNote || null);
    setCashBusy(false);
    if (r.error) {
      setErr(r.error);
    } else if (r.alreadyReceived || r.alreadyCompleted) {
      setCashMsg(t("Bestelling was al afgerond","Order was already completed"));
      onChanged && onChanged();
    } else if (r.emailSent === false) {
      setCashMsg(t("Contant ontvangen — bestelling afgerond. De factuurmail kon niet verzonden worden; gebruik 'Verstuur factuurmail opnieuw'.","Cash received — order completed. The invoice email failed; use 'Resend invoice email'."));
      onChanged && onChanged();
    } else {
      setCashMsg(t("Contant ontvangen — bestelling afgerond en factuurmail verzonden.","Cash received — order completed and invoice email sent."));
      onChanged && onChanged();
    }
  };

  // Resend the completed/invoice email when an afgerond cash order never got it
  // (e.g. Resend failed at confirmation time).
  const handleResendCashEmail = async () => {
    setCashResendBusy(true); setErr(null); setCashResendMsg(null);
    const r = await window.zsb.resendCashPaymentReceived(order.id);
    setCashResendBusy(false);
    if (r.error) { setErr(r.error); }
    else if (r.emailSent) { setCashResendMsg(t("Factuurmail verzonden","Invoice email sent")); onChanged && onChanged(); }
    else { setErr(t("Factuurmail kon niet verzonden worden. Probeer het later opnieuw.","Invoice email could not be sent. Please try again later.")); }
  };

  return (
    <div className="card" style={{padding:0, marginBottom:12, overflow:"hidden", boxShadow: highlight ? "0 0 0 2px var(--accent)" : undefined, transition:"box-shadow .5s ease"}}>
      <button onClick={()=>setOpen(o=>!o)} style={{
        width:"100%", padding:"16px 20px", display:"grid",
        gridTemplateColumns:"auto 1fr auto auto", gap:16, alignItems:"center",
        background:"transparent", textAlign:"left",
      }}>
        <div className="mono" style={{fontSize:11, color:"var(--ink-mute)", letterSpacing:".08em"}}>
          {order.id.slice(0,8).toUpperCase()}
        </div>
        <div>
          <div style={{fontWeight:600}}>{order.customer_name || order.customer_email}</div>
          <div style={{fontSize:12, color:"var(--ink-dim)"}}>{order.customer_email} · {dt}</div>
        </div>
        <div style={{fontFamily:"var(--f-display)", fontWeight:700, fontSize:16}}>{formatEUR(Number(order.total) || 0)}</div>
        <div style={{display:"flex", alignItems:"center", gap:8, flexWrap:"wrap", justifyContent:"flex-end"}}>
          {order.payment_status === "paid" && <PaidBadge label={t("Betaald","Paid")}/>}
          {order.status !== "paid" && <StatusBadge status={order.status}/>}
        </div>
      </button>

      {open && (
        <div style={{padding:"4px 20px 20px", borderTop:"1px solid var(--line)"}}>
          <div className="grid-2" style={{gap:18, marginTop:16, marginBottom:18}}>
            <div>
              <div className="mono" style={{fontSize:10.5, color:"var(--ink-mute)", letterSpacing:".1em", textTransform:"uppercase", marginBottom:6}}>{t("Klant","Customer")}</div>
              <div style={{fontSize:13.5, lineHeight:1.6}}>
                {order.customer_name}<br/>
                {order.customer_email}<br/>
                {order.customer_phone || "—"}
              </div>
            </div>
            <div>
              <div className="mono" style={{fontSize:10.5, color:"var(--ink-mute)", letterSpacing:".1em", textTransform:"uppercase", marginBottom:6}}>{t("Levering","Delivery")}</div>
              {order.fulfillment_method === "ophalen" ? (
                <div style={{fontSize:13.5, lineHeight:1.6}}>
                  <strong>{t("Ophalen","Pickup")}</strong><br/>
                  {t("Ophalen in showroom","Pickup at showroom")}
                </div>
              ) : (
                <div style={{fontSize:13.5, lineHeight:1.6}}>
                  <strong>{t("Bezorgen","Delivery")}</strong><br/>
                  {order.delivery_street}<br/>
                  {order.delivery_postcode} {order.delivery_city}
                </div>
              )}
            </div>
          </div>

          <div className="mono" style={{fontSize:10.5, color:"var(--ink-mute)", letterSpacing:".1em", textTransform:"uppercase", marginBottom:6}}>{t("Producten","Products")}</div>
          <div style={{fontSize:13.5, marginBottom:18, lineHeight:1.8}}>
            {products.map((p,i)=>(
              <div key={i} style={{display:"flex", justifyContent:"space-between"}}>
                <span>{p.qty}× {ALL_ITEMS[p.id] ? (LP(ALL_ITEMS[p.id],"name",lang) || LP(ALL_ITEMS[p.id],"short",lang) || p.name) : p.name}</span>
                <span className="mono">{formatEUR((Number(p.price)||0)*(Number(p.qty)||1))}</span>
              </div>
            ))}
            {Number(order.assembly_fee) > 0 && (
              <div style={{display:"flex", justifyContent:"space-between"}}>
                <span>{t("Montage door Zyro","Assembly by Zyro")}</span>
                <span className="mono">{formatEUR(Number(order.assembly_fee))}</span>
              </div>
            )}
            <div style={{display:"flex", justifyContent:"space-between", fontWeight:700, borderTop:"1px solid var(--line)", paddingTop:6, marginTop:6}}>
              <span>{t("Totaal","Total")}</span>
              <span className="mono">{formatEUR(Number(order.total)||0)}</span>
            </div>
          </div>

          <div className="card" style={{padding:14, background:"var(--bg)", marginBottom:16}}>
            <div className="mono" style={{fontSize:10.5, color:"var(--ink-mute)", letterSpacing:".1em", textTransform:"uppercase", marginBottom:8}}>{t("Betaling","Payment")}</div>
            <div style={{display:"grid", gridTemplateColumns:"repeat(2,minmax(0,1fr))", gap:10, fontSize:13}}>
              <div><strong>{t("Betaalmethode","Payment method")}:</strong> {isCashOrder ? t("Contant bij ophalen","Cash on pickup") : t("Online (Mollie)","Online (Mollie)")}</div>
              <div style={{display:"flex", alignItems:"center", gap:8, flexWrap:"wrap"}}>
                <strong>{t("Betaalstatus","Payment status")}:</strong>
                {order.payment_status === "paid" ? <PaidBadge label={t("Betaald","Paid")}/> : <span>{paymentStatus}</span>}
              </div>
              {isCashOrder ? (
                <div><strong>{t("Contant ontvangen op","Cash received at")}:</strong> {cashReceivedAt || t("Nee","No")}</div>
              ) : (
                <div><strong>{t("Mollie ID","Mollie ID")}:</strong> {order.mollie_payment_id || "—"}</div>
              )}
              {isCashOrder && order.cash_amount_received != null && (
                <div><strong>{t("Contant ontvangen","Cash received")}:</strong> {formatEUR(Number(order.cash_amount_received)||0)}</div>
              )}
              {isCashOrder && order.cash_change_given != null && (
                <div><strong>{t("Wisselgeld gegeven","Change given")}:</strong> {formatEUR(Number(order.cash_change_given)||0)}</div>
              )}
              {isCashOrder && order.payment_status === "paid" && (
                <div><strong>{t("Aangenomen door","Accepted by")}:</strong> {order.cash_received_by_name || order.cash_received_by_email || "—"}</div>
              )}
              <div><strong>{t("Betaald op","Paid at")}:</strong> {paidAt}</div>
              <div><strong>{t("Bevestiging","Confirmation")}:</strong> {order.confirmation_number || "—"}</div>
              <div style={{gridColumn:"span 2"}}>
                <strong>{t("Bevestigingsmail verzonden","Confirmation email sent")}:</strong> {confirmationSentAt || t("Nee","No")}
              </div>
              {isCashOrder && order.cash_note && (
                <div style={{gridColumn:"span 2"}}>
                  <strong>{t("Notitie contant","Cash note")}:</strong> {order.cash_note}
                </div>
              )}
            </div>
            {canOrders && (() => {
              const isPaid = order.status === "paid" || order.payment_status === "paid";
              const hasEmail = !!order.customer_email;
              const resendDisabled = resendBusy || !isPaid || !hasEmail;
              return (
                <div style={{marginTop:12, borderTop:"1px solid var(--line)", paddingTop:10}}>
                  <button
                    className="btn btn-ghost btn-sm"
                    onClick={handleResend}
                    disabled={resendDisabled}
                    title={!isPaid ? t("Beschikbaar wanneer betaling is afgerond","Available once the payment is paid") : (!hasEmail ? t("Geen klantemail bekend","No customer email on file") : "")}
                    style={{borderColor:"var(--accent)", color:"var(--accent)", background:"#fff", opacity: resendDisabled ? .55 : 1, cursor: resendDisabled ? "not-allowed" : "pointer"}}
                  >
                    {resendBusy ? t("Verzenden...","Sending...") : t("Verstuur bevestiging opnieuw","Resend confirmation")}
                  </button>
                  {resendSuccess && <span style={{fontSize:12.5, color:"var(--ok)", marginLeft:10}}>✓ {t("Bevestiging verzonden","Confirmation sent")}</span>}
                </div>
              );
            })()}

            {canOrders && isCashOrder && order.status === "afgerond" && !order.completed_email_sent_at && (
              <div style={{marginTop:12, borderTop:"1px solid var(--line)", paddingTop:10}}>
                <div style={{fontSize:12.5, color:"var(--ink-dim)", marginBottom:8}}>
                  {t("De bestelling is afgerond, maar de factuurmail is nog niet verzonden.","The order is completed, but the invoice email hasn't been sent yet.")}
                </div>
                <button
                  className="btn btn-ghost btn-sm"
                  onClick={handleResendCashEmail}
                  disabled={cashResendBusy || !order.customer_email}
                  title={!order.customer_email ? t("Geen klantemail bekend","No customer email on file") : ""}
                  style={{borderColor:"var(--accent)", color:"var(--accent)", background:"#fff", opacity:(cashResendBusy || !order.customer_email) ? .55 : 1, cursor:(cashResendBusy || !order.customer_email) ? "not-allowed" : "pointer"}}
                >
                  {cashResendBusy ? t("Verzenden…","Sending…") : t("Verstuur factuurmail opnieuw","Resend invoice email")}
                </button>
                {cashResendMsg && <span style={{fontSize:12.5, color:"var(--ok)", marginLeft:10}}>✓ {cashResendMsg}</span>}
              </div>
            )}

            {canOrders && isCashPending && (
              <div style={{marginTop:12, borderTop:"1px solid var(--line)", paddingTop:10}}>
                <div style={{fontSize:12.5, color:"var(--ink-dim)", marginBottom:8}}>
                  {t("Deze bestelling wacht op contante betaling bij ophalen. Bevestig pas nadat je het geld hebt ontvangen.","This order is awaiting cash payment on pickup. Only confirm after you've received the cash.")}
                </div>
                <div style={{display:"flex", justifyContent:"space-between", fontSize:13, marginBottom:8}}>
                  <span style={{color:"var(--ink-dim)"}}>{t("Ordertotaal","Order total")}</span>
                  <span className="mono" style={{fontWeight:700}}>{formatEUR(orderTotalNum)}</span>
                </div>
                <div className="field" style={{marginBottom:8}}>
                  <label className="field-label">{t("Contant ontvangen bedrag","Cash amount received")}</label>
                  <input
                    className="input"
                    type="text"
                    inputMode="decimal"
                    value={cashAmountReceived}
                    onChange={e=>setCashAmountReceived(e.target.value)}
                    placeholder={t("Bijv. 50,00","E.g. 50.00")}
                  />
                </div>
                {hasCashReceived && !cashBelowTotal && (
                  <div style={{display:"flex", justifyContent:"space-between", fontSize:13, marginBottom:8, color:"var(--ok)"}}>
                    <span style={{fontWeight:600}}>{t("Wisselgeld terug te geven","Change to return")}</span>
                    <span className="mono" style={{fontWeight:700}}>{formatEUR(cashChangePreview || 0)}</span>
                  </div>
                )}
                {cashBelowTotal && (
                  <div style={{fontSize:12.5, color:"var(--bad)", marginBottom:8}}>
                    {t("Ontvangen bedrag is lager dan het ordertotaal.","Received amount is lower than the order total.")}
                  </div>
                )}
                <div className="field" style={{marginBottom:8}}>
                  <label className="field-label">{t("Notitie contante betaling (optioneel)","Cash payment note (optional)")}</label>
                  <input className="input" value={cashNote} onChange={e=>setCashNote(e.target.value)} placeholder={t("Bijv. 'Contant ontvangen, gepast bedrag'","E.g. 'Cash received, exact amount'")}/>
                </div>
                <button
                  className="btn btn-accent btn-sm"
                  onClick={handleCashReceived}
                  disabled={cashConfirmDisabled}
                  title={t("Bevestig dat de klant contant heeft betaald bij ophalen","Confirm the customer paid cash on pickup")}
                  style={{opacity: cashConfirmDisabled ? .55 : 1, cursor: cashConfirmDisabled ? "not-allowed" : "pointer"}}
                >
                  {cashBusy ? t("Bevestigen…","Confirming…") : t("Contant ontvangen & afronden","Confirm cash & complete")}
                </button>
                {cashMsg && <span style={{fontSize:12.5, color:"var(--ok)", marginLeft:10}}>✓ {cashMsg}</span>}
              </div>
            )}
          </div>

          {(() => {
            const isPickup = order.fulfillment_method === "ophalen" || order.delivery_method === "pickup";
            // Cash pickup auto-completes on "Contant ontvangen & afronden", so the
            // Klaar-voor-ophalen step is irrelevant — hide the whole pickup block.
            // Online pickup keeps its normal ready-for-pickup flow.
            if (isCashOrder && isPickup) return null;
            const pickupReadyAt = order.pickup_ready_at ? new Date(order.pickup_ready_at).toLocaleString(locale, { dateStyle:"medium", timeStyle:"short" }) : null;
            const pickupReadySentAt = order.pickup_ready_sent_at ? new Date(order.pickup_ready_sent_at).toLocaleString(locale, { dateStyle:"medium", timeStyle:"short" }) : null;
            const deliveryStartedAt = order.delivery_started_at ? new Date(order.delivery_started_at).toLocaleString(locale, { dateStyle:"medium", timeStyle:"short" }) : null;
            const deliveryEmailSentAt = order.delivery_email_sent_at ? new Date(order.delivery_email_sent_at).toLocaleString(locale, { dateStyle:"medium", timeStyle:"short" }) : null;
            return (
              <div className="card" style={{padding:14, background:"var(--bg)", marginBottom:16}}>
                <div className="mono" style={{fontSize:10.5, color:"var(--ink-mute)", letterSpacing:".1em", textTransform:"uppercase", marginBottom:8}}>
                  {isPickup ? t("Ophalen","Pickup") : t("Bezorging","Delivery")}
                </div>
                {isPickup ? (
                  <div style={{display:"grid", gridTemplateColumns:"repeat(2,minmax(0,1fr))", gap:10, fontSize:13}}>
                    <div style={{gridColumn:"span 2"}}>
                      <strong>{t("Ophalen in showroom","Pickup at showroom")}</strong>
                    </div>
                    <div><strong>{t("Klaar voor ophalen op","Ready for pickup at")}:</strong> {pickupReadyAt || "—"}</div>
                    <div><strong>{t("Ophaalmail verzonden","Pickup email sent")}:</strong> {pickupReadySentAt || t("Nee","No")}</div>
                    {canOrders && (
                      <div className="field" style={{gridColumn:"span 2"}}>
                        <label className="field-label">{t("Ophaalnotitie (optioneel — komt in de mail)","Pickup note (optional — appears in the email)")}</label>
                        <input className="input" value={pickupNote} onChange={e=>setPickupNote(e.target.value)} placeholder={t("Bijv. 'Open zaterdag op afspraak'","E.g. 'Open Saturday by appointment'")}/>
                      </div>
                    )}
                  </div>
                ) : (
                  <div style={{display:"grid", gridTemplateColumns:"repeat(2,minmax(0,1fr))", gap:10, fontSize:13}}>
                    <div style={{gridColumn:"span 2", lineHeight:1.6}}>
                      <strong>{t("Bezorgadres","Delivery address")}:</strong>{" "}
                      {order.delivery_address_verified ? <span style={{fontSize:11, color:"var(--ok)"}}>✓ {t("Geverifieerd","Verified")}</span> : <span style={{fontSize:11, color:"var(--bad)"}}>{t("Niet geverifieerd","Not verified")}</span>}
                      <br/>
                      {order.delivery_name ? <>{order.delivery_name}<br/></> : null}
                      {order.delivery_formatted_address
                        ? order.delivery_formatted_address
                        : ((order.delivery_street || "—") + (order.delivery_postcode || order.delivery_city ? ", " + [order.delivery_postcode, order.delivery_city].filter(Boolean).join(" ") : "") + (order.delivery_country || order.shipping_country ? ", " + (order.delivery_country || order.shipping_country) : ""))
                      }
                      {order.delivery_phone ? <><br/><span style={{color:"var(--ink-mute)"}}>{t("Tel.","Phone")}: {order.delivery_phone}</span></> : null}
                    </div>
                    <div><strong>{t("Onderweg sinds","Out for delivery since")}:</strong> {deliveryStartedAt || "—"}</div>
                    <div><strong>{t("Bezorgmail verzonden","Shipment email sent")}:</strong> {deliveryEmailSentAt || t("Nee","No")}</div>
                    {canOrders && (
                      <div className="field" style={{gridColumn:"span 2"}}>
                        <label className="field-label">{t("Tracking-URL (optioneel — komt in de mail)","Tracking URL (optional — appears in the email)")}</label>
                        <input className="input" type="url" value={trackingUrl} onChange={e=>setTrackingUrl(e.target.value)} placeholder="https://…"/>
                      </div>
                    )}
                  </div>
                )}
              </div>
            );
          })()}

          {(() => {
            const completedAt = order.completed_at ? new Date(order.completed_at).toLocaleString(locale, { dateStyle:"medium", timeStyle:"short" }) : null;
            const completedEmailSentAt = order.completed_email_sent_at ? new Date(order.completed_email_sent_at).toLocaleString(locale, { dateStyle:"medium", timeStyle:"short" }) : null;
            const reviewRequestedAt = order.review_requested_at ? new Date(order.review_requested_at).toLocaleString(locale, { dateStyle:"medium", timeStyle:"short" }) : null;
            return (
              <div className="card" style={{padding:14, background:"var(--bg)", marginBottom:16}}>
                <div className="mono" style={{fontSize:10.5, color:"var(--ink-mute)", letterSpacing:".1em", textTransform:"uppercase", marginBottom:8}}>
                  {t("Afronding","Completion")}
                </div>
                <div style={{display:"grid", gridTemplateColumns:"repeat(2,minmax(0,1fr))", gap:10, fontSize:13}}>
                  <div><strong>{t("Afgerond op","Completed at")}:</strong> {completedAt || "—"}</div>
                  <div><strong>{t("Afgerond-mail verzonden","Completed email sent")}:</strong> {completedEmailSentAt || t("Nee","No")}</div>
                  <div><strong>{t("Factuurnummer","Invoice number")}:</strong> {order.invoice_number || "—"}</div>
                  <div><strong>{t("Reviewverzoek op","Review requested at")}:</strong> {reviewRequestedAt || "—"}</div>
                  {order.invoice_pdf_url && (
                    <div style={{gridColumn:"span 2"}}>
                      <strong>{t("Factuur PDF","Invoice PDF")}:</strong>{" "}
                      <a href={order.invoice_pdf_url} target="_blank" rel="noopener noreferrer" className="accent">{t("Download","Download")}</a>
                    </div>
                  )}
                </div>
              </div>
            );
          })()}

          <div style={{display:"flex", gap:6, flexWrap:"wrap", marginBottom:16}}>
            {[
              {id:"geaccepteerd",      t:t("Accepteren","Accept"),                    type:"status"},
              {id:"geweigerd",         t:t("Weigeren","Reject"),                      type:"status"},
              {id:"klaar_voor_ophalen",t:t("Klaar voor ophalen","Ready for pickup"),  type:"pickup"},
              {id:"onderweg",          t:t("Onderweg","On the way"),                  type:"ship"},
              {id:"afgerond",          t:t("Afgerond","Completed"),                   type:"completed"},
              {id:"geannuleerd",       t:t("Annuleren","Cancel"),                     type:"status"},
            ].filter(b => {
              const fm = order.fulfillment_method;
              // Hide pickup-only button for delivery orders and vice versa.
              if (b.type === "pickup" && fm === "bezorgen") return false;
              if (b.type === "ship"   && fm === "ophalen")  return false;
              // Cash pickup auto-completes via the cash action — no manual
              // "Klaar voor ophalen" step. (Online pickup keeps it.)
              if (b.type === "pickup" && isCashOrder) return false;
              // Cash orders may ONLY be finished via "Contant ontvangen &
              // afronden" — the generic Afgerond button would complete the order
              // without recording the cash payment. Hide it for all cash orders
              // (a not-yet-paid cash order must go through the cash flow; an
              // already-afgerond one needs no manual Afgerond). Online pickup and
              // delivery keep their normal Afgerond button.
              if (b.type === "completed" && isCashOrder) return false;
              return true;
            }).map(b => {
              const isPickupBtn    = b.type === "pickup";
              const isShipBtn      = b.type === "ship";
              const isCompletedBtn = b.type === "completed";
              const disabled = busy
                || (isPickupBtn    && pickupBusy)
                || (isShipBtn      && shipBusy)
                || (isCompletedBtn && completedBusy);
              const onClick = isPickupBtn
                ? handleReadyForPickup
                : isShipBtn
                  ? handleOutForDelivery
                  : isCompletedBtn
                    ? handleCompleted
                    : (() => quickStatus(b.id));
              const label = (isPickupBtn && pickupBusy) || (isShipBtn && shipBusy) || (isCompletedBtn && completedBusy)
                ? t("Verzenden…","Sending…")
                : b.t;
              return (
                <button key={b.id} className="btn btn-ghost btn-sm" onClick={onClick} disabled={disabled}>
                  {label}
                </button>
              );
            })}
          </div>
          {(pickupMsg || shipMsg || completedMsg) && (
            <div style={{marginBottom:14, fontSize:12.5, color:"var(--ok)"}}>
              {pickupMsg    && <div>✓ {pickupMsg}</div>}
              {shipMsg      && <div>✓ {shipMsg}</div>}
              {completedMsg && <div>✓ {completedMsg}</div>}
            </div>
          )}

          <div className="grid-2" style={{gap:14, marginBottom:14}}>
            <div className="field">
              <label className="field-label">{t("Status","Status")}</label>
              <select className="select" value={status} onChange={e=>setStatus(e.target.value)}>
                {window.zsb.statusOrder.filter(s => s !== "paid" || status === "paid").map(s => (
                  <option key={s} value={s}>{window.zsb.statusLabel ? window.zsb.statusLabel(s, langCtx.lang) : window.zsb.statusLabels[s]}</option>
                ))}
              </select>
            </div>
            <div className="field">
              <label className="field-label">{t("Aankoopbewijs URL","Receipt URL")}</label>
              <input className="input" value={proofUrl} onChange={e=>setProofUrl(e.target.value)} placeholder="https://…/aankoopbewijs.pdf"/>
            </div>
          </div>

          <div className="field" style={{marginBottom:14}}>
            <label className="field-label">{t("Notitie (optioneel — in order history)","Note (optional — in order history)")}</label>
            <input className="input" value={note} onChange={e=>setNote(e.target.value)} placeholder={t("Bijv. 'Klant gebeld, afspraak vrijdag'","E.g. 'Called customer, Friday appointment'")}/>
          </div>

          {err && <div style={{color:"var(--bad)", fontSize:13, marginBottom:10}}>{err}</div>}
          <div className="gap-12">
            <button onClick={save} className="btn btn-primary" disabled={busy}>{busy ? t("Opslaan…","Saving…") : t("Opslaan","Save")}</button>
            {savedAt && <span style={{fontSize:12.5, color:"var(--ok)"}}>✓ {t("Opgeslagen","Saved")}</span>}
          </div>

          <div style={{marginTop:20, paddingTop:14, borderTop:"1px dashed var(--line)"}}>
            <div className="mono" style={{fontSize:10.5, color:"var(--ink-mute)", letterSpacing:".1em", textTransform:"uppercase", marginBottom:10}}>{t("Status-geschiedenis","Status history")}</div>
            <OrderHistory orderId={order.id} refreshKey={savedAt}/>
          </div>
        </div>
      )}
    </div>
  );
};

// ─────────────────────────────────────────────────────────────
// Product price card (live aanpasbaar)
// ─────────────────────────────────────────────────────────────
const ProductPriceCard = ({ row, onSaved, canEdit }) => {
  const [price, setPrice] = __us(row.price);
  const [oldPrice, setOldPrice] = __us(row.old_price == null ? "" : row.old_price);
  const [asmPrice, setAsmPrice] = __us(row.assembly_price);
  const [active, setActive] = __us(!!row.active);
  const [sortOrder, setSortOrder] = __us(row.sort_order == null ? 0 : row.sort_order);
  const [availability, setAvailability] = __us(row.availability_status || "available");
  const [busy, setBusy] = __us(false);
  const [err, setErr] = __us(null);
  const [savedAt, setSavedAt] = __us(null);
  const langCtx = __uc(window.LangContext || React.createContext({ lang:"nl", t: (nl)=>nl }));
  const t = langCtx.t || ((nl) => nl);
  const LP = window.LP || ((o, f) => o && o[f]);

  const category = row.category || "bike";
  const isBike = category === "bike";
  const catLabel = category === "bike" ? t("Fiets","Bike") : category === "accessory" ? t("Accessoire","Accessory") : t("Dienst","Service");
  const catTone = { bike:"var(--accent)", accessory:"var(--ok)", service:"var(--warn)" }[category] || "var(--ink-mute)";
  const mirrorItem = window.ALL_ITEMS && window.ALL_ITEMS[row.slug];
  const displayName = LP(mirrorItem, "name", langCtx.lang) || LP(mirrorItem, "short", langCtx.lang) || row.name;

  __ue(() => {
    setPrice(row.price);
    setOldPrice(row.old_price == null ? "" : row.old_price);
    setAsmPrice(row.assembly_price);
    setActive(!!row.active);
    setSortOrder(row.sort_order == null ? 0 : row.sort_order);
    setAvailability(row.availability_status || "available");
  }, [row.id, row.price, row.old_price, row.assembly_price, row.active, row.sort_order, row.availability_status]);

  const save = async (e) => {
    e.preventDefault();
    setErr(null);
    const p = Number(price), a = Number(asmPrice);
    const o = oldPrice === "" ? null : Number(oldPrice);
    const so = sortOrder === "" ? 0 : Number(sortOrder);
    if (!isFinite(p) || p < 0) { setErr(t("Prijs moet een geldig getal zijn.","Price must be a valid number.")); return; }
    if (isBike && (!isFinite(a) || a < 0)) { setErr(t("Montageprijs moet een geldig getal zijn.","Assembly price must be a valid number.")); return; }
    if (o !== null && (!isFinite(o) || o < 0)) { setErr(t("Oude prijs moet leeg zijn of een geldig getal.","Old price must be empty or a valid number.")); return; }
    if (!["available","sold_out","unavailable"].includes(availability)) {
      setErr(t("Onbekende beschikbaarheidsstatus.","Unknown availability status."));
      return;
    }
    setBusy(true);
    const patch = { price: p, old_price: o, active: !!active, sort_order: so, availability_status: availability };
    if (isBike) patch.assembly_price = a;
    const r = await window.zsb.updateProductWithAudit(row.slug || row.id, patch);
    setBusy(false);
    if (r.error) { setErr(r.error); return; }
    setSavedAt(Date.now());
    onSaved && onSaved();
  };

  const lastEdit = row.updated_at
    ? new Date(row.updated_at).toLocaleString(langCtx.lang === "en" ? "en-GB" : "nl-NL", { dateStyle:"short", timeStyle:"short" })
    : null;

  return (
    <div className="card" style={{padding:22}}>
      <div style={{display:"flex", justifyContent:"space-between", alignItems:"flex-start", gap:10, marginBottom:14, flexWrap:"wrap"}}>
        <div>
          <div className="mono" style={{fontSize:10.5, color:"var(--ink-mute)", letterSpacing:".1em", textTransform:"uppercase"}}>
            <span style={{color: catTone, marginRight:8, fontWeight:700}}>{catLabel}</span>
            <span>{row.slug}</span>
          </div>
          <div style={{fontFamily:"var(--f-display)", fontSize:20, fontWeight:700, marginTop:2}}>{displayName}</div>
        </div>
        <label style={{display:"inline-flex", alignItems:"flex-start", gap:8, fontSize:13, maxWidth:240}}>
          <input type="checkbox" checked={active} onChange={e=>setActive(e.target.checked)} style={{accentColor:"var(--accent)", marginTop:3}} disabled={!canEdit}/>
          <span>
            <strong>{t("Zichtbaar op website","Visible on website")}</strong>
            <div style={{fontSize:11.5, color:"var(--ink-mute)", marginTop:2, lineHeight:1.4}}>
              {active
                ? t("Aan: product zichtbaar in shop. Gebruik Beschikbaarheid voor verkocht / tijdelijk niet beschikbaar.","On: product visible in shop. Use Availability for sold / temporarily unavailable.")
                : t("Uit: product volledig verborgen. Niet gebruiken voor uitverkocht!","Off: product fully hidden. Don't use for sold out!")}
            </div>
          </span>
        </label>
      </div>

      <form onSubmit={save}>
        <div className="grid-2" style={{marginBottom:12}}>
          <div className="field">
            <label className="field-label">{t("Prijs (€)","Price (€)")}</label>
            <input className="input" type="number" min="0" step="1" value={price} onChange={e=>setPrice(e.target.value)} required disabled={!canEdit}/>
          </div>
          <div className="field">
            <label className="field-label">{t("Oude prijs (€) — optioneel","Old price (€) — optional")}</label>
            <input className="input" type="number" min="0" step="1" value={oldPrice} onChange={e=>setOldPrice(e.target.value)} placeholder={t("leeg = geen korting tonen","empty = no discount tag")} disabled={!canEdit}/>
          </div>
        </div>
        {isBike && (
          <div className="field" style={{marginBottom:12}}>
            <label className="field-label">{t("Montageprijs (€)","Assembly price (€)")}</label>
            <input className="input" type="number" min="0" step="1" value={asmPrice} onChange={e=>setAsmPrice(e.target.value)} required disabled={!canEdit}/>
          </div>
        )}
        <div className="grid-2" style={{marginBottom:14}}>
          <div className="field">
            <label className="field-label">{t("Sortering","Sort order")}</label>
            <input className="input" type="number" step="1" value={sortOrder} onChange={e=>setSortOrder(e.target.value)} disabled={!canEdit}/>
          </div>
          <div className="field">
            <label className="field-label">{t("Beschikbaarheid","Availability")}</label>
            <select className="select" value={availability} onChange={e=>setAvailability(e.target.value)} disabled={!canEdit}>
              <option value="available">{t("Beschikbaar","Available")}</option>
              <option value="sold_out">{t("Uitverkocht","Sold out")}</option>
              <option value="unavailable">{t("Tijdelijk niet beschikbaar","Temporarily unavailable")}</option>
            </select>
            <div style={{fontSize:11.5, color:"var(--ink-mute)", marginTop:4}}>
              {availability === "available"
                ? t("Klanten kunnen kopen.","Customers can buy.")
                : t("Zichtbaar maar niet koopbaar.","Visible but not buyable.")}
            </div>
          </div>
        </div>

        {err && <div style={{color:"var(--bad)", fontSize:13, marginBottom:10}}>{err}</div>}
        {canEdit ? (
          <div className="gap-12">
            <button type="submit" className="btn btn-primary" disabled={busy}>{busy ? t("Opslaan…","Saving…") : t("Opslaan","Save")}</button>
            {savedAt && <span style={{fontSize:12.5, color:"var(--ok)"}}>✓ {t("Opgeslagen · prijzen zijn direct live","Saved · prices are live")}</span>}
          </div>
        ) : (
          <div style={{fontSize:12.5, color:"var(--ink-mute)"}}>{t("Alleen-lezen — je hebt geen prijsbeheer-permissie.","Read-only — you don't have price-manage permission.")}</div>
        )}
        {lastEdit && (
          <div style={{marginTop:10, fontSize:11.5, color:"var(--ink-mute)"}}>
            {t("Laatst aangepast","Last edited")}: {lastEdit}
          </div>
        )}
      </form>
    </div>
  );
};

const ProductsAdminSection = ({ canEdit }) => {
  const productsStore = __uc(window.ProductsContext);
  const langCtx = __uc(window.LangContext || React.createContext({ lang:"nl", t: (nl)=>nl }));
  const t = langCtx.t || ((nl) => nl);
  const [category, setCategory] = __us("all");
  const rows = productsStore && productsStore.products ? Object.values(productsStore.products) : [];

  __ue(() => {
    if (productsStore && productsStore.refresh) productsStore.refresh();
    // eslint-disable-next-line
  }, [canEdit]);

  if (!window.zsb || !window.zsb.isConfigured) {
    return <div className="card" style={{padding:18, background:"#fff8e6", border:"1px solid #f0d985", fontSize:13.5, marginBottom:18}}>
      <strong>{t("Supabase nog niet geconfigureerd.","Supabase not configured yet.")}</strong>
    </div>;
  }
  if (!productsStore.ready) return <div className="card" style={{padding:24, color:"var(--ink-dim)"}}>{t("Producten laden…","Loading products…")}</div>;
  if (productsStore.error) return <div className="card" style={{padding:18, background:"#fbe9e9", border:"1px solid #efc5c5", fontSize:13.5}}>{productsStore.error}</div>;
  if (rows.length === 0) return <div className="card" style={{padding:24, color:"var(--ink-dim)"}}>
    <strong>{t("Geen producten gevonden.","No products found.")}</strong><br/>
    <span style={{fontSize:12.5, color:"var(--ink-dim)"}}>{t("Run supabase-products-settings-shipping.sql om accessoires en montage als beheerbare producten te seeden.","Run supabase-products-settings-shipping.sql to seed accessories and services as manageable products.")}</span>
  </div>;

  const grouped = { bike:[], accessory:[], service:[], other:[] };
  rows.forEach(r => { (grouped[r.category] || grouped.other).push(r); });
  Object.values(grouped).forEach(arr => arr.sort((a,b) => (a.sort_order||0) - (b.sort_order||0) || (a.name||"").localeCompare(b.name||"")));

  const visible = category === "all" ? rows.slice().sort((a,b) => {
    const order = { bike:0, accessory:1, service:2 };
    return (order[a.category]||9) - (order[b.category]||9) || (a.sort_order||0) - (b.sort_order||0);
  }) : (grouped[category] || []);

  const tabs = [
    { id:"all",       label:t("Alles","All"),               n:rows.length },
    { id:"bike",      label:t("Fietsen","Bikes"),           n:grouped.bike.length },
    { id:"accessory", label:t("Accessoires","Accessories"), n:grouped.accessory.length },
    { id:"service",   label:t("Diensten","Services"),       n:grouped.service.length },
  ];

  return (
    <div>
      <div style={{display:"flex", gap:6, marginBottom:16, flexWrap:"wrap"}}>
        {tabs.map(tab => (
          <button key={tab.id} onClick={()=>setCategory(tab.id)} style={{
            padding:"7px 12px", borderRadius:8, fontSize:12.5, fontWeight:600,
            background: category === tab.id ? "var(--ink)" : "var(--paper)",
            color:      category === tab.id ? "var(--bg)" : "var(--ink)",
            border: `1px solid ${category === tab.id ? "var(--ink)" : "var(--line)"}`,
          }}>{tab.label} · {tab.n}</button>
        ))}
      </div>
      <div style={{display:"grid", gridTemplateColumns:"1fr 1fr", gap:14}} className="prod-admin-grid">
        {visible.map(r => <ProductPriceCard key={r.id} row={r} onSaved={productsStore.refresh} canEdit={canEdit}/>)}
        <style>{`@media (max-width:780px){.prod-admin-grid{grid-template-columns:1fr !important}}`}</style>
      </div>
    </div>
  );
};

// ─────────────────────────────────────────────────────────────
// Admin Management (admin_users CRUD) — visible to Owner + admins with can_manage_admins
// ─────────────────────────────────────────────────────────────
const PermCheckbox = ({ label, value, onChange, disabled }) => (
  <label style={{display:"flex", alignItems:"center", gap:8, fontSize:13, padding:"6px 0"}}>
    <input type="checkbox" checked={!!value} onChange={e=>onChange(e.target.checked)} disabled={disabled} style={{accentColor:"var(--accent)"}}/>
    <span style={{color: disabled ? "var(--ink-mute)" : "var(--ink)"}}>{label}</span>
  </label>
);

// Compact ✓/— indicator used in the admin table rows.
const PermChip = ({ label, on }) => (
  <span style={{
    display:"inline-flex", alignItems:"center", gap:4, fontSize:11, padding:"2px 8px", borderRadius:999,
    background: on ? "#e7f4ec" : "var(--bg)", color: on ? "#1f6b3a" : "var(--ink-mute)",
    border: "1px solid " + (on ? "#bfe0cb" : "var(--line)"), whiteSpace:"nowrap",
  }}>{on ? "✓" : "—"} {label}</span>
);

// Add-admin presets. ALL create role="admin" (the UI never creates an owner);
// the distinction between "medewerker" and "admin" is purely the permission flags.
// Audit logs are OWNER-ONLY (RLS + UI). can_view_audit_logs is intentionally
// absent from every preset, so no preset — not even "Admin (volledig)" — can
// grant audit access. The column still exists in the DB but is inert.
const ADMIN_PRESETS = [
  { id:"employee",   labelNl:"Medewerker",       labelEn:"Employee",      perms:{ can_manage_orders:true,  can_manage_prices:false, can_manage_settings:false, can_manage_admins:false } },
  { id:"admin_base", labelNl:"Admin (beperkt)",  labelEn:"Admin (basic)", perms:{ can_manage_orders:true,  can_manage_prices:false, can_manage_settings:false, can_manage_admins:false } },
  { id:"admin_full", labelNl:"Admin (volledig)", labelEn:"Admin (full)",  perms:{ can_manage_orders:true,  can_manage_prices:true,  can_manage_settings:true,  can_manage_admins:false } },
];

const AdminUserRow = ({ row, currentAdmin, onChanged }) => {
  const langCtx = __uc(window.LangContext || React.createContext({ lang:"nl", t: (nl)=>nl }));
  const t = langCtx.t || ((nl) => nl);
  const locale = langCtx.lang === "en" ? "en-GB" : "nl-NL";
  const [edit, setEdit] = __us(false);
  const [busy, setBusy] = __us(false);
  const [err, setErr] = __us(null);
  const [perms, setPerms] = __us({
    can_manage_prices:  row.can_manage_prices,
    can_manage_orders:  row.can_manage_orders,
    can_manage_settings:row.can_manage_settings,
    can_manage_admins:  row.can_manage_admins,
  });

  const isOwnerRow   = row.role === "owner";
  const isSelfRow    = currentAdmin && (row.email.toLowerCase() === (currentAdmin.email || "").toLowerCase());
  const callerOwner  = !!(currentAdmin && currentAdmin.role === "owner");
  // OWNER-ONLY management. The owner row is NEVER editable from the UI (so the
  // owner can't accidentally deactivate/demote itself). RLS independently blocks
  // every non-owner write, regardless of what the UI renders.
  const canEditRow   = callerOwner && !isOwnerRow;
  const lastChanged  = row.updated_at || row.created_at;

  // All writes go through owner-only RPCs. The RPCs gate on private.is_owner(),
  // refuse the owner row + the caller's own account, and the audit trigger writes
  // ONE notification row per UPDATE (priority: access_revoked > reactivated >
  // rights_changed) so the target's browser receives the change via realtime.
  const save = async () => {
    setBusy(true); setErr(null);
    const r = await window.zsb.ownerUpdateAdminUser(row.id, {
      can_manage_orders:   perms.can_manage_orders,
      can_manage_prices:   perms.can_manage_prices,
      can_manage_settings: perms.can_manage_settings,
    });
    setBusy(false);
    if (r.error) { setErr(r.error); return; }
    setEdit(false);
    onChanged && onChanged();
  };

  const toggleActive = async () => {
    setBusy(true); setErr(null);
    const r = await window.zsb.ownerUpdateAdminUser(row.id, { active: !row.active });
    setBusy(false);
    if (r.error) { setErr(r.error); return; }
    onChanged && onChanged();
  };

  // Owner-only soft delete: sets active=false + deleted_at + deleted_by. Row
  // stays in admin_users (reactivatable). Target receives access_revoked notif.
  const handleDelete = async () => {
    if (!window.confirm(t("Weet je zeker dat je deze admin wilt verwijderen? Je kunt de toegang later weer activeren.","Are you sure you want to delete this admin? You can reactivate access later."))) return;
    setBusy(true); setErr(null);
    const r = await window.zsb.ownerSoftDeleteAdminUser(row.id);
    setBusy(false);
    if (r.error) { setErr(r.error); return; }
    onChanged && onChanged();
  };

  // Owner-only reactivate: clears deleted_at + sets active=true. Target
  // receives a reactivated notification.
  const handleReactivate = async () => {
    setBusy(true); setErr(null);
    const r = await window.zsb.ownerReactivateAdminUser(row.id);
    setBusy(false);
    if (r.error) { setErr(r.error); return; }
    onChanged && onChanged();
  };

  const isSoftDeleted = !!row.deleted_at;

  return (
    <div className="card" style={{padding:16, marginBottom:10}}>
      <div style={{display:"flex", justifyContent:"space-between", alignItems:"flex-start", gap:14, flexWrap:"wrap"}}>
        <div style={{minWidth:190}}>
          <div style={{fontWeight:600}}>{row.email}</div>
          <div style={{fontSize:12, color:"var(--ink-dim)", marginTop:3, display:"flex", alignItems:"center", gap:6, flexWrap:"wrap"}}>
            <span style={{
              display:"inline-block", padding:"2px 8px", borderRadius:4, fontSize:10, fontWeight:600,
              background: isOwnerRow ? "rgba(0,107,255,.12)" : "var(--bg-2)",
              color: isOwnerRow ? "var(--accent)" : "var(--ink-dim)",
              border: isOwnerRow ? "1px solid rgba(0,107,255,.3)" : "1px solid var(--line)",
              textTransform:"uppercase", letterSpacing:".08em", fontFamily:"var(--f-mono)",
            }}>{row.role}</span>
            <span style={{color: row.active ? "var(--ok)" : "var(--bad)", fontWeight:600}}>
              {row.active ? t("Actief","Active") : isSoftDeleted ? t("Verwijderd","Deleted") : t("Uitgeschakeld","Disabled")}
            </span>
            {isSelfRow && <span style={{color:"var(--ink-mute)"}}>· {t("dat ben jij","that's you")}</span>}
            {isSoftDeleted && (
              <span style={{color:"var(--ink-mute)"}}>· {t("verwijderd op","deleted on")} {new Date(row.deleted_at).toLocaleString(locale, { dateStyle:"short", timeStyle:"short" })}</span>
            )}
          </div>
        </div>

        <div style={{display:"flex", gap:6, flexWrap:"wrap", flex:1, justifyContent:"center", alignItems:"flex-start"}}>
          <PermChip label={t("Orders","Orders")}     on={row.can_manage_orders}/>
          <PermChip label={t("Prijzen","Prices")}    on={row.can_manage_prices}/>
          <PermChip label={t("Settings","Settings")} on={row.can_manage_settings}/>
        </div>

        <div style={{textAlign:"right", minWidth:150}}>
          <div className="mono" style={{fontSize:10, color:"var(--ink-mute)", marginBottom:6, lineHeight:1.5}}>
            {t("Laatst aangepast","Last changed")}<br/>
            {lastChanged ? new Date(lastChanged).toLocaleString(locale, { dateStyle:"medium", timeStyle:"short" }) : "—"}
          </div>
          {canEditRow ? (
            <div style={{display:"flex", gap:6, justifyContent:"flex-end", flexWrap:"wrap"}}>
              {!isSoftDeleted && (
                <button onClick={()=>setEdit(o=>!o)} className="btn btn-ghost btn-sm">{edit ? t("Sluiten","Close") : t("Rechten","Permissions")}</button>
              )}
              {/* Soft-deleted rows: only show Reactivate (clears deleted_at). */}
              {isSoftDeleted ? (
                <button onClick={handleReactivate} disabled={busy} className="btn btn-ghost btn-sm"
                  style={{borderColor:"var(--ok)", color:"var(--ok)"}}>
                  {busy ? "…" : t("Activeren","Reactivate")}
                </button>
              ) : (
                <>
                  <button onClick={toggleActive} disabled={busy} className="btn btn-ghost btn-sm"
                    style={{borderColor: row.active ? "var(--bad)" : "var(--ok)", color: row.active ? "var(--bad)" : "var(--ok)"}}>
                    {busy ? "…" : (row.active ? t("Uitschakelen","Disable") : t("Activeren","Enable"))}
                  </button>
                  <button onClick={handleDelete} disabled={busy} className="btn btn-ghost btn-sm"
                    style={{borderColor:"var(--bad)", color:"var(--bad)"}}>
                    {t("Verwijderen","Delete")}
                  </button>
                </>
              )}
            </div>
          ) : (
            <span style={{fontSize:11, color:"var(--ink-mute)"}}>{isOwnerRow ? t("Owner — beschermd","Owner — protected") : t("Beschermd","Protected")}</span>
          )}
          {err && !edit && <div style={{color:"var(--bad)", fontSize:11.5, marginTop:6}}>{err}</div>}
        </div>
      </div>

      {edit && canEditRow && (
        <div style={{marginTop:14, paddingTop:14, borderTop:"1px dashed var(--line)"}}>
          <div style={{padding:"10px 12px", background:"var(--bg)", borderRadius:8, marginBottom:14}}>
            <div className="mono" style={{fontSize:10.5, color:"var(--ink-mute)", letterSpacing:".1em", textTransform:"uppercase", marginBottom:6}}>{t("Permissies","Permissions")}</div>
            <PermCheckbox label={t("Orders beheren","Manage orders")}         value={perms.can_manage_orders}   onChange={v=>setPerms(p=>({...p,can_manage_orders:v}))}/>
            <PermCheckbox label={t("Productprijzen beheren","Manage prices")}  value={perms.can_manage_prices}   onChange={v=>setPerms(p=>({...p,can_manage_prices:v}))}/>
            <PermCheckbox label={t("Settings beheren","Manage settings")}      value={perms.can_manage_settings} onChange={v=>setPerms(p=>({...p,can_manage_settings:v}))}/>
          </div>
          {err && <div style={{color:"var(--bad)", fontSize:13, marginBottom:10}}>{err}</div>}
          <button onClick={save} className="btn btn-primary" disabled={busy}>{busy ? t("Opslaan…","Saving…") : t("Opslaan","Save")}</button>
        </div>
      )}
    </div>
  );
};

const AdminUsersSection = ({ currentAdmin }) => {
  const langCtx = __uc(window.LangContext || React.createContext({ lang:"nl", t: (nl)=>nl }));
  const t = langCtx.t || ((nl) => nl);
  const [list, setList] = __us([]);
  const [loading, setLoading] = __us(true);
  const [schemaMissing, setSchemaMissing] = __us(false);
  const [showAdd, setShowAdd] = __us(false);
  const [newEmail, setNewEmail] = __us("");
  const [newPerms, setNewPerms] = __us({ ...ADMIN_PRESETS[0].perms });
  const [presetId, setPresetId] = __us(ADMIN_PRESETS[0].id);
  const [addBusy, setAddBusy] = __us(false);
  const [addErr, setAddErr] = __us(null);

  const reload = () => {
    setLoading(true);
    window.zsb.getAdminUsers().then(r => {
      if (r.schemaMissing) setSchemaMissing(true);
      setList(r.data || []);
      setLoading(false);
    });
  };
  __ue(() => { reload(); }, []);

  const applyPreset = (p) => { setPresetId(p.id); setNewPerms({ ...p.perms }); };

  const submit = async (e) => {
    e.preventDefault();
    setAddErr(null);
    const email = newEmail.trim().toLowerCase();
    if (!email) { setAddErr(t("Vul een e-mailadres in.","Enter an email.")); return; }
    setAddBusy(true);
    // Owner-only RPC: it verifies a Supabase Auth account exists for this e-mail,
    // then inserts role="admin" (the UI never creates an owner). The frontend never
    // reads auth.users.
    const r = await window.zsb.ownerAddAdminUser({
      email,
      can_manage_orders:   newPerms.can_manage_orders,
      can_manage_prices:   newPerms.can_manage_prices,
      can_manage_settings: newPerms.can_manage_settings,
    });
    setAddBusy(false);
    if (r.error) { setAddErr(r.error); return; }
    if (r.data && r.data.status === "no_account") {
      setAddErr(t("Deze gebruiker heeft nog geen account. Laat hem/haar eerst registreren of inloggen.","This user has no account yet. Have them register or sign in first."));
      return;
    }
    setNewEmail(""); setNewPerms({ ...ADMIN_PRESETS[0].perms }); setPresetId(ADMIN_PRESETS[0].id);
    setShowAdd(false);
    reload();
  };

  if (schemaMissing) {
    return <div className="card" style={{padding:18, background:"#fff8e6", border:"1px solid #f0d985", fontSize:13.5}}>
      <strong>{t("Adminbeheer-tabel bestaat nog niet.","Admin table doesn't exist yet.")}</strong><br/>
      <span style={{color:"var(--ink-dim)"}}>{t("Run supabase-admin-audit.sql in Supabase SQL Editor.","Run supabase-admin-audit.sql in the Supabase SQL Editor.")}</span>
    </div>;
  }

  return (
    <div>
      <div style={{display:"flex", justifyContent:"space-between", alignItems:"center", marginBottom:6, gap:10, flexWrap:"wrap"}}>
        <h3 className="h-display" style={{fontSize:18, margin:0}}>{t("Adminbeheer","Admin management")}</h3>
        <button onClick={()=>setShowAdd(o=>!o)} className="btn btn-primary btn-sm">
          {showAdd ? t("Annuleren","Cancel") : t("+ Gebruiker toevoegen","+ Add user")}
        </button>
      </div>
      <p style={{fontSize:12.5, color:"var(--ink-dim)", marginBottom:14, maxWidth:680}}>
        {t("Beheer is owner-only. Elke wijziging wordt automatisch gelogd in audit_logs.","Management is owner-only. Every change is automatically logged to audit_logs.")}
      </p>

      {showAdd && (
        <form onSubmit={submit} className="card" style={{padding:18, marginBottom:14}}>
          <h4 style={{fontSize:14, marginBottom:12, textTransform:"uppercase", letterSpacing:".06em"}}>{t("Nieuwe gebruiker","New user")}</h4>

          <div className="field" style={{marginBottom:12}}>
            <label className="field-label">{t("E-mail (wordt lowercase opgeslagen)","Email (stored lowercase)")}</label>
            <input className="input" type="email" value={newEmail} onChange={e=>setNewEmail(e.target.value)} placeholder="persoon@email.com" required/>
          </div>

          <div style={{marginBottom:12}}>
            <div className="field-label" style={{marginBottom:6}}>{t("Preset","Preset")}</div>
            <div style={{display:"flex", gap:8, flexWrap:"wrap"}}>
              {ADMIN_PRESETS.map(p => (
                <button type="button" key={p.id} onClick={()=>applyPreset(p)}
                  className={"btn btn-sm " + (presetId === p.id ? "btn-accent" : "btn-ghost")}>
                  {langCtx.lang === "en" ? p.labelEn : p.labelNl}
                </button>
              ))}
            </div>
          </div>

          <div style={{padding:"10px 12px", background:"var(--bg)", borderRadius:8, marginBottom:14}}>
            <div className="mono" style={{fontSize:10.5, color:"var(--ink-mute)", letterSpacing:".1em", textTransform:"uppercase", marginBottom:6}}>{t("Permissies (aanpasbaar)","Permissions (adjustable)")}</div>
            <PermCheckbox label={t("Orders beheren","Manage orders")}         value={newPerms.can_manage_orders}    onChange={v=>setNewPerms(p=>({...p,can_manage_orders:v}))}/>
            <PermCheckbox label={t("Productprijzen beheren","Manage prices")}  value={newPerms.can_manage_prices}    onChange={v=>setNewPerms(p=>({...p,can_manage_prices:v}))}/>
            <PermCheckbox label={t("Settings beheren","Manage settings")}      value={newPerms.can_manage_settings}  onChange={v=>setNewPerms(p=>({...p,can_manage_settings:v}))}/>
          </div>
          {addErr && <div style={{color:"var(--bad)", fontSize:13, marginBottom:10}}>{addErr}</div>}
          <div style={{fontSize:11.5, color:"var(--ink-mute)", marginBottom:12}}>
            {t("Rol = admin (de UI maakt nooit een owner aan). De persoon moet inloggen met exact dit e-mailadres via Supabase Auth.","Role = admin (the UI never creates an owner). The person must sign in with exactly this email via Supabase Auth.")}
          </div>
          <button type="submit" className="btn btn-primary" disabled={addBusy}>{addBusy ? t("Toevoegen…","Adding…") : t("Gebruiker toevoegen","Add user")}</button>
        </form>
      )}

      {loading ? (
        <div className="card" style={{padding:24, color:"var(--ink-dim)"}}>{t("Laden…","Loading…")}</div>
      ) : list.length === 0 ? (
        <div className="card" style={{padding:24, color:"var(--ink-dim)"}}>{t("Geen admins gevonden.","No admins found.")}</div>
      ) : (
        list.map(row => <AdminUserRow key={row.id} row={row} currentAdmin={currentAdmin} onChanged={reload}/>)
      )}
    </div>
  );
};

// ─────────────────────────────────────────────────────────────
// Audit logs
// ─────────────────────────────────────────────────────────────
const AuditLogRow = ({ row }) => {
  const [open, setOpen] = __us(false);
  const langCtx = __uc(window.LangContext || React.createContext({ lang:"nl", t: (nl)=>nl }));
  const t = langCtx.t || ((nl) => nl);
  const locale = langCtx.lang === "en" ? "en-GB" : "nl-NL";
  return (
    <div className="card" style={{padding:0, marginBottom:8, overflow:"hidden"}}>
      <button onClick={()=>setOpen(o=>!o)} style={{
        width:"100%", padding:"12px 16px", display:"grid",
        gridTemplateColumns:"160px 1.4fr 1.4fr 1fr auto", gap:12, alignItems:"center",
        textAlign:"left", background:"transparent", fontSize:13,
      }} className="audit-row">
        <span className="mono" style={{fontSize:11, color:"var(--ink-mute)"}}>{new Date(row.created_at).toLocaleString(locale, {dateStyle:"short", timeStyle:"short"})}</span>
        <span style={{fontWeight:500}}>{row.actor_email || "—"} <span style={{fontSize:10.5, color:"var(--ink-mute)"}}>({row.actor_role || "—"})</span></span>
        <span className="mono" style={{fontSize:11.5, color:"var(--accent)"}}>{row.action}</span>
        <span className="mono" style={{fontSize:11, color:"var(--ink-dim)"}}>{row.entity_type}{row.entity_id ? " · " + String(row.entity_id).slice(0,8) : ""}</span>
        <Icon name="arrowDown" size={14} style={{color:"var(--ink-mute)", transform:open?"rotate(180deg)":"rotate(0)", transition:"transform .2s"}}/>
      </button>
      {open && (
        <div style={{padding:"4px 16px 16px", borderTop:"1px dashed var(--line)", fontSize:12, fontFamily:"var(--f-mono)"}}>
          {row.before_data && <div style={{marginTop:10}}>
            <div className="mono" style={{fontSize:10.5, color:"var(--ink-mute)", letterSpacing:".1em", textTransform:"uppercase", marginBottom:4}}>{t("Voor","Before")}</div>
            <pre style={{margin:0, background:"var(--bg)", padding:10, borderRadius:6, overflow:"auto", maxHeight:240, whiteSpace:"pre-wrap"}}>{JSON.stringify(row.before_data, null, 2)}</pre>
          </div>}
          {row.after_data && <div style={{marginTop:10}}>
            <div className="mono" style={{fontSize:10.5, color:"var(--ink-mute)", letterSpacing:".1em", textTransform:"uppercase", marginBottom:4}}>{t("Na","After")}</div>
            <pre style={{margin:0, background:"var(--bg)", padding:10, borderRadius:6, overflow:"auto", maxHeight:240, whiteSpace:"pre-wrap"}}>{JSON.stringify(row.after_data, null, 2)}</pre>
          </div>}
          {row.metadata && <div style={{marginTop:10}}>
            <div className="mono" style={{fontSize:10.5, color:"var(--ink-mute)", letterSpacing:".1em", textTransform:"uppercase", marginBottom:4}}>{t("Metadata","Metadata")}</div>
            <pre style={{margin:0, background:"var(--bg)", padding:10, borderRadius:6, overflow:"auto", maxHeight:240, whiteSpace:"pre-wrap"}}>{JSON.stringify(row.metadata, null, 2)}</pre>
          </div>}
        </div>
      )}
      <style>{`@media (max-width:780px){.audit-row{grid-template-columns:1fr !important;gap:4px !important}}`}</style>
    </div>
  );
};

const AuditLogsSection = () => {
  const langCtx = __uc(window.LangContext || React.createContext({ lang:"nl", t: (nl)=>nl }));
  const t = langCtx.t || ((nl) => nl);
  const [list, setList] = __us([]);
  const [loading, setLoading] = __us(true);
  const [schemaMissing, setSchemaMissing] = __us(false);
  const [actorFilter, setActorFilter] = __us("");
  const [actionFilter, setActionFilter] = __us("");
  const [entityFilter, setEntityFilter] = __us("");

  const reload = () => {
    setLoading(true);
    window.zsb.getAuditLogs({
      limit: 200,
      actorEmail: actorFilter || null,
      action: actionFilter || null,
      entityType: entityFilter || null,
    }).then(r => {
      if (r.schemaMissing) setSchemaMissing(true);
      setList(r.data || []);
      setLoading(false);
    });
  };
  __ue(() => { reload(); /* eslint-disable-next-line */ }, [actorFilter, actionFilter, entityFilter]);

  if (schemaMissing) {
    return <div className="card" style={{padding:18, background:"#fff8e6", border:"1px solid #f0d985", fontSize:13.5}}>
      <strong>{t("Audit-logs tabel bestaat nog niet.","Audit-logs table doesn't exist yet.")}</strong><br/>
      <span style={{color:"var(--ink-dim)"}}>{t("Run supabase-admin-audit.sql om audit logging in te schakelen.","Run supabase-admin-audit.sql to enable audit logging.")}</span>
    </div>;
  }

  // Build unique action / entity lists for filter dropdowns
  const actionsSet = new Set(); const entitiesSet = new Set();
  list.forEach(r => { if (r.action) actionsSet.add(r.action); if (r.entity_type) entitiesSet.add(r.entity_type); });
  const actions = Array.from(actionsSet).sort();
  const entities = Array.from(entitiesSet).sort();

  return (
    <div>
      <div style={{display:"flex", justifyContent:"space-between", alignItems:"center", marginBottom:14, gap:10, flexWrap:"wrap"}}>
        <h3 className="h-display" style={{fontSize:18, margin:0}}>{t("Audit logs","Audit logs")}</h3>
        <button onClick={reload} className="btn btn-ghost btn-sm">↻ {t("Vernieuw","Refresh")}</button>
      </div>

      <div className="card" style={{padding:14, marginBottom:14, display:"grid", gridTemplateColumns:"1fr 1fr 1fr", gap:10}} className="audit-filters">
        <input className="input" placeholder={t("Filter op actor (e-mail)","Filter by actor (email)")} value={actorFilter} onChange={e=>setActorFilter(e.target.value)}/>
        <select className="select" value={actionFilter} onChange={e=>setActionFilter(e.target.value)}>
          <option value="">{t("Alle acties","All actions")}</option>
          {actions.map(a => <option key={a} value={a}>{a}</option>)}
        </select>
        <select className="select" value={entityFilter} onChange={e=>setEntityFilter(e.target.value)}>
          <option value="">{t("Alle types","All types")}</option>
          {entities.map(a => <option key={a} value={a}>{a}</option>)}
        </select>
        <style>{`@media (max-width:780px){.audit-filters{grid-template-columns:1fr !important}}`}</style>
      </div>

      {loading ? (
        <div className="card" style={{padding:24, color:"var(--ink-dim)"}}>{t("Laden…","Loading…")}</div>
      ) : list.length === 0 ? (
        <div className="card" style={{padding:24, color:"var(--ink-dim)"}}>{t("Geen audit-logs gevonden voor deze filter.","No audit logs found for this filter.")}</div>
      ) : (
        list.map(row => <AuditLogRow key={row.id} row={row}/>)
      )}
    </div>
  );
};

// ─────────────────────────────────────────────────────────────
// Settings
// ─────────────────────────────────────────────────────────────
const SettingRow = ({ row, onChanged }) => {
  const langCtx = __uc(window.LangContext || React.createContext({ lang:"nl", t: (nl)=>nl }));
  const t = langCtx.t || ((nl) => nl);
  const [value, setValue] = __us(JSON.stringify(row.value ?? "", null, 2));
  const [busy, setBusy] = __us(false);
  const [err, setErr] = __us(null);
  const [savedAt, setSavedAt] = __us(null);

  const save = async () => {
    setBusy(true); setErr(null);
    let v;
    try { v = JSON.parse(value); } catch (e) { setErr(t("Waarde moet geldige JSON zijn.","Value must be valid JSON.")); setBusy(false); return; }
    const r = await window.zsb.updateSettingWithAudit(row.key, v);
    setBusy(false);
    if (r.error) { setErr(r.error); return; }
    setSavedAt(Date.now());
    onChanged && onChanged();
  };

  return (
    <div className="card" style={{padding:16, marginBottom:10}}>
      <div className="mono" style={{fontSize:10.5, color:"var(--ink-mute)", letterSpacing:".1em", textTransform:"uppercase", marginBottom:6}}>{row.key}</div>
      <textarea className="textarea" rows={4} value={value} onChange={e=>setValue(e.target.value)} style={{fontFamily:"var(--f-mono)", fontSize:12}}/>
      {err && <div style={{color:"var(--bad)", fontSize:13, marginTop:8}}>{err}</div>}
      <div className="gap-12" style={{marginTop:10}}>
        <button onClick={save} className="btn btn-primary btn-sm" disabled={busy}>{busy ? t("Opslaan…","Saving…") : t("Opslaan","Save")}</button>
        {savedAt && <span style={{fontSize:12.5, color:"var(--ok)"}}>✓ {t("Opgeslagen","Saved")}</span>}
      </div>
    </div>
  );
};

const ShippingSettingsSection = ({ list, onChanged }) => {
  const langCtx = __uc(window.LangContext || React.createContext({ lang:"nl", t: (nl)=>nl }));
  const settingsCtx = __uc(window.SettingsContext || React.createContext({ refresh: () => {} }));
  const t = langCtx.t || ((nl) => nl);
  const rowValue = (key, fallback) => {
    const row = (list || []).find(x => x.key === key);
    if (!row) return fallback;
    const v = row.value;
    if (v && typeof v === "object" && "amount" in v) return v.amount;
    return v;
  };
  const cfg = window.getShippingConfig ? window.getShippingConfig() : {
    nl:19, be:29, de:29, free_shipping_threshold:500, default_country:"NL",
    pickup_enabled:true, delivery_enabled:true,
  };

  const [nlFee, setNlFee] = __us(rowValue("shipping_nl_fee", cfg.nl));
  const [beFee, setBeFee] = __us(rowValue("shipping_be_fee", cfg.be));
  const [deFee, setDeFee] = __us(rowValue("shipping_de_fee", cfg.de));
  const [threshold, setThreshold] = __us(rowValue("free_shipping_threshold", cfg.free_shipping_threshold));
  const [pickupEnabled, setPickupEnabled] = __us(!!rowValue("pickup_enabled", cfg.pickup_enabled));
  const [deliveryEnabled, setDeliveryEnabled] = __us(!!rowValue("delivery_enabled", cfg.delivery_enabled));
  const [defaultCountry, setDefaultCountry] = __us(String(rowValue("default_shipping_country", cfg.default_country || "NL")).toUpperCase());
  const [busy, setBusy] = __us(false);
  const [err, setErr] = __us(null);
  const [savedAt, setSavedAt] = __us(null);

  __ue(() => {
    setNlFee(rowValue("shipping_nl_fee", cfg.nl));
    setBeFee(rowValue("shipping_be_fee", cfg.be));
    setDeFee(rowValue("shipping_de_fee", cfg.de));
    setThreshold(rowValue("free_shipping_threshold", cfg.free_shipping_threshold));
    setPickupEnabled(!!rowValue("pickup_enabled", cfg.pickup_enabled));
    setDeliveryEnabled(!!rowValue("delivery_enabled", cfg.delivery_enabled));
    setDefaultCountry(String(rowValue("default_shipping_country", cfg.default_country || "NL")).toUpperCase());
    // eslint-disable-next-line
  }, [JSON.stringify(list || [])]);

  const save = async (e) => {
    e.preventDefault();
    setErr(null);
    const n = Number(nlFee), b = Number(beFee), d = Number(deFee), f = Number(threshold);
    if (![n,b,d,f].every(x => Number.isFinite(x) && x >= 0)) {
      setErr(t("Alle bedragen moeten geldige positieve getallen zijn.","All amounts must be valid positive numbers."));
      return;
    }
    if (!["NL","BE","DE"].includes(defaultCountry)) {
      setErr(t("Kies een geldig standaardland.","Choose a valid default country."));
      return;
    }
    setBusy(true);
    const updates = [
      ["shipping_nl_fee", { amount:n }],
      ["shipping_be_fee", { amount:b }],
      ["shipping_de_fee", { amount:d }],
      ["free_shipping_threshold", { amount:f }],
      ["pickup_enabled", !!pickupEnabled],
      ["delivery_enabled", !!deliveryEnabled],
      ["default_shipping_country", defaultCountry],
    ];
    for (const [key, value] of updates) {
      const r = await window.zsb.updateSettingWithAudit(key, value);
      if (r.error) { setBusy(false); setErr(r.error); return; }
      if (window.applySettingRow) window.applySettingRow({ key, value });
    }
    if (window.zsb && window.zsb.createAuditLog) {
      window.zsb.createAuditLog("shipping_settings_updated", "site_settings", "shipping", {
        shipping_nl_fee:n, shipping_be_fee:b, shipping_de_fee:d,
        free_shipping_threshold:f, pickup_enabled:!!pickupEnabled,
        delivery_enabled:!!deliveryEnabled, default_shipping_country:defaultCountry,
      }).then(() => {}, () => {});
    }
    await Promise.resolve(settingsCtx.refresh && settingsCtx.refresh());
    setBusy(false);
    setSavedAt(Date.now());
    onChanged && onChanged();
  };

  return (
    <div className="card" style={{padding:22, marginBottom:18}}>
      <div style={{display:"flex", justifyContent:"space-between", gap:14, alignItems:"flex-start", flexWrap:"wrap", marginBottom:16}}>
        <div>
          <h3 className="h-display" style={{fontSize:20, marginBottom:6}}>{t("Verzendinstellingen","Shipping settings")}</h3>
          <p style={{color:"var(--ink-dim)", fontSize:13.5, maxWidth:640}}>
            {t("Beheer verzendkosten per land, gratis ophalen en de drempel voor gratis verzending.","Manage shipping costs by country, free pickup and the free-shipping threshold.")}
          </p>
        </div>
        {savedAt && <span style={{fontSize:12.5, color:"var(--ok)"}}>✓ {t("Opgeslagen","Saved")}</span>}
      </div>

      <form onSubmit={save}>
        <div className="grid-3" style={{marginBottom:14}}>
          <div className="field">
            <label className="field-label">{t("Verzendkosten Nederland (€)","Netherlands shipping (€)")}</label>
            <input className="input" type="number" min="0" step="1" value={nlFee} onChange={e=>setNlFee(e.target.value)}/>
          </div>
          <div className="field">
            <label className="field-label">{t("Verzendkosten België (€)","Belgium shipping (€)")}</label>
            <input className="input" type="number" min="0" step="1" value={beFee} onChange={e=>setBeFee(e.target.value)}/>
          </div>
          <div className="field">
            <label className="field-label">{t("Verzendkosten Duitsland (€)","Germany shipping (€)")}</label>
            <input className="input" type="number" min="0" step="1" value={deFee} onChange={e=>setDeFee(e.target.value)}/>
          </div>
        </div>
        <div className="grid-3" style={{marginBottom:14}}>
          <div className="field">
            <label className="field-label">{t("Gratis verzending vanaf (€)","Free shipping from (€)")}</label>
            <input className="input" type="number" min="0" step="1" value={threshold} onChange={e=>setThreshold(e.target.value)}/>
          </div>
          <div className="field">
            <label className="field-label">{t("Standaard verzendland","Default shipping country")}</label>
            <select className="select" value={defaultCountry} onChange={e=>setDefaultCountry(e.target.value)}>
              <option value="NL">{t("Nederland","Netherlands")}</option>
              <option value="BE">{t("België","Belgium")}</option>
              <option value="DE">{t("Duitsland","Germany")}</option>
            </select>
          </div>
          <div className="field">
            <label className="field-label">{t("Methodes","Methods")}</label>
            <div className="card" style={{padding:10, background:"var(--bg)"}}>
              <label style={{display:"flex", alignItems:"center", gap:8, fontSize:13, marginBottom:8}}>
                <input type="checkbox" checked={pickupEnabled} onChange={e=>setPickupEnabled(e.target.checked)} style={{accentColor:"var(--accent)"}}/>
                {t("Ophalen actief","Pickup enabled")}
              </label>
              <label style={{display:"flex", alignItems:"center", gap:8, fontSize:13}}>
                <input type="checkbox" checked={deliveryEnabled} onChange={e=>setDeliveryEnabled(e.target.checked)} style={{accentColor:"var(--accent)"}}/>
                {t("Bezorgen actief","Delivery enabled")}
              </label>
            </div>
          </div>
        </div>
        {err && <div style={{color:"var(--bad)", fontSize:13, marginBottom:10}}>{err}</div>}
        <button type="submit" className="btn btn-primary" disabled={busy}>{busy ? t("Opslaan...","Saving...") : t("Verzendinstellingen opslaan","Save shipping settings")}</button>
      </form>
    </div>
  );
};

const ContactSettingsSection = ({ list, onChanged }) => {
  const langCtx = __uc(window.LangContext || React.createContext({ lang:"nl", t: (nl)=>nl }));
  const t = langCtx.t || ((nl) => nl);
  
  const rowValue = (key, fallback) => {
    const row = (list || []).find(x => x.key === key);
    return row ? row.value : fallback;
  };

  const [contactEmail, setContactEmail] = __us("");
  const [phoneNumber, setPhoneNumber] = __us("");
  const [whatsappNumber, setWhatsappNumber] = __us("");
  const [emailFrom, setEmailFrom] = __us("");
  const [busy, setBusy] = __us(false);
  const [err, setErr] = __us(null);
  const [savedAt, setSavedAt] = __us(null);

  __ue(() => {
    setContactEmail(rowValue("contact_email", ""));
    setPhoneNumber(rowValue("phone_number", ""));
    setWhatsappNumber(rowValue("whatsapp_number", ""));
    setEmailFrom(rowValue("email_from", ""));
  }, [JSON.stringify(list || [])]);

  const save = async (e) => {
    e.preventDefault();
    setErr(null);
    setBusy(true);
    const updates = [
      ["contact_email", contactEmail.trim()],
      ["phone_number", phoneNumber.trim()],
      ["whatsapp_number", whatsappNumber.trim()],
      ["email_from", emailFrom.trim()],
    ];
    for (const [key, value] of updates) {
      const r = await window.zsb.updateSettingWithAudit(key, value);
      if (r.error) { setBusy(false); setErr(r.error); return; }
      if (window.applySettingRow) window.applySettingRow({ key, value });
    }
    setBusy(false);
    setSavedAt(Date.now());
    onChanged && onChanged();
  };

  return (
    <div className="card" style={{padding:22, marginBottom:20}}>
      <h3 className="h-display" style={{fontSize:18, marginBottom:14}}>{t("Contact & E-mail instellingen","Contact & Email settings")}</h3>
      <form onSubmit={save}>
        <div className="grid-2" style={{marginBottom:14}}>
          <div className="field">
            <label className="field-label">{t("Contact E-mailadres","Contact Email address")}</label>
            <input className="input" type="email" value={contactEmail} onChange={e=>setContactEmail(e.target.value)} placeholder="info@zyroelectric.nl" required/>
          </div>
          <div className="field">
            <label className="field-label">{t("Telefoonnummer","Phone number")}</label>
            <input className="input" value={phoneNumber} onChange={e=>setPhoneNumber(e.target.value)} placeholder="+31 6 38 36 21 47" required/>
          </div>
        </div>
        <div className="grid-2" style={{marginBottom:14}}>
          <div className="field">
            <label className="field-label">{t("WhatsApp nummer (landcode zonder + of 00)","WhatsApp number (country code without + or 00)")}</label>
            <input className="input" value={whatsappNumber} onChange={e=>setWhatsappNumber(e.target.value)} placeholder="31638362147" required/>
          </div>
          <div className="field">
            <label className="field-label">{t("E-mail 'From' afzender-naam","Email 'From' display name")}</label>
            <input className="input" value={emailFrom} onChange={e=>setEmailFrom(e.target.value)} placeholder="info@zyroelectric.nl" required/>
          </div>
        </div>
        {err && <div style={{color:"var(--bad)", fontSize:13, marginBottom:10}}>{err}</div>}
        <div className="gap-12">
          <button type="submit" className="btn btn-primary" disabled={busy}>{busy ? t("Opslaan...","Saving...") : t("Contactinstellingen opslaan","Save contact settings")}</button>
          {savedAt && <span style={{fontSize:12.5, color:"var(--ok)"}}>✓ {t("Opgeslagen","Saved")}</span>}
        </div>
      </form>
    </div>
  );
};

const SettingsSection = () => {
  const langCtx = __uc(window.LangContext || React.createContext({ lang:"nl", t: (nl)=>nl }));
  const t = langCtx.t || ((nl) => nl);
  const [list, setList] = __us([]);
  const [loading, setLoading] = __us(true);
  const [schemaMissing, setSchemaMissing] = __us(false);
  const [newKey, setNewKey] = __us("");

  const reload = () => {
    setLoading(true);
    window.zsb.getAllSettings().then(r => {
      if (r.schemaMissing) setSchemaMissing(true);
      setList(r.data || []);
      setLoading(false);
    });
  };
  __ue(() => { reload(); }, []);

  const addNew = async () => {
    if (!newKey.trim()) return;
    const r = await window.zsb.updateSettingWithAudit(newKey.trim(), {});
    if (!r.error) { setNewKey(""); reload(); }
  };

  if (schemaMissing) {
    return <div className="card" style={{padding:18, background:"#fff8e6", border:"1px solid #f0d985", fontSize:13.5}}>
      <strong>{t("Settings-tabel bestaat nog niet.","Settings table doesn't exist yet.")}</strong><br/>
      <span style={{color:"var(--ink-dim)"}}>{t("Run supabase-admin-audit.sql in Supabase SQL Editor.","Run supabase-admin-audit.sql in the Supabase SQL Editor.")}</span>
    </div>;
  }

  return (
    <div>
      <ContactSettingsSection list={list} onChanged={reload}/>
      <ShippingSettingsSection list={list} onChanged={reload}/>
      <div style={{display:"flex", justifyContent:"space-between", alignItems:"center", marginBottom:14, gap:10, flexWrap:"wrap"}}>
        <h3 className="h-display" style={{fontSize:18, margin:0}}>{t("Settings","Settings")}</h3>
        <div className="gap-8">
          <input className="input" placeholder={t("nieuwe key (bv. whatsapp_number)","new key (e.g. whatsapp_number)")} value={newKey} onChange={e=>setNewKey(e.target.value)} style={{width:240}}/>
          <button onClick={addNew} className="btn btn-primary btn-sm">+ {t("Nieuwe key","New key")}</button>
        </div>
      </div>
      <p style={{fontSize:12.5, color:"var(--ink-dim)", marginBottom:14}}>
        {t("Elke setting is een key + JSON-waarde. Bedoeld voor bv. whatsapp_number, showroom_hours, delivery_costs.","Each setting is a key + JSON value. Intended for e.g. whatsapp_number, showroom_hours, delivery_costs.")}
      </p>
      {loading ? <div className="card" style={{padding:24}}>{t("Laden…","Loading…")}</div>
        : list.length === 0 ? <div className="card" style={{padding:24, color:"var(--ink-dim)"}}>{t("Nog geen settings. Voeg een key toe hierboven.","No settings yet. Add a key above.")}</div>
        : list.filter(row => !["contact_email", "phone_number", "whatsapp_number", "email_from"].includes(row.key)).map(row => <SettingRow key={row.key} row={row} onChanged={reload}/>)}
    </div>
  );
};

// ─────────────────────────────────────────────────────────────
// Role-change popup (target side)
// ─────────────────────────────────────────────────────────────
// Driven entirely by the latest unseen admin_role_notifications.kind.
// Text is rendered client-side from `kind` — the notification row contains NO
// actor information by design (see supabase-admin-role-notifications.sql).
// The popup intentionally never displays who made the change.
const RoleChangePopup = ({ notice, t, onAcknowledge }) => {
  if (!notice) return null;
  const granted = notice.kind === "access_granted";
  const revoked = notice.kind === "access_revoked";
  const reactivated = notice.kind === "reactivated";
  const title = granted
    ? t("Admin-toegang toegevoegd","Admin access added")
    : revoked
      ? t("Admin-toegang ingetrokken","Admin access revoked")
      : reactivated
        ? t("Admin-toegang heractiveerd","Admin access reactivated")
        : t("Je rechten zijn gewijzigd","Your permissions have changed");
  const body = granted
    ? t("Je hebt toegang gekregen tot het adminpaneel.","You've been given access to the admin panel.")
    : revoked
      ? t("Je hebt geen toegang meer tot het adminpaneel. Je gewone account blijft werken.","You no longer have access to the admin panel. Your normal account still works.")
      : reactivated
        ? t("Je adminrechten zijn opnieuw actief. Het paneel wordt opnieuw geladen.","Your admin rights are active again. The panel will reload.")
        : t("Je toegangsrechten zijn aangepast. Het adminpaneel wordt opnieuw geladen.","Your access rights have been updated. The admin panel will reload.");
  const btn = revoked ? t("Terug naar account","Back to my account") : t("Vernieuwen","Refresh");
  return (
    <div role="dialog" aria-modal="true" style={{
      position:"fixed", inset:0, background:"rgba(0,0,0,.55)", zIndex:9999,
      display:"flex", alignItems:"center", justifyContent:"center", padding:18,
    }}>
      <div className="card" style={{
        maxWidth:460, width:"100%", padding:24, background:"var(--paper)",
        boxShadow:"0 18px 50px rgba(0,0,0,.32)", borderRadius:12,
      }}>
        <h3 className="h-display" style={{fontSize:22, marginBottom:10}}>{title}</h3>
        <p style={{color:"var(--ink-dim)", marginBottom:18, lineHeight:1.5, fontSize:14}}>{body}</p>
        <button className={"btn " + (revoked ? "btn-primary" : "btn-accent")} onClick={onAcknowledge} autoFocus>
          {btn}
        </button>
      </div>
    </div>
  );
};

// ─────────────────────────────────────────────────────────────
// Main page
// ─────────────────────────────────────────────────────────────
// ─────────────────────────────────────────────────────────────
// Reviews moderation (owner OR can_manage_orders)
// ─────────────────────────────────────────────────────────────
const ReviewStarsAdmin = ({ value }) => (
  <span style={{display:"inline-flex", gap:2, color:"var(--accent)"}}>
    {[1,2,3,4,5].map(n => <Icon key={n} name="star" size={13} fill={n <= value ? "var(--accent)" : "transparent"}/>)}
  </span>
);

const ReviewsModerationSection = () => {
  const langCtx = __uc(window.LangContext || React.createContext({ lang:"nl", t:(nl)=>nl }));
  const t = langCtx.t || ((nl)=>nl);
  const locale = langCtx.lang === "en" ? "en-GB" : "nl-NL";
  const [statusFilter, setStatusFilter] = __us("pending");
  const [list, setList] = __us([]);
  const [loading, setLoading] = __us(true);
  const [err, setErr] = __us(null);
  const [busyId, setBusyId] = __us(null);

  const reload = () => {
    setLoading(true); setErr(null);
    window.zsb.getReviewsForModeration(statusFilter === "all" ? null : statusFilter).then(r => {
      if (r.error) setErr(r.error);
      setList(r.data || []);
      setLoading(false);
    });
  };
  __ue(() => { reload(); /* eslint-disable-next-line */ }, [statusFilter]);

  const moderate = async (id, action) => {
    setBusyId(id); setErr(null);
    const r = action === "approve" ? await window.zsb.approveReview(id) : await window.zsb.rejectReview(id);
    setBusyId(null);
    if (r.error) { setErr(r.error); return; }
    reload();
  };

  const FILTERS = [
    { id:"pending",  label:t("In behandeling","Pending") },
    { id:"approved", label:t("Goedgekeurd","Approved") },
    { id:"rejected", label:t("Afgewezen","Rejected") },
    { id:"all",      label:t("Alles","All") },
  ];
  const badge = (s) => ({
    approved: { bg:"#e7f4ec", fg:"#1f6b3a", label:t("Goedgekeurd","Approved") },
    rejected: { bg:"#fbe9e9", fg:"#9a2222", label:t("Afgewezen","Rejected") },
  }[s] || { bg:"var(--bg-2)", fg:"var(--ink-dim)", label:t("In behandeling","Pending") });

  return (
    <div>
      <h2 className="h-display" style={{fontSize:22, marginBottom:6}}>{t("Reviews","Reviews")}</h2>
      <p style={{fontSize:12.5, color:"var(--ink-dim)", marginBottom:14, maxWidth:680}}>
        {t("Nieuwe reviews staan op 'in behandeling' en worden pas publiek zichtbaar na goedkeuring. Goed-/afkeuren wordt gelogd in audit_logs.","New reviews are 'pending' and only become publicly visible after approval. Approve/reject is logged to audit_logs.")}
      </p>
      <div style={{display:"flex", gap:6, marginBottom:16, flexWrap:"wrap"}}>
        {FILTERS.map(f => (
          <button key={f.id} onClick={()=>setStatusFilter(f.id)}
            className={"btn btn-sm " + (statusFilter === f.id ? "btn-accent" : "btn-ghost")}>{f.label}</button>
        ))}
      </div>
      {err && <div style={{color:"var(--bad)", fontSize:13, marginBottom:10}}>{err}</div>}
      {loading ? (
        <div className="card" style={{padding:24, color:"var(--ink-dim)"}}>{t("Laden…","Loading…")}</div>
      ) : list.length === 0 ? (
        <div className="card" style={{padding:24, color:"var(--ink-dim)"}}>{t("Geen reviews in deze categorie.","No reviews in this category.")}</div>
      ) : (
        list.map(rv => {
          const b = badge(rv.status);
          return (
            <div key={rv.id} className="card" style={{padding:16, marginBottom:10}}>
              <div style={{display:"flex", justifyContent:"space-between", gap:12, flexWrap:"wrap", alignItems:"flex-start"}}>
                <div style={{minWidth:200, flex:1}}>
                  <div style={{display:"flex", alignItems:"center", gap:8, marginBottom:4}}>
                    <ReviewStarsAdmin value={rv.rating}/>
                    <span style={{fontSize:12, color:"var(--ink-mute)"}}>{rv.product_name || rv.product_slug}</span>
                  </div>
                  {rv.title && <div style={{fontWeight:600, fontSize:14}}>{rv.title}</div>}
                  {rv.body && <div style={{fontSize:13.5, color:"var(--ink-dim)", marginTop:4, whiteSpace:"pre-wrap"}}>{rv.body}</div>}
                  <div className="mono" style={{fontSize:11, color:"var(--ink-mute)", marginTop:8}}>
                    {(rv.customer_name || "—")} · {(rv.customer_email || "—")} · {new Date(rv.created_at).toLocaleString(locale, { dateStyle:"short", timeStyle:"short" })}
                  </div>
                </div>
                <div style={{textAlign:"right"}}>
                  <span style={{display:"inline-block", padding:"3px 10px", borderRadius:6, fontSize:11, fontWeight:600, marginBottom:8, background:b.bg, color:b.fg}}>{b.label}</span>
                  <div style={{display:"flex", gap:6, justifyContent:"flex-end", flexWrap:"wrap"}}>
                    {rv.status !== "approved" && (
                      <button onClick={()=>moderate(rv.id,"approve")} disabled={busyId===rv.id} className="btn btn-ghost btn-sm" style={{borderColor:"var(--ok)", color:"var(--ok)"}}>
                        {busyId===rv.id ? "…" : t("Goedkeuren","Approve")}
                      </button>
                    )}
                    {rv.status !== "rejected" && (
                      <button onClick={()=>moderate(rv.id,"reject")} disabled={busyId===rv.id} className="btn btn-ghost btn-sm" style={{borderColor:"var(--bad)", color:"var(--bad)"}}>
                        {busyId===rv.id ? "…" : t("Afwijzen","Reject")}
                      </button>
                    )}
                  </div>
                </div>
              </div>
            </div>
          );
        })
      )}
    </div>
  );
};

const PageAdmin = ({ setPage }) => {
  const auth = __uc(window.AuthContext);
  const langCtx = __uc(window.LangContext || React.createContext({ lang:"nl", t: (nl)=>nl }));
  const t = langCtx.t || ((nl) => nl);

  const [currentAdmin, setCurrentAdmin] = __us(null);
  const [adminReady, setAdminReady] = __us(false);
  const [schemaMissing, setSchemaMissing] = __us(false);

  const [orders, setOrders] = __us([]);
  const [ordersLoading, setOrdersLoading] = __us(true);
  const [orderFilter, setOrderFilter] = __us("all");
  const [orderSearch, setOrderSearch] = __us("");

  // Live order updates (Supabase Realtime + fallback polling).
  const [liveStatus, setLiveStatus] = __us("connecting"); // connecting | live | fallback
  const [orderToast, setOrderToast] = __us(null);          // { count } when new orders arrive
  const [highlightId, setHighlightId] = __us(null);
  const liveStatusRef = __ur("connecting");
  const reloadTimerRef = __ur(null);
  const pollRef = __ur(null);
  const unsubRef = __ur(null);
  const connectTimerRef = __ur(null);
  const highlightTimerRef = __ur(null);
  const toastTimerRef = __ur(null);
  const audioCtxRef = __ur(null);

  const [section, setSection] = __us("orders");

  // ─── Role-change watcher (target side) ─────────────────────────────────────
  // Subscribes to realtime INSERTs on admin_role_notifications filtered to the
  // caller's own target_email, polls every 12 s as a fallback (paused when the
  // tab is hidden), and re-fetches getCurrentAdmin() + unseen notifications on
  // any signal. The realtime payload is used ONLY as a trigger; perms come
  // from the sanitized get_my_admin_row() RPC (no actor leak). The popup is
  // driven off the latest unseen notification's `kind` — server-classified so
  // the offline / next-login path is handled uniformly.
  const [roleNotice, setRoleNotice] = __us(null);
  const firstLoadRef = __ur(false);
  const roleUnsubRef = __ur(null);
  const rolePollRef  = __ur(null);

  const refreshSelf = React.useCallback(async () => {
    if (!window.zsb || !window.zsb.isConfigured) return null;
    const [adminRes, notifsRes] = await Promise.all([
      window.zsb.getCurrentAdmin(),
      (window.zsb.getMyUnseenRoleNotifications ? window.zsb.getMyUnseenRoleNotifications() : Promise.resolve({ data: [] })),
    ]);
    if (adminRes && adminRes.schemaMissing) setSchemaMissing(true);
    const newAdmin = (adminRes && adminRes.data) || null;
    const unseen   = (notifsRes && notifsRes.data) || [];
    setCurrentAdmin(newAdmin);

    if (!firstLoadRef.current) {
      // FIRST load: never auto-pop on the existing state. ONLY pop if there are
      // unseen notifications from an offline period (acceptance test D).
      firstLoadRef.current = true;
      if (unseen.length > 0) {
        setRoleNotice({ kind: unseen[0].kind });
      }
      return newAdmin;
    }

    // Subsequent refresh: a new unseen notification means a fresh server event.
    if (unseen.length > 0) {
      setRoleNotice({ kind: unseen[0].kind });
    } else if (!newAdmin || !newAdmin.active) {
      // Defensive: row gone (hard delete) or active=false without notif row
      // (legacy edit) → treat as revoked so the user always exits cleanly.
      setRoleNotice(cur => cur || { kind: "access_revoked" });
    }
    return newAdmin;
  }, []);

  // Load admin user info + open the role-change channel.
  __ue(() => {
    if (!auth || !auth.ready) return;
    if (!auth.user) {
      setCurrentAdmin(null);
      setAdminReady(true);
      firstLoadRef.current = false;
      return;
    }
    if (!window.zsb || !window.zsb.isConfigured) { setAdminReady(true); return; }

    const email = (auth.user.email || "").toLowerCase();
    firstLoadRef.current = false; // reset across sign-in changes

    refreshSelf().finally(() => setAdminReady(true));

    // Realtime: INSERTs on admin_role_notifications filtered to self.
    // Payload is NOT trusted — used only as a trigger for refreshSelf().
    const unsub = (window.zsb && typeof window.zsb.subscribeMyRoleNotifications === "function") ? window.zsb.subscribeMyRoleNotifications({
      email,
      onInsert: () => { refreshSelf(); },
      onStatus: () => {},
    }) : null;
    roleUnsubRef.current = unsub;

    // Polling fallback: every 12 s while tab is visible.
    rolePollRef.current = setInterval(() => {
      if (typeof document !== "undefined" && document.visibilityState === "hidden") return;
      refreshSelf();
    }, 12000);

    // Visibility: immediate refresh on tab-visible.
    const onVis = () => {
      if (typeof document !== "undefined" && document.visibilityState === "visible") refreshSelf();
    };
    if (typeof document !== "undefined") document.addEventListener("visibilitychange", onVis);

    return () => {
      try { if (roleUnsubRef.current) roleUnsubRef.current(); } catch (_) {}
      roleUnsubRef.current = null;
      if (rolePollRef.current) { clearInterval(rolePollRef.current); rolePollRef.current = null; }
      if (typeof document !== "undefined") document.removeEventListener("visibilitychange", onVis);
    };
    // eslint-disable-next-line
  }, [auth && auth.ready, auth && auth.user && auth.user.id]);

  // Popup acknowledgement handler.
  const acknowledgeRole = async () => {
    const kind = roleNotice && roleNotice.kind;
    // Mark all unseen notifications for this user as seen (owner-only RPC,
    // SECURITY DEFINER, scoped to own email + seen_at column).
    try { if (window.zsb && window.zsb.markAllMyRoleNotificationsSeen) await window.zsb.markAllMyRoleNotificationsSeen(); } catch (_) {}
    setRoleNotice(null);
    if (kind === "access_revoked") {
      // Stop realtime + polling, clear admin state, send to account page.
      try { if (roleUnsubRef.current) roleUnsubRef.current(); } catch (_) {}
      roleUnsubRef.current = null;
      if (rolePollRef.current) { clearInterval(rolePollRef.current); rolePollRef.current = null; }
      setCurrentAdmin(null);
      if (typeof setPage === "function") setPage("account");
    } else {
      // access_granted / rights_changed / reactivated → currentAdmin is already
      // refreshed; the tab/buttons re-render from the new perms automatically.
      refreshSelf();
    }
  };

  // Derived permissions (from admin_users row, fallback to false)
  const isOwner   = !!(currentAdmin && currentAdmin.role === "owner" && currentAdmin.active);
  const isAdminish = !!(currentAdmin && currentAdmin.active && (currentAdmin.role === "owner" || currentAdmin.role === "admin"));
  const can = {
    prices:   isOwner || !!(currentAdmin && currentAdmin.active && currentAdmin.can_manage_prices),
    orders:   isOwner || !!(currentAdmin && currentAdmin.active && currentAdmin.can_manage_orders),
    settings: isOwner || !!(currentAdmin && currentAdmin.active && currentAdmin.can_manage_settings),
    admins:   isOwner, // Adminbeheer is OWNER-ONLY (UI tab + RLS). can_manage_admins no longer grants write access.
    audit:    isOwner, // Audit logs are OWNER-ONLY (UI tab + RLS). can_view_audit_logs no longer grants access.
  };

  // Decide default section based on permissions
  __ue(() => {
    if (!adminReady) return;
    if (section === "orders" && !can.orders)   setSection(can.prices ? "products" : can.admins ? "admins" : can.audit ? "audit" : can.settings ? "settings" : "orders");
  }, [adminReady, can.orders, can.prices, can.admins, can.audit, can.settings]);

  // Load orders (only if can.orders). `silent` skips the loading spinner so
  // realtime/poll refreshes don't flicker the list. On a fetch error we surface
  // the warning state (the spec's 401/403 case) instead of swallowing it — true
  // session loss is handled separately by the auth gate below (which unmounts
  // this whole section and triggers the realtime cleanup).
  const loadOrders = (silent) => {
    if (!window.zsb || !window.zsb.isConfigured) { setOrdersLoading(false); return; }
    if (!silent) setOrdersLoading(true);
    window.zsb.getAllOrders().then(({ data, error }) => {
      if (error) {
        liveStatusRef.current = "fallback";
        setLiveStatus("fallback");
      } else {
        setOrders(data || []);
      }
      setOrdersLoading(false);
    });
  };
  __ue(() => { if (can.orders) loadOrders(); /* eslint-disable-next-line */ }, [can.orders]);

  // ──────────── Live order updates: Realtime (primary) + polling (safety net) ──
  // Starts only AFTER the admin/orders permission check (can.orders). The
  // realtime payload is NEVER injected into the UI — every event triggers a
  // re-fetch through the existing RLS-protected getAllOrders(). No service role,
  // no secrets, no extra Vercel function (realtime is client-side).
  __ue(() => {
    if (!can.orders) return;

    const beep = () => {
      try {
        const AC = window.AudioContext || window.webkitAudioContext;
        if (!AC) return;
        if (!audioCtxRef.current) audioCtxRef.current = new AC();
        const ctx = audioCtxRef.current;
        if (ctx.state === "suspended" && ctx.resume) ctx.resume();
        const osc = ctx.createOscillator();
        const gain = ctx.createGain();
        osc.frequency.value = 880;
        gain.gain.value = 0.04;
        osc.connect(gain); gain.connect(ctx.destination);
        osc.start();
        gain.gain.exponentialRampToValueAtTime(0.0001, ctx.currentTime + 0.25);
        osc.stop(ctx.currentTime + 0.26);
      } catch (_) { /* autoplay blocked — ignore */ }
    };

    const scheduleReload = () => {
      if (reloadTimerRef.current) clearTimeout(reloadTimerRef.current);
      reloadTimerRef.current = setTimeout(() => loadOrders(true), 400);
    };

    // Keep the indicator honest: if SUBSCRIBED never arrives, drop
    // "connecting" → "fallback" after 7s (polling is already covering us).
    connectTimerRef.current = setTimeout(() => {
      if (liveStatusRef.current !== "live") { liveStatusRef.current = "fallback"; setLiveStatus("fallback"); }
    }, 7000);

    const unsub = (window.zsb && typeof window.zsb.subscribeOrders === "function") ? window.zsb.subscribeOrders({
      onInsert: (payload) => {
        const id = payload && payload.new && payload.new.id;
        if (id) {
          setHighlightId(id);
          if (highlightTimerRef.current) clearTimeout(highlightTimerRef.current);
          highlightTimerRef.current = setTimeout(() => setHighlightId(cur => (cur === id ? null : cur)), 4500);
        }
        setOrderToast(prev => ({ count: (prev ? prev.count : 0) + 1 }));
        if (toastTimerRef.current) clearTimeout(toastTimerRef.current);
        toastTimerRef.current = setTimeout(() => setOrderToast(null), 8000);
        beep();
        scheduleReload();
      },
      onUpdate: () => { scheduleReload(); },
      onStatus: (status) => {
        const live = status === "SUBSCRIBED";
        liveStatusRef.current = live ? "live" : "fallback";
        setLiveStatus(live ? "live" : "fallback");
      },
    }) : null;
    unsubRef.current = unsub;
    if (!unsub) { liveStatusRef.current = "fallback"; setLiveStatus("fallback"); }

    // Fallback polling: every 10s, only when the tab is visible AND realtime is
    // not confirmed live. "connecting" counts as not-live, so polling covers the
    // startup window and the case where realtime never connects.
    pollRef.current = setInterval(() => {
      if (typeof document !== "undefined" && document.visibilityState === "hidden") return;
      if (liveStatusRef.current !== "live") loadOrders(true);
    }, 10000);

    const onVis = () => { if (typeof document !== "undefined" && document.visibilityState === "visible") loadOrders(true); };
    if (typeof document !== "undefined") document.addEventListener("visibilitychange", onVis);

    return () => {
      if (unsubRef.current) { try { unsubRef.current(); } catch (_) {} unsubRef.current = null; }
      if (pollRef.current) { clearInterval(pollRef.current); pollRef.current = null; }
      if (reloadTimerRef.current) { clearTimeout(reloadTimerRef.current); reloadTimerRef.current = null; }
      if (connectTimerRef.current) { clearTimeout(connectTimerRef.current); connectTimerRef.current = null; }
      if (highlightTimerRef.current) { clearTimeout(highlightTimerRef.current); highlightTimerRef.current = null; }
      if (toastTimerRef.current) { clearTimeout(toastTimerRef.current); toastTimerRef.current = null; }
      if (typeof document !== "undefined") document.removeEventListener("visibilitychange", onVis);
      try { if (audioCtxRef.current && audioCtxRef.current.close) { audioCtxRef.current.close(); audioCtxRef.current = null; } } catch (_) {}
    };
    // eslint-disable-next-line
  }, [can.orders]);

  // ──────────── Auth/permission gates ────────────
  if (!auth || !auth.ready || !adminReady) {
    return <div className="page section"><div className="wrap"><p>{t("Laden…","Loading…")}</p></div></div>;
  }
  if (!auth.user) {
    return (
      <div className="page section">
        <div className="wrap" style={{maxWidth:520, textAlign:"center"}}>
          <h2 className="h-display" style={{fontSize:32, marginBottom:10}}>{t("Log eerst in als admin","Sign in as admin first")}</h2>
          <button className="btn btn-primary" onClick={()=>setPage("login")}>{t("Naar inloggen","To sign in")}</button>
        </div>
      </div>
    );
  }
  if (!isAdminish) {
    return (
      <div className="page section">
        <div className="wrap" style={{maxWidth:520, textAlign:"center"}}>
          <h2 className="h-display" style={{fontSize:32, marginBottom:10}}>{t("Geen toegang","No access")}</h2>
          <p style={{color:"var(--ink-dim)", marginBottom:18}}>
            {t("Je bent ingelogd, maar je staat niet in admin_users (of bent uitgeschakeld).","You're signed in, but you're not in admin_users (or you've been disabled).")}
          </p>
          {schemaMissing && (
            <div className="card" style={{padding:16, background:"#fff8e6", border:"1px solid #f0d985", marginBottom:18, fontSize:13, textAlign:"left"}}>
              <strong>{t("Adminbeheer-SQL nog niet gerund.","Admin SQL not yet run.")}</strong>
              <div style={{color:"var(--ink-dim)", marginTop:6}}>
                {t("Run supabase-admin-audit.sql in Supabase SQL Editor (zie de SQL in dit project).","Run supabase-admin-audit.sql in the Supabase SQL Editor (see SQL in this project).")}
              </div>
            </div>
          )}
          <button className="btn btn-primary" onClick={()=>setPage("account")}>{t("Terug naar mijn account","Back to my account")}</button>
        </div>
        {/* Popup overlay: shown even on the no-access gate so a fresh revoke
            during this session is always acknowledged before navigating away. */}
        <RoleChangePopup notice={roleNotice} t={t} onAcknowledge={acknowledgeRole}/>
      </div>
    );
  }

  // ──────────── Filtered orders ────────────
  const filtered = orders
    .filter(o => orderFilter === "all" ? true :
      orderFilter === "payment_paid" ? o.payment_status === "paid" :
      orderFilter === "payment_unpaid" ? o.payment_status !== "paid" :
      o.status === orderFilter)
    .filter(o => !orderSearch ? true : (
      (o.customer_name || "").toLowerCase().includes(orderSearch.toLowerCase()) ||
      (o.customer_email || "").toLowerCase().includes(orderSearch.toLowerCase()) ||
      (o.id || "").toLowerCase().includes(orderSearch.toLowerCase())
    ));
  const stats = (window.zsb && window.zsb.statusOrder ? window.zsb.statusOrder : []).reduce((acc, s) => {
    acc[s] = orders.filter(o => o.status === s).length;
    return acc;
  }, {});

  // ──────────── Sections list (only those allowed) ────────────
  const sections = [
    can.orders   ? { id:"orders",   label:t("Bestellingen","Orders") } : null,
    can.orders   ? { id:"reviews",  label:t("Reviews","Reviews") } : null,
    can.prices   ? { id:"products", label:t("Productprijzen","Product prices") } : null,
    can.admins   ? { id:"admins",   label:t("Adminbeheer","Admins") } : null,
    can.audit    ? { id:"audit",    label:t("Audit logs","Audit logs") } : null,
    can.settings ? { id:"settings", label:t("Settings","Settings") } : null,
  ].filter(Boolean);

  return (
    <div className="page">
      <section style={{padding:"32px 0 16px", borderBottom:"1px solid var(--line)"}}>
        <div className="wrap" style={{display:"flex", justifyContent:"space-between", alignItems:"flex-end", flexWrap:"wrap", gap:14}}>
          <div>
            <div className="eyebrow" style={{marginBottom:10}}>Admin</div>
            <h1 className="h-display" style={{fontSize:"clamp(28px,4vw,42px)"}}>{t("Adminpaneel","Admin panel")}</h1>
            <div className="gap-8" style={{marginTop:10, flexWrap:"wrap"}}>
              <span style={{
                display:"inline-block", padding:"4px 12px", borderRadius:6, fontSize:11.5, fontWeight:700, letterSpacing:".08em",
                background: isOwner ? "var(--accent)" : "var(--ink)", color: isOwner ? "#fff" : "var(--bg)",
                textTransform:"uppercase",
              }}>
                {isOwner ? "Owner" : "Admin"}
              </span>
              <span style={{fontSize:13, color:"var(--ink-dim)"}}>{auth.user.email}</span>
            </div>
          </div>
          <div className="gap-8">
            <button className="btn btn-ghost btn-sm" onClick={()=>setPage("account")}>{t("Mijn account","My account")}</button>
          </div>
        </div>

        {sections.length > 1 && (
          <div className="wrap" style={{marginTop:18, display:"flex", gap:6, flexWrap:"wrap"}}>
            {sections.map(s => (
              <button key={s.id} onClick={()=>setSection(s.id)} style={{
                padding:"8px 14px", borderRadius:8, fontSize:13, fontWeight:600,
                background: section === s.id ? "var(--ink)" : "var(--paper)",
                color: section === s.id ? "var(--bg)" : "var(--ink)",
                border: `1px solid ${section === s.id ? "var(--ink)" : "var(--line)"}`,
                cursor:"pointer",
              }}>{s.label}</button>
            ))}
          </div>
        )}
      </section>

      <section className="section-sm">
        <div className="wrap">

          {/* ORDERS */}
          {section === "orders" && can.orders && (
            <>
              <h2 className="h-display" style={{fontSize:22, marginBottom:14}}>{t("Bestellingen & reserveringen","Orders & reservations")}</h2>
              <div className="grid-4" style={{marginBottom:18}}>
                {[
                  {id:"all",                ti:t("Alles","All"),                       n:orders.length},
                  {id:"in_behandeling",     ti:t("In behandeling","Pending"),          n:stats.in_behandeling||0},
                  {id:"klaar_voor_ophalen", ti:t("Klaar v. ophalen","Ready for pickup"),n:stats.klaar_voor_ophalen||0},
                  {id:"onderweg",           ti:t("Onderweg","On the way"),             n:stats.onderweg||0},
                ].map(s => (
                  <button key={s.id} onClick={()=>setOrderFilter(s.id)} className="card" style={{
                    padding:14, cursor:"pointer", textAlign:"left",
                    border: orderFilter === s.id ? "2px solid var(--accent)" : "1px solid var(--line)",
                  }}>
                    <div className="mono" style={{fontSize:10.5, color:"var(--ink-mute)", letterSpacing:".1em", textTransform:"uppercase"}}>{s.ti}</div>
                    <div className="h-display accent" style={{fontSize:28, marginTop:2}}>{s.n}</div>
                  </button>
                ))}
              </div>

              <div style={{display:"flex", alignItems:"center", gap:8, marginBottom:10, fontSize:12}}>
                <span style={{width:8, height:8, borderRadius:50, background: liveStatus === "live" ? "var(--ok)" : liveStatus === "fallback" ? "var(--warn)" : "var(--ink-mute)", boxShadow: liveStatus === "live" ? "0 0 6px var(--ok)" : "none"}}/>
                <span className="mono" style={{letterSpacing:".04em", color:"var(--ink-dim)"}}>
                  {liveStatus === "live"
                    ? t("Live verbonden","Live connected")
                    : liveStatus === "fallback"
                      ? t("Live updates niet actief — automatische controle elke 10 sec","Live updates inactive — automatic check every 10 sec")
                      : t("Live verbinden…","Connecting live…")}
                </span>
              </div>

              {orderToast && (
                <div style={{display:"flex", alignItems:"center", gap:12, padding:"10px 14px", borderRadius:8, background:"var(--accent-soft)", border:"1px solid var(--accent)", marginBottom:12}}>
                  <span style={{fontWeight:600, fontSize:13.5}}>🔔 {orderToast.count > 1 ? t("Nieuwe bestellingen ontvangen","New orders received") + " (" + orderToast.count + ")" : t("Nieuwe bestelling ontvangen","New order received")}</span>
                  <button className="btn btn-accent btn-sm" style={{marginLeft:"auto"}} onClick={() => { setSection("orders"); setOrderToast(null); if (typeof window !== "undefined" && window.scrollTo) window.scrollTo({ top: 0, behavior: "smooth" }); }}>{t("Bekijken","View")}</button>
                </div>
              )}

              <div style={{display:"flex", gap:10, alignItems:"center", marginBottom:14, flexWrap:"wrap"}}>
                <select className="select" value={orderFilter} onChange={e=>setOrderFilter(e.target.value)} style={{maxWidth:240}}>
                  <option value="all">{t("Alle statussen","All statuses")}</option>
                  <option value="payment_paid">{t("Betaald","Paid")} ({orders.filter(o => o.payment_status === "paid").length})</option>
                  <option value="payment_unpaid">{t("Niet betaald","Unpaid")} ({orders.filter(o => o.payment_status !== "paid").length})</option>
                  {window.zsb.statusOrder.map(s => (
                    <option key={s} value={s}>{(window.zsb.statusLabel ? window.zsb.statusLabel(s, langCtx.lang) : window.zsb.statusLabels[s])} ({stats[s]||0})</option>
                  ))}
                </select>
                <input className="input" placeholder={t("Zoek op naam, e-mail of order-ID","Search by name, email or order-ID")} value={orderSearch} onChange={e=>setOrderSearch(e.target.value)} style={{flex:1, minWidth:240}}/>
                <button className="btn btn-ghost btn-sm" onClick={() => loadOrders()}>↻ {t("Vernieuw","Refresh")}</button>
              </div>

              {ordersLoading ? (
                <div className="card" style={{padding:36, textAlign:"center", color:"var(--ink-dim)"}}>{t("Laden…","Loading…")}</div>
              ) : filtered.length === 0 ? (
                <div className="card" style={{padding:36, textAlign:"center", color:"var(--ink-dim)"}}>{t("Geen bestellingen gevonden.","No orders found.")}</div>
              ) : (
                filtered.map(o => <AdminOrderRow key={o.id} order={o} onChanged={loadOrders} canOrders={can.orders} highlight={o.id === highlightId}/>)
              )}
            </>
          )}

          {/* PRODUCTS */}
          {section === "products" && can.prices && (
            <>
              <h2 className="h-display" style={{fontSize:22, marginBottom:14}}>{t("Productprijzen","Product prices")}</h2>
              <p style={{color:"var(--ink-dim)", fontSize:13.5, marginBottom:16, maxWidth:680}}>
                {t(
                  "Pas hier de live prijzen aan. Een wijziging is direct van kracht. Elke wijziging wordt automatisch gelogd in audit_logs.",
                  "Adjust live prices here. Changes are immediately effective. Each change is automatically logged in audit_logs."
                )}
              </p>
              <ProductsAdminSection canEdit={can.prices}/>
            </>
          )}

          {/* REVIEWS MODERATION (owner OR can_manage_orders) */}
          {section === "reviews" && can.orders && (
            <ReviewsModerationSection/>
          )}

          {/* ADMIN MANAGEMENT */}
          {section === "admins" && can.admins && (
            <AdminUsersSection currentAdmin={currentAdmin}/>
          )}

          {/* AUDIT LOGS */}
          {section === "audit" && can.audit && (
            <AuditLogsSection/>
          )}

          {/* SETTINGS */}
          {section === "settings" && can.settings && (
            <SettingsSection/>
          )}

          {/* If no sections at all available (admin row exists but all flags false) */}
          {sections.length === 0 && (
            <div className="card" style={{padding:24, textAlign:"center"}}>
              <h3 className="h-display" style={{fontSize:20, marginBottom:10}}>{t("Je hebt geen actieve permissies","You have no active permissions")}</h3>
              <p style={{color:"var(--ink-dim)"}}>{t("Vraag Owner om je permissies in te schakelen.","Ask Owner to enable your permissions.")}</p>
            </div>
          )}

        </div>
      </section>
      {/* Popup overlay — fixed-position, z-index 9999. Driven by the latest
          unseen admin_role_notifications.kind. The notification table has NO
          actor column so this overlay can never display who made the change. */}
      <RoleChangePopup notice={roleNotice} t={t} onAcknowledge={acknowledgeRole}/>
    </div>
  );
};

window.PageAdmin = PageAdmin;
