// === Products list with inline price edit ===
function ProductsScreen({ products, setProducts, goto, openDetail,
                          productsSource, productsLoading, productsError, productsMeta, reloadProducts }) {
  const { CATEGORIES } = window.MOCK;
  const isLive = productsSource === "live";
  const [search, setSearch] = useState("");
  const [activeCat, setActiveCat] = useState("all");
  const [activeStatus, setActiveStatus] = useState("all");
  const [selected, setSelected] = useState(new Set());
  const [edits, setEdits] = useState({});           // pid -> new salePrice (number)
  const [discountEdits, setDiscountEdits] = useState({}); // pid -> new immediateDiscount (number)
  const [confirmSave, setConfirmSave] = useState(false);
  const [naverSaving, setNaverSaving] = useState(false);
  const [saveProgress, setSaveProgress] = useState(null); // { done, total, succeeded:[], failed:[] }

  const filtered = useMemo(() => {
    return products.filter(p => {
      if (activeCat !== "all" && p.category !== activeCat) return false;
      if (activeStatus !== "all" && p.status !== activeStatus) return false;
      if (search && !p.name.toLowerCase().includes(search.toLowerCase()) && !p.sku.toLowerCase().includes(search.toLowerCase())) return false;
      return true;
    });
  }, [products, search, activeCat, activeStatus]);

  const toggleAll = () => {
    if (selected.size === filtered.length) setSelected(new Set());
    else setSelected(new Set(filtered.map(p => p.id)));
  };
  const toggleOne = (id) => {
    const s = new Set(selected);
    if (s.has(id)) s.delete(id); else s.add(id);
    setSelected(s);
  };

  // 어떤 상품이라도 dirty(가격 또는 즉시할인 변경 대기)인지 카운트
  const dirtyPids = useMemo(() => {
    const s = new Set();
    Object.keys(edits).forEach(k => s.add(k));
    Object.keys(discountEdits).forEach(k => s.add(k));
    return s;
  }, [edits, discountEdits]);
  const dirtyCount = dirtyPids.size;

  // 변경 대기 목록 (실제로 가격 또는 즉시할인 값이 다른 것만)
  const dirtyList = useMemo(() => {
    const out = [];
    dirtyPids.forEach(pid => {
      const p = products.find(x => x.id === pid);
      if (!p) return;
      const oldPrice = p.price || 0;
      const oldDiscount = (p.price || 0) - (p.discountedPrice || p.price || 0);
      // edits/discountEdits 값이 있으면 사용, 없으면 기존 값 유지
      const priceRaw = edits[pid];
      const discountRaw = discountEdits[pid];
      const newPrice = priceRaw !== undefined
        ? parseInt(String(priceRaw).replace(/[^0-9]/g, ""), 10)
        : oldPrice;
      const newDiscount = discountRaw !== undefined
        ? parseInt(String(discountRaw).replace(/[^0-9]/g, ""), 10)
        : oldDiscount;
      const priceChanged = !isNaN(newPrice) && newPrice >= 0 && newPrice !== oldPrice;
      const discountChanged = !isNaN(newDiscount) && newDiscount >= 0 && newDiscount !== oldDiscount;
      if (!priceChanged && !discountChanged) return;
      const newDiscountedPrice = Math.max(0, newPrice - newDiscount);
      out.push({
        pid, name: p.name || pid,
        oldPrice, newPrice, priceChanged,
        oldDiscount, newDiscount, discountChanged,
        oldDiscountedPrice: Math.max(0, oldPrice - oldDiscount),
        newDiscountedPrice,
        invalidUnit: (priceChanged && newPrice % 10 !== 0) || (discountChanged && newDiscount % 10 !== 0),
        originProductNo: p.originProductNo,
      });
    });
    return out;
  }, [dirtyPids, edits, discountEdits, products]);

  const hasInvalidUnits = dirtyList.some(e => e.invalidUnit);

  const savePrice = (pid) => {
    const v = parseInt(edits[pid].toString().replace(/[^0-9]/g, ""), 10);
    if (isNaN(v) || v < 0) { toast("올바른 가격을 입력하세요"); return; }
    if (isLive) {
      // 라이브 모드: 10원 단위 정규화 후 edits 유지. 사용자가 "네이버에 적용..."으로 모달 띄움.
      const normalized = Math.floor(v / 10) * 10;
      if (normalized !== v) toast(`10원 단위로 자동 조정: ${formatKRW(normalized)}`);
      setEdits(prev => ({ ...prev, [pid]: normalized }));
      return;
    }
    setProducts(prev => prev.map(p => p.id === pid ? { ...p, price: v } : p));
    setEdits(prev => { const n = { ...prev }; delete n[pid]; return n; });
    toast("판매가가 저장되었습니다");
  };

  // 즉시할인 amount 변경 (라이브 모드 전용)
  const saveDiscount = (pid) => {
    const raw = discountEdits[pid];
    if (raw === undefined) return;
    const v = parseInt(String(raw).replace(/[^0-9]/g, ""), 10);
    if (isNaN(v) || v < 0) { toast("올바른 즉시할인 금액을 입력하세요"); return; }
    const normalized = Math.floor(v / 10) * 10;
    if (normalized !== v) toast(`즉시할인 10원 단위로 자동 조정: ${formatNum(normalized)}원`);
    // 판매가보다 큰 할인은 불가
    const p = products.find(x => x.id === pid);
    const priceForCap = (edits[pid] !== undefined ? edits[pid] : p?.price) || 0;
    if (normalized > priceForCap) {
      toast(`즉시할인이 판매가(${formatKRW(priceForCap)})를 넘을 수 없습니다`);
      setDiscountEdits(prev => ({ ...prev, [pid]: priceForCap }));
      return;
    }
    setDiscountEdits(prev => ({ ...prev, [pid]: normalized }));
  };

  const saveAllEdits = () => {
    if (isLive) {
      if (dirtyList.length === 0) { toast("변경된 값이 없습니다"); return; }
      setSaveProgress(null);
      setConfirmSave(true);
      return;
    }
    setProducts(prev => prev.map(p => {
      if (edits[p.id] === undefined) return p;
      const v = parseInt(edits[p.id].toString().replace(/[^0-9]/g, ""), 10);
      if (isNaN(v) || v < 0) return p;
      return { ...p, price: v };
    }));
    setEdits({});
    toast(`${dirtyCount}개 상품의 판매가가 저장되었습니다`);
  };

  const discardEdits = () => {
    setEdits({});
    setDiscountEdits({});
    toast("변경사항이 취소되었습니다");
  };

  // 라이브 모드 — 모달 확인 후 호출. 순차 PUT (salePrice + immediateDiscount 동시 변경 지원).
  const saveAllEditsToNaver = async () => {
    setNaverSaving(true);
    const succeeded = [];
    const failed = [];
    setSaveProgress({ done: 0, total: dirtyList.length, succeeded: [], failed: [] });
    for (let i = 0; i < dirtyList.length; i++) {
      const item = dirtyList[i];
      try {
        if (!item.originProductNo) throw new Error("originProductNo 누락 — 라이브 상품 아님");
        const changes = {};
        if (item.priceChanged) changes.salePrice = item.newPrice;
        if (item.discountChanged) changes.immediateDiscount = item.newDiscount;
        await window.API.naverChangeProductPricing(item.originProductNo, changes);
        succeeded.push(item);
      } catch (err) {
        console.error("[saveToNaver]", item.pid, err);
        failed.push({ ...item, error: err.message });
      }
      setSaveProgress({ done: i + 1, total: dirtyList.length, succeeded: [...succeeded], failed: [...failed] });
    }

    // 성공한 것만 products에 반영, edits/discountEdits에서 제거
    if (succeeded.length > 0) {
      const okIds = new Set(succeeded.map(x => x.pid));
      setProducts(prev => prev.map(p => {
        if (!okIds.has(p.id)) return p;
        const item = succeeded.find(x => x.pid === p.id);
        return { ...p, price: item.newPrice, discountedPrice: item.newDiscountedPrice };
      }));
      setEdits(prev => { const n = { ...prev }; okIds.forEach(id => delete n[id]); return n; });
      setDiscountEdits(prev => { const n = { ...prev }; okIds.forEach(id => delete n[id]); return n; });
    }

    setNaverSaving(false);
    if (failed.length === 0) {
      toast(`${succeeded.length}개 변경사항이 네이버에 반영되었습니다`);
      setTimeout(() => setConfirmSave(false), 1500);
    } else {
      toast(`${succeeded.length}개 성공, ${failed.length}개 실패 — 모달에서 확인`);
    }
  };

  return (
    <div className="page">
      <div className="page-header" style={{ display: "flex", alignItems: "flex-end", gap: 16 }}>
        <div style={{ flex: 1 }}>
          <h1 className="page-title">상품 관리</h1>
          <p className="page-desc">
            {isLive
              ? `네이버 라이브 ${products.length}개 표시 (전체 ${productsMeta?.totalElements || "?"}개)`
              : productsLoading
                ? "네이버에서 불러오는 중..."
                : productsError
                  ? `네이버 조회 실패 — 샘플 데이터 표시 중`
                  : `샘플 데이터 ${products.length}개 (API 설정에서 자격증명 입력 시 라이브 표시)`}
          </p>
        </div>
        <div className="row gap-2">
          {isLive && reloadProducts && (
            <button className="btn" onClick={() => reloadProducts()} disabled={productsLoading}>
              <window.I.Refresh size={14} /> 새로고침
            </button>
          )}
          <button className="btn" onClick={() => goto("bulk")}>
            <window.I.Filter size={14} /> 일괄 수정
          </button>
          <button className="btn btn-primary" onClick={() => goto("isvm")}>
            <window.I.Plus size={14} /> 신규 등록
          </button>
        </div>
      </div>

      {isLive && dirtyCount === 0 && (
        <div style={{
          padding: "8px 14px", marginBottom: 12, borderRadius: 8,
          background: "var(--accent-soft)", border: "1px solid var(--accent)",
          fontSize: 12, color: "var(--text-muted)", display: "flex", gap: 8, alignItems: "center"
        }}>
          <window.I.AlertCircle size={14} stroke="var(--accent)" />
          <span>
            가격 칸을 클릭해 수정 후 <strong style={{ color: "var(--text)" }}>"네이버에 적용..."</strong> 버튼을 누르면 확인 모달이 뜹니다.
            한 번에 여러 상품을 모아서 일괄 적용할 수 있어요.
          </span>
        </div>
      )}
      {productsError && (
        <div style={{
          padding: "10px 14px", marginBottom: 12, borderRadius: 8,
          background: "rgba(239, 68, 68, 0.08)", border: "1px solid rgba(239, 68, 68, 0.35)",
          fontSize: 12, color: "var(--danger)", display: "flex", gap: 8, alignItems: "center"
        }}>
          <window.I.AlertCircle size={14} stroke="var(--danger)" />
          <span>네이버 조회 실패: {productsError}</span>
          {reloadProducts && (
            <button className="btn btn-sm" style={{ marginLeft: "auto" }} onClick={() => reloadProducts()}>다시 시도</button>
          )}
        </div>
      )}

      {/* Toolbar */}
      <div className="toolbar">
        <div className="input-with-icon" style={{ flex: 1, maxWidth: 320 }}>
          <window.I.Search size={14} className="input-icon" />
          <input className="input" placeholder="상품명 또는 SKU 검색..." value={search} onChange={e => setSearch(e.target.value)} />
        </div>
        <div className="toolbar-divider" />
        <div className="toolbar-group" style={{ flexWrap: "wrap" }}>
          <button className={"chip " + (activeCat === "all" ? "active" : "")} onClick={() => setActiveCat("all")}>전체 카테고리</button>
          {CATEGORIES.map(c => (
            <button key={c.id} className={"chip " + (activeCat === c.id ? "active" : "")} onClick={() => setActiveCat(c.id)}>
              {c.name}
            </button>
          ))}
        </div>
        <div className="toolbar-divider" />
        <div className="seg">
          <button className={"seg-btn " + (activeStatus === "all" ? "active" : "")} onClick={() => setActiveStatus("all")}>전체</button>
          <button className={"seg-btn " + (activeStatus === "active" ? "active" : "")} onClick={() => setActiveStatus("active")}>판매중</button>
          <button className={"seg-btn " + (activeStatus === "outofstock" ? "active" : "")} onClick={() => setActiveStatus("outofstock")}>품절</button>
        </div>
      </div>

      {/* Selection / dirty bar */}
      {(selected.size > 0 || dirtyCount > 0) && (
        <div style={{
          padding: "10px 16px",
          background: "var(--accent-soft)",
          border: "1px solid var(--accent)",
          borderRadius: 8,
          marginBottom: 12,
          display: "flex", alignItems: "center", gap: 12,
        }}>
          {selected.size > 0 && (
            <>
              <window.I.Check size={14} stroke="var(--accent)" />
              <span style={{ fontSize: 13, fontWeight: 500 }}>{selected.size}개 선택됨</span>
              <button className="btn btn-sm" onClick={() => setSelected(new Set())}>해제</button>
            </>
          )}
          {dirtyCount > 0 && (
            <>
              <span style={{ marginLeft: selected.size > 0 ? 12 : 0, fontSize: 13, fontWeight: 500 }}>
                {dirtyCount}개 항목 {isLive ? "네이버 적용 대기 (판매가/즉시할인)" : "변경 대기"}
              </span>
              <div className="right row gap-2">
                <button className="btn btn-sm" onClick={discardEdits}>취소</button>
                <button className="btn btn-primary btn-sm" onClick={saveAllEdits}>
                  {isLive ? <><window.I.Check size={14} /> 네이버에 적용...</> : "전체 저장"}
                </button>
              </div>
            </>
          )}
        </div>
      )}

      {/* Table */}
      <div className="table-wrap">
        <table className="table">
          <thead>
            <tr>
              <th className="table-checkbox">
                <input type="checkbox" className="checkbox"
                       checked={filtered.length > 0 && selected.size === filtered.length}
                       onChange={toggleAll} />
              </th>
              <th>상품정보</th>
              <th>카테고리</th>
              <th>상태</th>
              <th className="numeric">재고</th>
              <th className="numeric">판매가</th>
              <th className="numeric">즉시할인</th>
              <th className="numeric">최종가</th>
              <th className="numeric">30일 판매</th>
              <th style={{ width: 80 }}></th>
            </tr>
          </thead>
          <tbody>
            {filtered.map(p => {
              const cat = CATEGORIES.find(c => c.id === p.category);
              const margin = ((p.price - p.costPrice) / p.price * 100).toFixed(1);
              const isDirty = edits[p.id] !== undefined;
              const isDiscountDirty = discountEdits[p.id] !== undefined;
              // 현재 표시할 값: dirty면 edits 값, 아니면 product 값
              const currentPrice = isDirty ? edits[p.id] : p.price;
              const currentDiscount = isDiscountDirty
                ? discountEdits[p.id]
                : (p.price - (p.discountedPrice || p.price));
              const currentDiscountedPrice = Math.max(0, (parseInt(currentPrice) || 0) - (parseInt(currentDiscount) || 0));
              const rowDirty = isDirty || isDiscountDirty;
              return (
                <tr key={p.id} className={selected.has(p.id) ? "selected" : ""}>
                  <td>
                    <input type="checkbox" className="checkbox" checked={selected.has(p.id)} onChange={() => toggleOne(p.id)} />
                  </td>
                  <td>
                    <div className="product-cell">
                      <ProductThumb tone={p.tone} name={p.name} size={40} />
                      <div style={{ minWidth: 0 }}>
                        <div className="product-name" style={{ cursor: "pointer" }} onClick={() => openDetail(p.id)}>{p.name}</div>
                        <div className="product-meta">{p.sku} · {p.brand}</div>
                      </div>
                    </div>
                  </td>
                  <td className="muted text-sm">{cat?.icon} {cat?.name}</td>
                  <td><StatusBadge status={p.status} /></td>
                  <td className="numeric">
                    <span className={p.stock === 0 ? "" : ""} style={{ color: p.stock === 0 ? "var(--danger)" : p.stock < 50 ? "var(--warning)" : "var(--text)" }}>
                      {formatNum(p.stock)}
                    </span>
                  </td>
                  {/* 판매가 (편집 가능) */}
                  <td className="numeric">
                    <input
                      className={"price-edit " + (isDirty ? "dirty" : "")}
                      type="text"
                      value={isDirty ? edits[p.id] : formatNum(p.price)}
                      onChange={e => setEdits(prev => ({ ...prev, [p.id]: e.target.value.replace(/[^0-9]/g, "") }))}
                      onBlur={() => isDirty && savePrice(p.id)}
                      onKeyDown={e => {
                        if (e.key === "Enter") { e.target.blur(); }
                        if (e.key === "Escape") { setEdits(prev => { const n = { ...prev }; delete n[p.id]; return n; }); }
                      }}
                      title="판매가 (정가)"
                    />
                    {!isLive && (
                      <div className="text-xs muted" style={{ marginTop: 2 }}>마진 {margin}%</div>
                    )}
                  </td>
                  {/* 즉시할인 (라이브 편집 가능, mock에서 readonly) */}
                  <td className="numeric">
                    {isLive ? (
                      <input
                        className={"price-edit " + (isDiscountDirty ? "dirty" : "")}
                        type="text"
                        value={isDiscountDirty ? discountEdits[p.id] : formatNum(p.price - (p.discountedPrice || p.price))}
                        onChange={e => setDiscountEdits(prev => ({ ...prev, [p.id]: e.target.value.replace(/[^0-9]/g, "") }))}
                        onBlur={() => isDiscountDirty && saveDiscount(p.id)}
                        onKeyDown={e => {
                          if (e.key === "Enter") { e.target.blur(); }
                          if (e.key === "Escape") { setDiscountEdits(prev => { const n = { ...prev }; delete n[p.id]; return n; }); }
                        }}
                        title="즉시할인 (0 = 할인 안 함)"
                      />
                    ) : (
                      <span className="muted">—</span>
                    )}
                  </td>
                  {/* 최종가 (자동 계산, 편집 불가) */}
                  <td className="numeric">
                    {isLive ? (
                      <>
                        <div style={{ fontWeight: 600, color: "var(--accent)" }}>
                          {formatKRW(currentDiscountedPrice)}
                        </div>
                        {rowDirty && (
                          <div className="text-xs muted" style={{ marginTop: 2 }}>(미적용)</div>
                        )}
                      </>
                    ) : (
                      <span className="muted">{formatKRW(p.price)}</span>
                    )}
                  </td>
                  <td className="numeric">
                    <div style={{ fontWeight: 600 }}>{p.sales30d}건</div>
                    <div className="text-xs muted">{formatKRW(p.sales30d * p.price)}</div>
                  </td>
                  <td>
                    <button className="btn btn-icon btn-sm btn-ghost" onClick={() => openDetail(p.id)}>
                      <window.I.Eye size={14} />
                    </button>
                  </td>
                </tr>
              );
            })}
          </tbody>
        </table>
        {filtered.length === 0 && (
          <div className="empty">
            <window.I.Search size={32} className="empty-icon" />
            <div>검색 결과가 없습니다</div>
          </div>
        )}
      </div>

      {/* 네이버 가격 적용 확인 모달 (라이브 모드 전용) */}
      <window.Modal
        open={confirmSave}
        onClose={() => { if (!naverSaving) { setConfirmSave(false); setSaveProgress(null); } }}
        title="네이버에 가격 변경 적용"
        size="lg"
        footer={
          !saveProgress ? (
            <>
              <button className="btn" onClick={() => setConfirmSave(false)}>취소</button>
              <button className="btn btn-primary" onClick={saveAllEditsToNaver}>
                <window.I.Check size={14} /> {dirtyList.length}개 네이버에 적용
              </button>
            </>
          ) : naverSaving ? (
            <button className="btn" disabled>
              저장 중... ({saveProgress.done}/{saveProgress.total})
            </button>
          ) : (
            <button className="btn btn-primary" onClick={() => { setConfirmSave(false); setSaveProgress(null); }}>
              닫기
            </button>
          )
        }>
        {!saveProgress ? (
          <>
            <p style={{ marginBottom: 12, fontSize: 13 }}>
              다음 <strong>{dirtyList.length}개 상품</strong>의 판매가를 네이버 스토어에 즉시 반영합니다.
            </p>
            <div style={{ maxHeight: 340, overflow: "auto", border: "1px solid var(--border)", borderRadius: 6 }}>
              <table className="table">
                <thead>
                  <tr>
                    <th>상품명 · 할인가 변동</th>
                    <th className="numeric">판매가</th>
                    <th className="numeric">즉시할인</th>
                  </tr>
                </thead>
                <tbody>
                  {dirtyList.map(item => {
                    const priceDiff = item.newPrice - item.oldPrice;
                    const discountDiff = item.newDiscount - item.oldDiscount;
                    return (
                      <tr key={item.pid}>
                        <td style={{ maxWidth: 280, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }} title={item.name}>
                          {item.name}
                          <div className="text-xs" style={{ marginTop: 2, color: "var(--accent)" }}>
                            할인가 {formatKRW(item.oldDiscountedPrice)} → {formatKRW(item.newDiscountedPrice)}
                          </div>
                        </td>
                        <td className="numeric" style={{ fontSize: 12 }}>
                          {item.priceChanged ? (
                            <>
                              <div className="muted">{formatKRW(item.oldPrice)}</div>
                              <div style={{ fontWeight: 600 }}>→ {formatKRW(item.newPrice)}</div>
                              <div style={{ color: priceDiff > 0 ? "var(--success)" : "var(--danger)", fontSize: 11 }}>
                                {priceDiff > 0 ? "+" : ""}{formatNum(priceDiff)}
                              </div>
                            </>
                          ) : <span className="muted">{formatKRW(item.oldPrice)}</span>}
                        </td>
                        <td className="numeric" style={{ fontSize: 12 }}>
                          {item.discountChanged ? (
                            <>
                              <div className="muted">{formatNum(item.oldDiscount)}원</div>
                              <div style={{ fontWeight: 600 }}>→ {formatNum(item.newDiscount)}원</div>
                              <div style={{ color: discountDiff > 0 ? "var(--success)" : "var(--danger)", fontSize: 11 }}>
                                {discountDiff > 0 ? "+" : ""}{formatNum(discountDiff)}
                              </div>
                            </>
                          ) : <span className="muted">{formatNum(item.oldDiscount)}원</span>}
                        </td>
                      </tr>
                    );
                  })}
                </tbody>
              </table>
            </div>
            <div style={{
              marginTop: 12, padding: "10px 12px", borderRadius: 6,
              background: "rgba(245, 158, 11, 0.08)", border: "1px solid rgba(245, 158, 11, 0.35)",
              fontSize: 12, color: "var(--text-muted)", display: "flex", gap: 8, alignItems: "flex-start"
            }}>
              <window.I.AlertCircle size={14} stroke="var(--warning)" style={{ flexShrink: 0, marginTop: 1 }} />
              <span>
                <strong style={{ color: "var(--text)" }}>주의:</strong> 적용은 즉시 네이버 스토어에 반영됩니다.
                되돌리려면 다시 가격을 변경해야 합니다.
                (참고: 첫 PUT 시 상품의 detailContent 일부 메타데이터가 sanitize됩니다 — 이미지/텍스트 표시는 정상 유지)
              </span>
            </div>
          </>
        ) : (
          <>
            <div style={{ marginBottom: 12 }}>
              <div style={{ display: "flex", justifyContent: "space-between", marginBottom: 6, fontSize: 13 }}>
                <span>{naverSaving ? "네이버에 적용 중..." : "완료"}</span>
                <span>{saveProgress.done} / {saveProgress.total}</span>
              </div>
              <div style={{ height: 8, background: "var(--border)", borderRadius: 4, overflow: "hidden" }}>
                <div style={{
                  height: "100%",
                  width: `${saveProgress.total ? (saveProgress.done / saveProgress.total * 100) : 0}%`,
                  background: "var(--accent)",
                  transition: "width 0.2s",
                }} />
              </div>
            </div>
            <div style={{ display: "flex", gap: 16, fontSize: 13, marginBottom: 12 }}>
              <span><strong style={{ color: "var(--success)" }}>{saveProgress.succeeded.length}</strong> 성공</span>
              <span><strong style={{ color: "var(--danger)" }}>{saveProgress.failed.length}</strong> 실패</span>
            </div>
            {saveProgress.failed.length > 0 && (
              <div style={{ marginTop: 8, padding: 10, background: "rgba(239, 68, 68, 0.05)", border: "1px solid rgba(239, 68, 68, 0.2)", borderRadius: 6, fontSize: 12, maxHeight: 220, overflow: "auto" }}>
                <div style={{ fontWeight: 600, marginBottom: 6 }}>실패 목록 (다시 시도 가능):</div>
                {saveProgress.failed.map(f => (
                  <div key={f.pid} style={{ marginBottom: 6 }}>
                    <div style={{ color: "var(--text)" }}>{f.name}</div>
                    <div className="muted" style={{ paddingLeft: 8, fontSize: 11 }}>{f.error}</div>
                  </div>
                ))}
              </div>
            )}
          </>
        )}
      </window.Modal>
    </div>
  );
}

window.ProductsScreen = ProductsScreen;
