// Order detail view with SLA timeline

const OrderDetailView = ({ orderId, tweaks, onBack }) => {
  const fromGlobal = (window.ORDERS || []).find(o => o.id === orderId) || null;
  const [order, setOrder]     = React.useState(fromGlobal);
  const [fetching, setFetching] = React.useState(!fromGlobal);
  const [fetchErr, setFetchErr] = React.useState(null);
  const [docs, setDocs]         = React.useState(fromGlobal?.documents || []);
  const [uploading, setUploading] = React.useState(false);
  const uploadInputRef = React.useRef(null);
  const [showEditModal, setShowEditModal] = React.useState(false);
  const [showDeleteModal, setShowDeleteModal] = React.useState(false);
  const [showUploadDocModal, setShowUploadDocModal] = React.useState(false);
  const [viewingDoc, setViewingDoc] = React.useState(null); // { id, filename, orderId, hasFile }
  const [viewingDocBlobUrl, setViewingDocBlobUrl] = React.useState(null);

  React.useEffect(() => {
    if (!viewingDoc?.hasFile) { setViewingDocBlobUrl(null); return; }
    let url = null;
    window.apiFetch(`/api/orders/${viewingDoc.orderId}/documents/${viewingDoc.id}/file`)
      .then(r => r.blob()).then(b => { url = URL.createObjectURL(b); setViewingDocBlobUrl(url); })
      .catch(() => setViewingDocBlobUrl(null));
    return () => { if (url) URL.revokeObjectURL(url); };
  }, [viewingDoc]);
  const [missingDocs, setMissingDocs] = React.useState(null); // null = hidden, [...] = show dialog
  const [allContacts, setAllContacts] = React.useState(null); // null = loading

  React.useEffect(() => {
    if (!order?.company?.id) return;
    window.apiFetch(`/api/companies/${order.company.id}/contacts`)
      .then(r => r.ok ? r.json() : [])
      .then(rows => setAllContacts(rows))
      .catch(() => setAllContacts([]));
  }, [order?.company?.id]);

  React.useEffect(() => {
    if (fromGlobal) { setOrder(fromGlobal); setDocs(fromGlobal.documents || []); setFetching(false); return; }
    setFetching(true);
    window.apiFetch(`/api/orders/${orderId}`)
      .then(r => r.ok ? r.json() : r.json().then(d => Promise.reject(d.error || 'ไม่พบ Order')))
      .then(d => { setOrder(d); setDocs(d.documents || []); setFetching(false); })
      .catch(e => { setFetchErr(typeof e === 'string' ? e : 'โหลดข้อมูลไม่ได้'); setFetching(false); });
  }, [orderId]);

  const handleUploadFiles = async (e) => {
    const files = Array.from(e.target.files || []);
    if (!files.length) return;
    e.target.value = '';
    setUploading(true);
    try {
      for (const f of files) {
        const fd = new FormData();
        fd.append('file', f);
        const r = await fetch(`/api/orders/${orderId}/documents/upload`, {
          method: 'POST',
          headers: { Authorization: `Bearer ${localStorage.getItem('sol_auth_token')}` },
          body: fd,
        });
        if (!r.ok) {
          const d = await r.json();
          showToast(d.error || 'อัปโหลดไม่สำเร็จ', { variant: 'error' });
          return;
        }
      }
      const refreshed = await window.apiFetch(`/api/orders/${orderId}/documents`).then(r => r.ok ? r.json() : null);
      if (refreshed) setDocs(refreshed);
      showToast(`อัปโหลด ${files.length} ไฟล์สำเร็จ`, { variant: 'success' });
    } catch {
      showToast('อัปโหลดไม่สำเร็จ', { variant: 'error' });
    } finally {
      setUploading(false);
    }
  };

  if (fetching) return (
    <div style={{ padding: '60px 20px', textAlign: 'center', color: 'var(--ink-3)', fontSize: 13 }}>
      กำลังโหลด Order <span className="num">{orderId}</span>…
    </div>
  );

  if (!order || fetchErr) return (
    <div style={{ padding: '40px 20px', textAlign: 'center', color: 'var(--ink-3)' }}>
      <Icon name="file" size={28}/><br/>
      <div style={{ marginTop: 12, fontSize: 14 }}>ไม่พบ Order <span className="num">{orderId}</span></div>
      <div style={{ marginTop: 6, fontSize: 12 }}>{fetchErr || 'อาจถูกลบหรือยังไม่ได้โหลดข้อมูล'}</div>
    </div>
  );

  const allProds = window.PRODUCTS || PRODUCTS || [];
  const products = order.items.map(it => {
    const product = allProds.find(p => p.id === it.productId);
    const pkg = product?.packages?.find(p => p.id === it.packageId);
    return { item: it, product, pkg };
  }).filter(x => x.product);

  const totalUnits = order.items.reduce((s, it) => s + it.qty, 0);
  const maxSla = products.length ? Math.max(...products.map(p => p.product?.slaDays || 0)) : 0;
  const slaOverride = tweaks.slaOverride; // null = use product default, number = override

  const ownerUser = (window.USERS || USERS || []).find(u => u.id === order.owner);
  const ownerName = ownerUser?.name || order.owner || '—';

  const doSubmitDraft = async () => {
    const r = await window.apiFetch(`/api/orders/${order.id}/status`, {
      method: 'PATCH', body: JSON.stringify({ status: 'submitted' }),
    });
    if (r.ok) {
      await window.apiFetch(`/api/orders/${order.id}/activities`, {
        method: 'POST', body: JSON.stringify({ action: 'submitted' }),
      }).catch(() => {});
      showToast('Submit สำเร็จ — รออนุมัติ', { variant: 'success' });
      const d = await window.apiFetch(`/api/orders/${order.id}`).then(r => r.ok ? r.json() : null);
      if (d) { setOrder(d); setDocs(d.documents || []); }
    } else {
      const e = await r.json().catch(() => ({}));
      showToast(e.error || 'ไม่สามารถ submit ได้', { variant: 'error' });
    }
  };

  // Calc actual SLA per item, considering override
  const itemSla = (item) => {
    const prod = allProds.find(p => p.id === item.productId);
    return slaOverride != null ? slaOverride : (prod?.slaDays || 0);
  };
  const effectiveMaxSla = slaOverride != null ? slaOverride : maxSla;

  // Status pipeline (uses tweaks.statusOverride if set)
  const currentStatus = tweaks.statusOverride
    ? ORDER_STATUSES.find(s => s.id === tweaks.statusOverride)
    : order.status;

  return (
    <>
      {/* Breadcrumb + title */}
      <div style={{ marginBottom: 16 }}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 6, fontSize: 11.5, color: 'var(--ink-3)', marginBottom: 10 }}>
          <button onClick={onBack} style={{
            background: 'none', border: 'none', cursor: 'pointer', color: 'var(--ink-3)',
            fontFamily: 'Kanit, sans-serif', fontSize: 11.5, padding: 0,
            display: 'inline-flex', alignItems: 'center', gap: 4,
          }}>
            <Icon name="chevronLeft" size={11}/>
            Orders
          </button>
          <Icon name="chevron" size={9}/>
          <span className="num" style={{ color: 'var(--ink-2)' }}>{order.id}</span>
        </div>

        <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-end', gap: 16 }}>
          <div>
            <div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 4 }}>
              <h1 className="num" style={{ fontSize: 22, fontWeight: 500, letterSpacing: '-0.01em', margin: 0 }}>{order.id}</h1>
              <StatusChip status={currentStatus}/>
            </div>
            <div style={{ fontSize: 14, color: 'var(--ink-2)' }}>
              {order.company?.name || '—'}
              <span style={{ color: 'var(--ink-4)', margin: '0 8px' }}>·</span>
              <span style={{ fontSize: 12 }}>สร้างโดย {ownerName} · {fmtDate(order.createdAt)}</span>
            </div>
          </div>

          <div style={{ display: 'flex', gap: 8 }}>
            {(currentStatus.id === 'sent_back' || currentStatus.id === 'draft') && (
              <Button variant="ghost" icon="edit" onClick={() => setShowEditModal(true)}>Edit</Button>
            )}
            <Button variant="ghost" icon="download" onClick={async () => {
              try {
                const res = await window.apiFetch(`/api/orders/${order.id}/quote`);
                if (!res.ok) { showToast('ไม่สามารถสร้าง PDF ได้', { variant: 'error' }); return; }
                const blob = await res.blob();
                const url  = URL.createObjectURL(blob);
                const a    = document.createElement('a');
                a.href = url; a.download = `quote-${order.id}.pdf`; a.click();
                URL.revokeObjectURL(url);
              } catch { showToast('เกิดข้อผิดพลาด', { variant: 'error' }); }
            }}>Download quote</Button>

            {currentStatus.id === 'draft' && (
              <Button variant="ghost" icon="trash" onClick={() => setShowDeleteModal(true)}
                style={{ color: 'var(--negative)' }}>Delete Draft</Button>
            )}
            {currentStatus.id === 'draft' && (
              <Button variant="primary" icon="arrowRight" onClick={() => {
                // Check required documents before submitting
                const docTypes = window.DOC_TYPES || DOC_TYPES || [];
                const docReqs  = window.DOC_REQUIREMENTS || DOC_REQUIREMENTS || {};
                const docLevels = {};
                for (const it of (order.items || [])) {
                  const reqs = docReqs[it.productId] || {};
                  for (const [docId, level] of Object.entries(reqs)) {
                    if (level === 'none') continue;
                    const cur = docLevels[docId];
                    if (!cur || (level === 'required' && cur !== 'required')) docLevels[docId] = level;
                  }
                }
                const requiredTypes = docTypes.filter(d => docLevels[d.id] === 'required');
                const uploadedTypeIds = new Set((docs || []).map(d => d.doc_type_id || d.docTypeId).filter(Boolean));
                const missing = requiredTypes.filter(d => !uploadedTypeIds.has(d.id));
                if (missing.length > 0) {
                  setMissingDocs(missing);
                  return;
                }
                // All good — proceed to submit
                doSubmitDraft();
              }}>Submit for approval</Button>
            )}
            {currentStatus.id === 'sent_back' && (
              <Button variant="primary" icon="arrowRight"
                onClick={async () => {
                  const r = await window.apiFetch(`/api/orders/${order.id}/status`, {
                    method: 'PATCH', body: JSON.stringify({ status: 'submitted' }),
                  });
                  if (r.ok) {
                    await window.apiFetch(`/api/orders/${order.id}/activities`, {
                      method: 'POST', body: JSON.stringify({ action: 'resubmitted' }),
                    }).catch(() => {});
                    showToast('Resubmit สำเร็จ — รออนุมัติอีกครั้ง', { variant: 'success' });
                    const d = await window.apiFetch(`/api/orders/${order.id}`).then(r => r.ok ? r.json() : null);
                    if (d) { setOrder(d); setDocs(d.documents || []); }
                  } else {
                    const d = await r.json();
                    showToast(d.error || 'เกิดข้อผิดพลาด', { variant: 'error' });
                  }
                }}>
                Resubmit
              </Button>
            )}
          </div>
        </div>
      </div>

      {/* Submitted (pending approval) banner — owner can still edit */}
      {currentStatus.id === 'submitted' && (
        <div style={{
          background: '#eff6ff', border: '1px solid #bfdbfe', borderLeft: '3px solid #3b82f6',
          borderRadius: 3, padding: '10px 14px', marginBottom: 16,
          display: 'flex', alignItems: 'center', gap: 10, fontSize: 12.5,
        }}>
          <Icon name="alert" size={14} color="#3b82f6"/>
          <div style={{ flex: 1 }}>
            <span style={{ fontWeight: 500, color: '#1d4ed8' }}>Order กำลังรออนุมัติ</span>
            <span style={{ color: 'var(--ink-2)', marginLeft: 8 }}>
              คุณยังสามารถแก้ไขข้อมูลและส่งข้อความถึง Approver ได้ — กด Edit เพื่อแก้ไขและ Resubmit
            </span>
          </div>
          <Button variant="ghost" size="sm" icon="edit" onClick={() => setShowEditModal(true)}>
            Edit & Resubmit
          </Button>
        </div>
      )}

      {/* Sent back banner */}
      {currentStatus.id === 'sent_back' && (
        <div style={{
          background: '#f5f0ff', border: '1px solid #c4b5fd', borderLeft: '3px solid #8b5cf6',
          borderRadius: 3, padding: '10px 14px', marginBottom: 16,
          display: 'flex', alignItems: 'center', gap: 10, fontSize: 12.5,
        }}>
          <Icon name="alert" size={14} color="#8b5cf6"/>
          <div style={{ flex: 1 }}>
            <span style={{ fontWeight: 500, color: '#6d28d9' }}>Approver ส่งคืน — ต้องการข้อมูลเพิ่มเติม</span>
            <span style={{ color: 'var(--ink-2)', marginLeft: 8 }}>
              กรุณาเพิ่มเอกสารหรือหมายเหตุ แล้วกด Resubmit เพื่อส่งกลับไปอนุมัติอีกครั้ง
            </span>
          </div>
        </div>
      )}

      {/* Approval banner — dynamic from workflow */}
      {currentStatus.id === 'pending_apv' && (() => {
        const wf    = window.DEFAULT_WORKFLOWS || {};
        const users = window.USERS || [];
        const conds = window.CONDITIONS_DATA || [];

        // Build merged stages (same logic as SLATimeline)
        const seenApprovers = new Map();
        (order.items || []).forEach(it => {
          (wf[it.productId] || []).forEach((s, i) => {
            const key = s.approver;
            if (!seenApprovers.has(key) || s.slaH > seenApprovers.get(key).slaH) {
              seenApprovers.set(key, { ...s, stageOrder: i });
            }
          });
        });
        const mergedStages = [...seenApprovers.values()].sort((a, b) => a.stageOrder - b.stageOrder);

        const activeStage   = mergedStages[order.approvalStage ?? 0];
        const approverUser  = users.find(u => u.id === activeStage?.approver);
        const approverName  = approverUser?.name || 'Approver';
        const approverRole  = approverUser?.role || '';
        const condData      = conds.find(c => c.id === activeStage?.condition);
        const condLabel     = condData?.labelTh || condData?.label || null;

        return (
          <div style={{
            background: '#fef3e8', border: '1px solid #f4c684', borderLeft: '3px solid #d97b2e',
            borderRadius: 3, padding: '10px 14px', marginBottom: 16,
            display: 'flex', alignItems: 'center', gap: 10, fontSize: 12.5,
          }}>
            <Icon name="alert" size={14} color="#d97b2e"/>
            <div style={{ flex: 1 }}>
              <span style={{ fontWeight: 500 }}>
                รออนุมัติจาก {approverName}
                {approverRole && <span style={{ fontWeight: 400, color: 'var(--ink-2)', marginLeft: 6 }}>· {approverRole}</span>}
              </span>
              <span style={{ color: 'var(--ink-2)', marginLeft: 8 }}>
                {condLabel && <span style={{ marginRight: 6, padding: '1px 6px', background: '#fde68a', color: '#92400e', borderRadius: 2, fontSize: 11, fontWeight: 500 }}>{condLabel}</span>}
                MRR {fmtBaht(order.monthly)} · ส่งคำขอเมื่อ {fmtDate(order.createdAt)}
              </span>
            </div>
          </div>
        );
      })()}

      {/* Edit modal */}
      {showEditModal && (
        <EditOrderModal
          order={order}
          statusId={currentStatus.id}
          onClose={() => setShowEditModal(false)}
          onSaved={(fresh) => { setOrder(fresh); setDocs(fresh.documents || []); }}
        />
      )}

      {showUploadDocModal && (
        <UploadDocModal
          order={order}
          existingDocs={docs}
          onClose={() => setShowUploadDocModal(false)}
          onSaved={(freshDocs) => { setDocs(freshDocs); setShowUploadDocModal(false); }}
        />
      )}

      {missingDocs && (
        <MissingDocsModal
          missingDocs={missingDocs}
          onClose={() => setMissingDocs(null)}
          onSubmitAnyway={() => { setMissingDocs(null); doSubmitDraft(); }}
        />
      )}

      {showDeleteModal && (
        <DeleteDraftModal
          order={order}
          ownerName={ownerName}
          onClose={() => setShowDeleteModal(false)}
          onDeleted={() => {
            if (window.ORDERS) window.ORDERS = window.ORDERS.filter(o => o.id !== order.id);
            showToast(`ลบ Draft ${order.id} แล้ว`, { variant: 'success' });
            onBack && onBack();
          }}
        />
      )}

      {/* Three-column layout */}
      <div style={{ display: 'grid', gridTemplateColumns: '2fr 1fr', gap: 16 }}>
        {/* LEFT */}
        <div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
          {/* SLA Timeline */}
          <SLATimeline order={order} currentStatus={currentStatus} maxSla={effectiveMaxSla} viz={tweaks.slaViz}/>

          {/* Products in order */}
          <Section title="Products & packages" subtitle={`${products.length} product${products.length > 1 ? 's' : ''} · ${fmtInt(totalUnits)} total units`}>
            <div style={{ display: 'flex', flexDirection: 'column' }}>
              {products.map(({ item, product, pkg }, i) => (
                <div key={i} style={{
                  padding: '14px 16px',
                  borderBottom: i === products.length - 1 ? 'none' : '1px solid var(--line-2)',
                  display: 'grid', gridTemplateColumns: '40px 1fr auto', gap: 14, alignItems: 'center',
                }}>
                  <ProductGlyph productId={item.productId} size={36}/>
                  <div style={{ minWidth: 0 }}>
                    <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
                      <span style={{ fontWeight: 500, fontSize: 13.5 }}>{product?.name || item.productId}</span>
                      <span style={{ fontSize: 10.5, color: 'var(--ink-3)', textTransform: 'uppercase', letterSpacing: '0.06em' }}>·</span>
                      <span style={{ fontSize: 12, color: 'var(--ink-2)' }}>{pkg?.name || item.packageId}</span>
                    </div>
                    <div style={{ fontSize: 11, color: 'var(--ink-3)', marginTop: 2 }}>
                      <span className="num">{item.qty}</span> {product?.unit || 'unit'}{item.qty > 1 ? 's' : ''}
                      <span style={{ margin: '0 6px' }}>·</span>
                      <span className="num">{fmtBaht(pkg?.price || item.unitPrice || 0)}</span> / {product?.unit || 'unit'} / mo
                      <span style={{ margin: '0 6px' }}>·</span>
                      SLA <span className="num">{itemSla(item)}</span> วันทำการ
                    </div>
                  </div>
                  <div style={{ textAlign: 'right' }}>
                    <div className="num" style={{ fontSize: 14, fontWeight: 500 }}>{fmtBaht(pkg.price * item.qty)}</div>
                    <div style={{ fontSize: 10.5, color: 'var(--ink-3)' }}>per month</div>
                  </div>
                </div>
              ))}
              <div style={{
                padding: '12px 16px', background: 'var(--bg-2)',
                display: 'flex', justifyContent: 'space-between', alignItems: 'baseline', fontWeight: 500,
              }}>
                <div>
                  <div style={{ fontSize: 11, color: 'var(--ink-3)' }}>Monthly Recurring Revenue · {order.contractMonths} mo contract</div>
                  <div style={{ fontSize: 11, color: 'var(--ink-3)' }}>Total contract value: <span className="num" style={{ color: 'var(--ink)' }}>{fmtBaht(order.monthly * order.contractMonths)}</span></div>
                </div>
                <div className="num" style={{ fontSize: 20, letterSpacing: '-0.02em' }}>{fmtBaht(order.monthly)}<span style={{ fontSize: 11, color: 'var(--ink-3)', marginLeft: 4 }}>/mo</span></div>
              </div>
            </div>
          </Section>

          {/* Activity feed */}
          <Section title="Activity" subtitle="ประวัติการเปลี่ยนแปลง">
            <ActivityFeed order={order} currentStatus={currentStatus}/>
          </Section>
        </div>

        {/* RIGHT */}
        <div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
          {/* Company card */}
          <Section title="Company" subtitle="ข้อมูลบริษัท">
            <div style={{ padding: '14px 16px' }}>
              <div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 10 }}>
                <Avatar name={order.company?.name || '?'} size={36} square/>
                <div>
                  <div style={{ fontWeight: 500, fontSize: 13.5 }}>{order.company?.name || '—'}</div>
                  <div style={{ fontSize: 11, color: 'var(--ink-3)' }}>{order.company?.sector || '—'}</div>
                </div>
              </div>
              <DefRow label="Tax ID" value={<span className="num">{order.company?.taxId || '—'}</span>}/>
              <DefRow label="Size" value={order.company?.size ? `${order.company.size} employees` : '—'}/>
              <DefRow label="Province" value={order.company?.province || '—'}/>
            </div>
          </Section>

          {/* Contacts card — all contacts for this company */}
          {(() => {
            const contacts = allContacts ?? (order.contact ? [{ ...order.contact, isPrimary: true }] : []);
            return (
              <Section
                title="Contact"
                subtitle={contacts.length > 0 ? `${contacts.length} ผู้ติดต่อ` : 'ผู้ติดต่อ'}
              >
                <div style={{ padding: '6px 0' }}>
                  {contacts.length === 0 && (
                    <div style={{ padding: '12px 16px', fontSize: 12, color: 'var(--ink-4)', textAlign: 'center' }}>
                      ยังไม่มีข้อมูลผู้ติดต่อ
                    </div>
                  )}
                  {contacts.map((ct, i) => (
                    <div key={ct.id || i} style={{
                      padding: '12px 16px',
                      borderBottom: i < contacts.length - 1 ? '1px solid var(--line-2)' : 'none',
                    }}>
                      <div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 8 }}>
                        <Avatar name={ct.name || '?'} size={32}/>
                        <div style={{ flex: 1, minWidth: 0 }}>
                          <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
                            <span style={{ fontWeight: 500, fontSize: 13 }}>{ct.name || '—'}</span>
                            {ct.isPrimary && (
                              <span style={{
                                fontSize: 9.5, padding: '1px 6px',
                                background: 'var(--bg-2)', border: '1px solid var(--line)',
                                borderRadius: 2, color: 'var(--ink-3)', fontWeight: 500,
                                letterSpacing: '0.04em',
                              }}>PRIMARY</span>
                            )}
                          </div>
                          <div style={{ fontSize: 11, color: 'var(--ink-3)' }}>{ct.role || '—'}</div>
                        </div>
                      </div>
                      <div style={{ paddingLeft: 42, display: 'flex', flexDirection: 'column', gap: 2 }}>
                        {ct.email  && <ContactRow icon="mail"  value={ct.email}/>}
                        {ct.phone  && <ContactRow icon="phone" value={ct.phone}/>}
                        {ct.mobile && <ContactRow icon="phone" value={`${ct.mobile} (mobile)`}/>}
                      </div>
                    </div>
                  ))}
                </div>
              </Section>
            );
          })()}

          {/* Documents */}
          <Section title="Documents" subtitle={`${docs.length} ไฟล์แนบ`}>
            <div style={{ padding: '6px 0' }}>
              <input ref={uploadInputRef} type="file" multiple accept=".pdf,.jpg,.jpeg,.png,.xlsx,.docx"
                style={{ display: 'none' }} onChange={handleUploadFiles}/>
              {docs.length === 0 && (
                <div style={{ padding: '12px 16px', fontSize: 12, color: 'var(--ink-4)', textAlign: 'center' }}>
                  ยังไม่มีเอกสารแนบ
                </div>
              )}
              {docs.map((doc, i) => {
                const filename   = typeof doc === 'string' ? doc : doc.filename;
                const size       = typeof doc === 'object' ? doc.size : null;
                const uploadedAt = typeof doc === 'object' ? doc.uploadedAt : order.createdAt;
                const docId      = typeof doc === 'object' ? doc.id : null;
                const hasFile    = typeof doc === 'object' ? !!doc.file_path : false;
                return (
                  <div key={i} style={{
                    padding: '10px 16px', display: 'flex', alignItems: 'center', gap: 10,
                    borderBottom: i === docs.length - 1 ? 'none' : '1px solid var(--line-2)',
                    transition: 'background 120ms',
                  }} onMouseEnter={e => e.currentTarget.style.background = 'var(--bg-hover)'}
                     onMouseLeave={e => e.currentTarget.style.background = 'transparent'}>
                    <Icon name="fileCheck" size={14} color="var(--positive)"/>
                    <div style={{ flex: 1, minWidth: 0 }}>
                      <div style={{ fontSize: 12, fontWeight: 500, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{filename}</div>
                      <div style={{ fontSize: 10.5, color: 'var(--ink-3)' }}>
                        {size && <span className="num">{size} · </span>}
                        uploaded {relTime(uploadedAt || order.createdAt)}
                      </div>
                    </div>
                    {docId && (
                      <button
                        onClick={() => setViewingDoc({ id: docId, filename, orderId: order.id, hasFile })}
                        title="ดูเอกสาร"
                        style={{
                          background: 'none', border: 'none', cursor: 'pointer',
                          color: 'var(--ink-3)', padding: '2px 4px',
                          display: 'flex', alignItems: 'center', gap: 4,
                          fontSize: 11, borderRadius: 3,
                        }}
                        onMouseEnter={e => { e.currentTarget.style.background = 'var(--bg-3)'; e.currentTarget.style.color = 'var(--ink)'; }}
                        onMouseLeave={e => { e.currentTarget.style.background = 'none'; e.currentTarget.style.color = 'var(--ink-3)'; }}
                      >
                        <Icon name="eye" size={13} color="currentColor"/>
                      </button>
                    )}
                  </div>
                );
              })}
              <div style={{ padding: 10, borderTop: docs.length > 0 ? '1px solid var(--line-2)' : 'none' }}>
                <Button variant="ghost" size="sm" icon="upload" onClick={() => setShowUploadDocModal(true)}
                  style={{ width: '100%', justifyContent: 'center' }}>
                  Upload document
                </Button>
              </div>
            </div>
          </Section>
        </div>
      </div>

      {/* File viewer overlay */}
      {viewingDoc && (
        <div style={{
          position: 'fixed', inset: 0, background: 'rgba(0,0,0,0.7)', zIndex: 1200,
          display: 'flex', flexDirection: 'column', padding: 24,
        }} onClick={e => { if (e.target === e.currentTarget) setViewingDoc(null); }}>
          <div style={{
            background: 'var(--panel)', borderRadius: 6, flex: 1,
            display: 'flex', flexDirection: 'column', overflow: 'hidden',
            border: '1px solid var(--line)', boxShadow: '0 8px 40px rgba(0,0,0,0.3)',
          }}>
            <div style={{
              padding: '12px 18px', borderBottom: '1px solid var(--line)',
              display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 12,
            }}>
              <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
                <Icon name="file" size={14} color="var(--ink-3)"/>
                <span style={{ fontSize: 13, fontWeight: 500, color: 'var(--ink)' }}>{viewingDoc.filename}</span>
              </div>
              <button onClick={() => setViewingDoc(null)} style={{
                background: 'none', border: 'none', cursor: 'pointer', color: 'var(--ink-3)',
                padding: 4, display: 'flex', alignItems: 'center', gap: 5, fontSize: 12,
              }}>
                <Icon name="close" size={14}/>
                ปิด
              </button>
            </div>
            {viewingDoc.hasFile ? (
              <iframe
                src={viewingDocBlobUrl || ''}
                style={{ flex: 1, border: 'none', width: '100%' }}
                title={viewingDoc.filename}
                
              />
            ) : (
              <div style={{ flex: 1, display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', gap: 12, color: 'var(--ink-3)' }}>
                <Icon name="file" size={40} color="var(--line-3)"/>
                <div style={{ fontSize: 14, fontWeight: 500, color: 'var(--ink-2)' }}>{viewingDoc.filename}</div>
                <div style={{ fontSize: 12.5, color: 'var(--ink-4)' }}>ไม่มีไฟล์จริงสำหรับเอกสารนี้</div>
              </div>
            )}
          </div>
        </div>
      )}
    </>
  );
};

// ---------- SLA Timeline ----------
const SLATimeline = ({ order, currentStatus, viz }) => {
  const users  = window.USERS || [];
  const wf     = window.DEFAULT_WORKFLOWS || {};
  const mrr    = order.monthly || 0;
  const months = order.contractMonths || 0;

  // Evaluate whether a workflow condition applies to this order
  const condApplies = (cond) => {
    if (!cond || cond === 'always') return true;

    // Dynamic evaluation from CONDITIONS_DATA (DB-driven conditions)
    const condData = (window.CONDITIONS_DATA || []).find(c => c.id === cond);
    if (condData) {
      const { type, operator, value, value2 } = condData;
      const subject = type === 'mrr' ? mrr
                    : type === 'contract_months' ? months
                    : null;
      if (subject === null) return true;
      if (operator === '>'  || operator === 'gt')  return subject > value;
      if (operator === '>=' || operator === 'gte') return subject >= value;
      if (operator === '<'  || operator === 'lt')  return subject < value;
      if (operator === '<=' || operator === 'lte') return subject <= value;
      if (operator === '='  || operator === 'eq')  return subject === value;
      if (operator === 'between') return subject >= value && subject <= (value2 ?? value);
      return true;
    }

    // Fallback: hardcoded system condition IDs
    if (cond === 'mrr_gt_50k')       return mrr > 50000;
    if (cond === 'mrr_gt_200k')      return mrr > 200000;
    if (cond === 'mrr_gt_500k')      return mrr > 500000;
    if (cond === 'contract_gt_24mo') return months >= 24;
    return true;
  };

  // For multi-product orders: merge ALL stages across products (no condition filter),
  // dedup by approver keeping highest slaH, then sort by original stage_order
  const seenApprovers = new Map();
  (order.items || []).forEach(it => {
    (wf[it.productId] || []).forEach((s, i) => {
      const key = s.approver;
      if (!seenApprovers.has(key) || s.slaH > seenApprovers.get(key).slaH) {
        seenApprovers.set(key, { ...s, stageOrder: i });
      }
    });
  });
  const mergedStages = [...seenApprovers.values()].sort((a, b) => a.stageOrder - b.stageOrder);

  const allSteps = mergedStages.map((s, i) => {
    const u = users.find(u => u.id === s.approver);
    return {
      id: `apv_${i}`,
      type: 'approval',
      label: u?.name || s.approver,
      th: `อนุมัติระดับที่ ${i + 1}`,
      owner: u?.role || 'Approver',
      slaH: s.slaH || 24,
      days: Math.ceil((s.slaH || 24) / 8),
      condition: s.condition,
      condMet: condApplies(s.condition),
    };
  });

  const totalDays = allSteps.reduce((sum, s) => sum + s.days, 0);

  // Map order status + approvalStage → active step index
  const stage = currentStatus?.stage ?? -1;
  const approvalStage = order.approvalStage ?? 0; // which step is currently active (0-based)
  let currentStepIdx;
  if (currentStatus?.id === 'rejected') {
    currentStepIdx = null;
  } else if (stage <= 0) {
    currentStepIdx = -1; // draft: nothing started yet
  } else if (stage === 1 || stage === 2) {
    // submitted / pending_apv → use approvalStage to know which step is active
    currentStepIdx = approvalStage;
  } else {
    currentStepIdx = allSteps.length; // approved / provisioning / active → all done
  }

  // Calculate per-step dates from order.createdAt
  let cursor = new Date(order.createdAt);
  const stepsWithDates = allSteps.map((s, i) => {
    const startD = new Date(cursor);
    // skipped steps don't consume timeline days
    if (s.condMet !== false) cursor.setDate(cursor.getDate() + s.days);
    const endD = new Date(cursor);
    let state;
    if (currentStatus?.id === 'rejected') {
      state = i === 0 ? 'rejected' : 'cancelled';
    } else if (currentStepIdx === -1) {
      state = s.condMet === false ? 'skipped' : 'pending';
    } else if (currentStepIdx >= allSteps.length) {
      // approved/active: condMet=false → skipped, condMet=true → done
      state = s.condMet === false ? 'skipped' : 'done';
    } else if (i < currentStepIdx) {
      state = s.condMet === false ? 'skipped' : 'done';
    } else if (i === currentStepIdx) {
      // Only set active if condition is met, otherwise skip forward
      state = s.condMet === false ? 'skipped' : 'active';
    } else {
      state = s.condMet === false ? 'skipped' : 'pending';
    }
    return { ...s, startD, endD, state };
  });

  const handoverDate = stepsWithDates.length > 0
    ? stepsWithDates[stepsWithDates.length - 1].endD
    : new Date(order.createdAt);

  return (
    <Section
      title="SLA & Approval timeline"
      subtitle={
        <span>
          ระยะเวลาทั้งหมดประมาณ{' '}
          <span className="num" style={{ color: 'var(--ink-2)', fontWeight: 500 }}>{totalDays}</span>{' '}
          วันทำการ · ส่งมอบประมาณ{' '}
          <span className="num" style={{ color: 'var(--ink-2)', fontWeight: 500 }}>{fmtDate(handoverDate)}</span>
        </span>
      }
    >
      <div style={{ padding: '16px 18px' }}>
        {viz === 'gantt'
          ? <GanttView steps={stepsWithDates} totalDays={totalDays}/>
          : <StepperView steps={stepsWithDates}/>}
      </div>
    </Section>
  );
};

const StepperView = ({ steps }) => (
  <div style={{ display: 'flex', flexDirection: 'column' }}>
    {steps.map((s, i) => {
      const c = s.state === 'done'      ? 'var(--positive)'  :
                s.state === 'active'    ? 'var(--accent-2)'  :
                s.state === 'rejected'  ? 'var(--negative)'  :
                s.state === 'cancelled' ? 'var(--ink-4)'     :
                s.state === 'skipped'   ? 'var(--line-3)'    : 'var(--ink-4)';
      const dimmed = s.state === 'pending' || s.state === 'cancelled' || s.state === 'skipped';
      return (
        <div key={s.id || i} style={{ display: 'grid', gridTemplateColumns: '24px 1fr auto', gap: 12, alignItems: 'flex-start' }}>
          {/* Node + connector */}
          <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', minHeight: 56 }}>
            <div style={{
              width: 18, height: 18, borderRadius: '50%',
              background: s.state === 'done' || s.state === 'active' ? c : 'var(--panel)',
              border: `1.5px solid ${dimmed ? 'var(--line-3)' : c}`,
              display: 'grid', placeItems: 'center',
              color: '#fff', marginTop: 2, flexShrink: 0,
            }}>
              {s.state === 'done'     && <Icon name="check" size={10} color="#fff"/>}
              {s.state === 'active'   && <span style={{ width: 6, height: 6, borderRadius: '50%', background: '#fff' }}/>}
              {s.state === 'rejected' && <Icon name="close" size={9} color="#fff"/>}
            </div>
            {i < steps.length - 1 && (
              <div style={{
                width: 2, flex: 1, background: s.state === 'done' ? c : 'var(--line-2)',
                marginTop: 2, marginBottom: 2, minHeight: 14,
              }}/>
            )}
          </div>

          {/* Content */}
          <div style={{ paddingBottom: 16 }}>
            <div style={{ display: 'flex', gap: 8, alignItems: 'center', flexWrap: 'wrap' }}>
              <span style={{ fontSize: 13, fontWeight: 500, color: dimmed ? 'var(--ink-3)' : 'var(--ink)' }}>
                {s.label}
              </span>
              <span style={{ fontSize: 11, color: 'var(--ink-4)' }}>{s.th}</span>
              {s.type === 'approval' && (
                <span style={{
                  fontSize: 9.5, padding: '1px 6px',
                  background: dimmed ? 'var(--bg-2)' : c + '18',
                  color: dimmed ? 'var(--ink-4)' : c,
                  borderRadius: 2, fontWeight: 500, letterSpacing: '0.04em',
                }}>
                  APPROVAL
                </span>
              )}
            </div>
            <div style={{ fontSize: 11, color: 'var(--ink-3)', marginTop: 3, display: 'flex', alignItems: 'center', gap: 4, flexWrap: 'wrap' }}>
              <span>{s.owner}</span>
              <span style={{ color: 'var(--line-3)' }}>·</span>
              {s.type === 'approval'
                ? <><span className="num">{s.slaH}h</span><span> SLA</span></>
                : <><span className="num">{s.days}</span><span> วันทำการ</span></>
              }
              {s.state === 'active' && (
                <span style={{ marginLeft: 4, color: 'var(--accent-2)', fontWeight: 500 }}>กำลังดำเนินการ</span>
              )}
              {s.state === 'skipped' && (
                <span style={{ marginLeft: 4, fontSize: 10, padding: '1px 5px', background: 'var(--bg-2)', border: '1px solid var(--line-2)', borderRadius: 2, color: 'var(--ink-4)', fontWeight: 500 }}>
                  ข้ามขั้นตอน
                </span>
              )}
              {s.condition && s.condition !== 'always' && (() => {
                const condData = (window.CONDITIONS_DATA || []).find(c => c.id === s.condition);
                const condLabel = condData?.labelTh || condData?.label || s.condition;
                return (
                  <span style={{
                    marginLeft: 4, fontSize: 9.5, padding: '1px 5px', borderRadius: 2,
                    background: s.condMet ? '#e3f1ea' : 'var(--bg-2)',
                    color: s.condMet ? 'var(--positive)' : 'var(--ink-4)',
                    fontWeight: 500,
                  }}>
                    {condLabel}
                  </span>
                );
              })()}
            </div>
          </div>

          {/* Dates */}
          <div style={{ textAlign: 'right', paddingTop: 2 }}>
            <div className="num" style={{ fontSize: 11.5, color: dimmed ? 'var(--ink-4)' : 'var(--ink-2)', fontWeight: s.state === 'active' ? 500 : 400 }}>
              {fmtDateShort(s.startD)} → {fmtDateShort(s.endD)}
            </div>
          </div>
        </div>
      );
    })}
  </div>
);

const GanttView = ({ steps, totalDays }) => {
  const today = new Date();
  const start = steps[0].startD;
  const end = steps[steps.length - 1].endD;
  const totalMs = end - start || 1;
  const todayPct = Math.max(0, Math.min(100, (today - start) / totalMs * 100));
  const showTodayLine = today >= start && today <= end;

  return (
    <div>
      {/* Date scale */}
      <div style={{ display: 'flex', justifyContent: 'space-between', fontSize: 10.5, color: 'var(--ink-3)', marginBottom: 8, paddingLeft: 160, paddingRight: 8 }}>
        <span className="num">{fmtDateShort(start)}</span>
        <span className="num">{fmtDateShort(end)}</span>
      </div>

      <div style={{ display: 'flex', flexDirection: 'column', gap: 4, position: 'relative' }}>
        {/* Today line */}
        {showTodayLine && (
          <div style={{
            position: 'absolute', left: `calc(160px + ${todayPct}% * (100% - 168px) / 100%)`,
            top: -4, bottom: -4, width: 1, background: 'var(--accent-2)', zIndex: 2,
          }}>
            <div style={{
              position: 'absolute', top: -16, left: -22, fontSize: 9.5, color: 'var(--accent-2)',
              fontWeight: 500, fontFamily: 'IBM Plex Mono', whiteSpace: 'nowrap',
            }}>TODAY</div>
          </div>
        )}
        {steps.map((s, i) => {
          const offsetPct = (s.startD - start) / totalMs * 100;
          const widthPct = (s.endD - s.startD) / totalMs * 100;
          const c = s.state === 'done' ? 'var(--positive)' :
                    s.state === 'active' ? 'var(--accent-2)' :
                    s.state === 'rejected' ? 'var(--negative)' :
                    'var(--ink-4)';
          const bg = s.state === 'done' ? '#e3f1ea' :
                     s.state === 'active' ? '#fef0e0' :
                     'var(--bg-2)';
          return (
            <div key={s.id} style={{ display: 'grid', gridTemplateColumns: '160px 1fr', gap: 8, alignItems: 'center', minHeight: 28 }}>
              <div style={{ fontSize: 12, color: 'var(--ink-2)', fontWeight: s.state === 'active' ? 500 : 400, paddingRight: 4 }}>
                <div>{s.label}</div>
                <div style={{ fontSize: 10, color: 'var(--ink-3)' }}>{s.owner}</div>
              </div>
              <div style={{ position: 'relative', height: 22, background: 'var(--bg-2)', borderRadius: 2 }}>
                <div style={{
                  position: 'absolute', top: 2, bottom: 2,
                  left: `${offsetPct}%`, width: `${widthPct}%`,
                  background: bg,
                  borderLeft: `3px solid ${c}`,
                  borderRadius: 2,
                  display: 'flex', alignItems: 'center', justifyContent: 'space-between',
                  padding: '0 6px',
                  fontSize: 10, color: c, fontFamily: 'IBM Plex Mono', fontWeight: 500,
                }}>
                  <span>{s.days}d</span>
                  {s.state === 'done' && <Icon name="check" size={10} color={c}/>}
                  {s.state === 'active' && <span style={{ width: 5, height: 5, borderRadius: '50%', background: c }}/>}
                </div>
              </div>
            </div>
          );
        })}
      </div>
    </div>
  );
};

// ---------- Activity feed ----------
const ACTIVITY_ICON_COLOR = {
  created:      { icon: 'plus',       color: 'var(--ink-2)' },
  submitted:    { icon: 'arrowRight', color: 'var(--accent-2)' },
  approved:     { icon: 'check',      color: 'var(--positive)' },
  rejected:     { icon: 'close',      color: 'var(--negative)' },
  sent_back:    { icon: 'alert',      color: '#8b5cf6' },
  resubmitted:  { icon: 'arrowRight', color: 'var(--accent-2)' },
  edited:       { icon: 'edit',       color: '#d97b2e' },
  provisioning: { icon: 'arrowRight', color: '#2563eb' },
  active:       { icon: 'check',      color: '#16a34a' },
};

const ActivityFeed = ({ order }) => {
  const [activities, setActivities] = React.useState(null);

  React.useEffect(() => {
    window.apiFetch(`/api/orders/${order.id}/activities`)
      .then(r => r.ok ? r.json() : [])
      .then(rows => {
        // Always prepend the "created" event from order.createdAt if not in log
        const hasCreated = rows.some(r => r.action === 'created');
        const ownerUser = (window.USERS || []).find(u => u.id === order.owner);
        const ownerName = ownerUser?.name || order.owner || '—';
        const base = hasCreated ? rows : [
          {
            id: 'created',
            action: 'created',
            label: 'สร้างคำสั่งซื้อ',
            icon: 'plus',
            note: null,
            userName: ownerName,
            createdAt: order.createdAt,
          },
          ...rows,
        ];
        setActivities(base);
      })
      .catch(() => setActivities([]));
  }, [order.id]);

  if (activities === null) {
    return <div style={{ padding: '16px 18px', fontSize: 12, color: 'var(--ink-4)' }}>กำลังโหลด…</div>;
  }

  const sorted = [...activities].sort((a, b) => new Date(b.createdAt) - new Date(a.createdAt));

  return (
    <div style={{ padding: '12px 18px 16px' }}>
      {sorted.length === 0 && (
        <div style={{ fontSize: 12, color: 'var(--ink-4)', textAlign: 'center', padding: '12px 0' }}>
          ยังไม่มี activity
        </div>
      )}
      {sorted.map((ev, i) => {
        const cfg = ACTIVITY_ICON_COLOR[ev.action] || { icon: 'dot', color: 'var(--ink-3)' };
        const date = new Date(ev.createdAt);
        return (
          <div key={ev.id || i} style={{ display: 'flex', gap: 12, padding: '6px 0',
            borderBottom: i < sorted.length - 1 ? '1px solid var(--line-2)' : 'none' }}>
            <div style={{
              width: 24, height: 24, borderRadius: '50%',
              background: cfg.color + '15', border: `1px solid ${cfg.color}40`,
              display: 'grid', placeItems: 'center', flexShrink: 0,
            }}>
              <Icon name={cfg.icon} size={11} color={cfg.color}/>
            </div>
            <div style={{ flex: 1, minWidth: 0 }}>
              <div style={{ fontSize: 12.5, color: 'var(--ink)', fontWeight: 500 }}>{ev.label}</div>
              {ev.note && (
                <div style={{ fontSize: 11.5, color: 'var(--ink-2)', marginTop: 2, fontStyle: 'italic' }}>
                  "{ev.note}"
                </div>
              )}
              <div style={{ fontSize: 10.5, color: 'var(--ink-3)', marginTop: 2 }}>
                {ev.userName}
                {ev.userRole && <span style={{ color: 'var(--ink-4)', marginLeft: 4 }}>· {ev.userRole}</span>}
                <span style={{ margin: '0 6px', color: 'var(--line-3)' }}>·</span>
                <span className="num">{date.toLocaleDateString('en-GB', { day: '2-digit', month: 'short', hour: '2-digit', minute: '2-digit' })}</span>
              </div>
            </div>
          </div>
        );
      })}
    </div>
  );
};

// ---------- Helper bits ----------
const Section = ({ title, subtitle, children }) => (
  <div style={{ background: 'var(--panel)', border: '1px solid var(--line)', borderRadius: 4 }}>
    <div style={{ padding: '14px 18px 12px', borderBottom: '1px solid var(--line)' }}>
      <h3 style={{ fontSize: 14, fontWeight: 600, letterSpacing: '-0.01em', margin: 0 }}>{title}</h3>
      {subtitle && <div style={{ fontSize: 11.5, color: 'var(--ink-3)', marginTop: 3 }}>{subtitle}</div>}
    </div>
    {children}
  </div>
);

const DefRow = ({ label, value }) => (
  <div style={{ display: 'flex', justifyContent: 'space-between', padding: '5px 0', fontSize: 12 }}>
    <span style={{ color: 'var(--ink-3)' }}>{label}</span>
    <span style={{ color: 'var(--ink)', textAlign: 'right' }}>{value}</span>
  </div>
);

const ContactRow = ({ icon, value }) => (
  <div style={{ display: 'flex', alignItems: 'center', gap: 8, padding: '5px 0', fontSize: 12, color: 'var(--ink-2)' }}>
    <Icon name={icon} size={12} color="var(--ink-3)"/>
    <span style={{ wordBreak: 'break-all' }}>{value}</span>
  </div>
);

// ---------- Upload Document Modal ----------
const UploadDocModal = ({ order, existingDocs, onClose, onSaved }) => {
  const [typedFiles, setTypedFiles]   = React.useState({});   // docTypeId → fileInfo
  const [untypedFiles, setUntypedFiles] = React.useState([]); // generic untyped
  const [deletedIds, setDeletedIds]   = React.useState([]);
  const [localDocs, setLocalDocs]     = React.useState(existingDocs || []);
  const [saving, setSaving]           = React.useState(false);
  const otherInputRef = React.useRef(null);

  const docTypes = window.DOC_TYPES || DOC_TYPES || [];
  const docReqs  = window.DOC_REQUIREMENTS || DOC_REQUIREMENTS || {};

  // Compute required/optional from order's products
  const docLevels = {};
  for (const it of (order.items || [])) {
    const reqs = docReqs[it.productId] || {};
    for (const [docId, level] of Object.entries(reqs)) {
      if (level === 'none') continue;
      const cur = docLevels[docId];
      if (!cur || (level === 'required' && cur !== 'required')) docLevels[docId] = level;
    }
  }
  // Normalize: API returns either doc_type_id (raw DB) or docTypeId (camelCase from /api/init)
  const getDocTypeId = (d) => d.doc_type_id || d.docTypeId || null;
  const getExistingDoc = (docTypeId) => localDocs.find(d => getDocTypeId(d) === docTypeId && !deletedIds.includes(d.id)) || null;

  const requiredDocs = docTypes.filter(d => docLevels[d.id] === 'required');
  const optionalDocs = docTypes.filter(d => docLevels[d.id] === 'optional');
  const typedDocIds  = new Set([...requiredDocs, ...optionalDocs].map(d => d.id));
  const untypedExisting = localDocs.filter(d => { const tid = getDocTypeId(d); return !tid || !typedDocIds.has(tid); });

  const handleTypedUpload = (docTypeId) => (fileInfo) => {
    const existing = localDocs.find(d => getDocTypeId(d) === docTypeId);
    if (existing && !deletedIds.includes(existing.id)) {
      setDeletedIds(prev => [...prev, existing.id]);
      setLocalDocs(prev => prev.filter(d => d.id !== existing.id));
    }
    setTypedFiles(prev => ({ ...prev, [docTypeId]: fileInfo }));
  };
  const handleTypedRemove = (docTypeId) => () =>
    setTypedFiles(prev => { const n = { ...prev }; delete n[docTypeId]; return n; });

  const handleOtherPick = (e) => {
    const files = Array.from(e.target.files || []);
    e.target.value = '';
    setUntypedFiles(prev => [...prev, ...files.map(f => ({
      file: f, name: f.name,
      size: f.size < 1024*1024 ? `${Math.round(f.size/1024)} KB` : `${(f.size/1024/1024).toFixed(1)} MB`,
    }))]);
  };

  const handleSave = async () => {
    setSaving(true);
    try {
      // Delete removed existing docs
      for (const id of deletedIds) {
        await window.apiFetch(`/api/orders/${order.id}/documents/${id}`, { method: 'DELETE' }).catch(() => {});
      }
      // Upload typed files
      for (const [doc_type_id, f] of Object.entries(typedFiles)) {
        const fd = new FormData();
        fd.append('file', f.file);
        fd.append('doc_type_id', doc_type_id);
        await fetch(`/api/orders/${order.id}/documents/upload`, {
          method: 'POST',
          headers: { Authorization: `Bearer ${localStorage.getItem('sol_auth_token')}` },
          body: fd,
        });
      }
      // Upload generic files
      for (const f of untypedFiles) {
        const fd = new FormData();
        fd.append('file', f.file);
        await fetch(`/api/orders/${order.id}/documents/upload`, {
          method: 'POST',
          headers: { Authorization: `Bearer ${localStorage.getItem('sol_auth_token')}` },
          body: fd,
        });
      }
      // Refresh document list
      const fresh = await window.apiFetch(`/api/orders/${order.id}/documents`).then(r => r.ok ? r.json() : null);
      onSaved(fresh || []);
      showToast('บันทึกเอกสารเรียบร้อย', { variant: 'success' });
    } catch {
      showToast('เกิดข้อผิดพลาดขณะอัปโหลด', { variant: 'error' });
    } finally { setSaving(false); }
  };

  const hasChanges = Object.keys(typedFiles).length > 0 || untypedFiles.length > 0 || deletedIds.length > 0;

  return (
    <Modal open title="อัปโหลดเอกสาร" subtitle={order.id} onClose={onClose} width={560}
      footer={<>
        <Button variant="ghost" onClick={onClose}>Cancel</Button>
        <Button variant="primary" icon={saving ? 'clock' : 'check'} disabled={saving || !hasChanges} onClick={handleSave}>
          {saving ? 'กำลังบันทึก…' : 'บันทึก'}
        </Button>
      </>}>

      {requiredDocs.length === 0 && optionalDocs.length === 0 ? (
        // No doc requirements — generic upload only
        <div>
          <div style={{ fontSize: 12, color: 'var(--ink-3)', marginBottom: 12 }}>
            ไม่มีเอกสารที่กำหนดไว้สำหรับ product นี้ — สามารถแนบเอกสารทั่วไปได้
          </div>
          <div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
            {untypedExisting.map((d, i) => (
              <div key={d.id || i} style={{ display: 'flex', alignItems: 'center', gap: 10, padding: '8px 12px', background: 'var(--bg-2)', borderRadius: 3, border: '1px solid var(--line)' }}>
                <Icon name="fileCheck" size={13} color="var(--positive)"/>
                <div style={{ flex: 1, fontSize: 12, fontWeight: 500 }}>{d.filename}</div>
                <button onClick={() => { setDeletedIds(p => [...p, d.id]); setLocalDocs(p => p.filter(x => x.id !== d.id)); }} style={{ background: 'none', border: 'none', cursor: 'pointer', color: 'var(--ink-4)', padding: 4 }}>
                  <Icon name="close" size={11}/>
                </button>
              </div>
            ))}
            {untypedFiles.map((f, i) => (
              <div key={i} style={{ display: 'flex', alignItems: 'center', gap: 10, padding: '8px 12px', background: '#f0fdf4', borderRadius: 3, border: '1px solid var(--positive)' }}>
                <Icon name="upload" size={13} color="var(--positive)"/>
                <div style={{ flex: 1, fontSize: 12, fontWeight: 500 }}>{f.name} <span style={{ color: 'var(--ink-3)' }}>{f.size}</span></div>
                <button onClick={() => setUntypedFiles(p => p.filter(x => x.name !== f.name))} style={{ background: 'none', border: 'none', cursor: 'pointer', color: 'var(--ink-4)', padding: 4 }}>
                  <Icon name="close" size={11}/>
                </button>
              </div>
            ))}
            <Button variant="secondary" size="sm" icon="upload" onClick={() => otherInputRef.current?.click()}>เลือกไฟล์</Button>
            <input ref={otherInputRef} type="file" multiple accept=".pdf,.jpg,.jpeg,.png,.xlsx,.docx" style={{ display: 'none' }} onChange={handleOtherPick}/>
          </div>
        </div>
      ) : (
        <div style={{ display: 'flex', flexDirection: 'column', gap: 18 }}>
          {/* Required */}
          {requiredDocs.length > 0 && (
            <div>
              <div className="eyebrow" style={{ fontSize: 10, color: 'var(--ink-3)', marginBottom: 8, letterSpacing: '.07em' }}>เอกสารที่จำเป็น · Required</div>
              <div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
                {requiredDocs.map(d => {
                  const existing = getExistingDoc(d.id);
                  const queued   = typedFiles[d.id];
                  const fileForRow = queued || (existing ? { name: existing.filename, size: existing.size } : null);
                  const accept = d.formats ? d.formats.replace(/·/g,',').replace(/ /g,'').toLowerCase().split(',').map(x=>'.'+x.trim()).join(',') : undefined;
                  return (
                    <DocRow key={d.id} label={d.label} en={d.en} hint={d.desc || ''}
                      file={fileForRow}
                      onUpload={handleTypedUpload(d.id)}
                      onRemove={existing && !queued ? () => { setDeletedIds(p=>[...p,existing.id]); setLocalDocs(p=>p.filter(x=>x.id!==existing.id)); } : handleTypedRemove(d.id)}
                      accept={accept}/>
                  );
                })}
              </div>
            </div>
          )}

          {/* Optional */}
          {optionalDocs.length > 0 && (
            <div>
              <div className="eyebrow" style={{ fontSize: 10, color: 'var(--ink-3)', marginBottom: 8, letterSpacing: '.07em' }}>เอกสารเพิ่มเติม · Optional</div>
              <div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
                {optionalDocs.map(d => {
                  const existing = getExistingDoc(d.id);
                  const queued   = typedFiles[d.id];
                  const fileForRow = queued || (existing ? { name: existing.filename, size: existing.size } : null);
                  const accept = d.formats ? d.formats.replace(/·/g,',').replace(/ /g,'').toLowerCase().split(',').map(x=>'.'+x.trim()).join(',') : undefined;
                  return (
                    <DocRow key={d.id} label={d.label} en={d.en} hint={d.desc || ''}
                      file={fileForRow}
                      onUpload={handleTypedUpload(d.id)}
                      onRemove={existing && !queued ? () => { setDeletedIds(p=>[...p,existing.id]); setLocalDocs(p=>p.filter(x=>x.id!==existing.id)); } : handleTypedRemove(d.id)}
                      accept={accept}/>
                  );
                })}
              </div>
            </div>
          )}

          {/* Other (untyped) */}
          <div>
            <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 8 }}>
              <div className="eyebrow" style={{ fontSize: 10, color: 'var(--ink-3)', letterSpacing: '.07em' }}>เอกสารอื่นๆ · Other</div>
              <Button variant="ghost" size="sm" icon="upload" onClick={() => otherInputRef.current?.click()}>เพิ่มไฟล์</Button>
              <input ref={otherInputRef} type="file" multiple accept=".pdf,.jpg,.jpeg,.png,.xlsx,.docx" style={{ display: 'none' }} onChange={handleOtherPick}/>
            </div>
            <div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
              {untypedExisting.map((d, i) => (
                <div key={d.id || i} style={{ display: 'flex', alignItems: 'center', gap: 10, padding: '8px 12px', background: 'var(--bg-2)', borderRadius: 3, border: '1px solid var(--line)' }}>
                  <Icon name="fileCheck" size={13} color="var(--positive)"/>
                  <div style={{ flex: 1, minWidth: 0, fontSize: 12, fontWeight: 500, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{d.filename}</div>
                  <button onClick={() => { setDeletedIds(p => [...p, d.id]); setLocalDocs(p => p.filter(x => x.id !== d.id)); }} style={{ background: 'none', border: 'none', cursor: 'pointer', color: 'var(--ink-4)', padding: 4 }}>
                    <Icon name="close" size={11}/>
                  </button>
                </div>
              ))}
              {untypedFiles.map((f, i) => (
                <div key={i} style={{ display: 'flex', alignItems: 'center', gap: 10, padding: '8px 12px', background: '#f0fdf4', borderRadius: 3, border: '1px solid var(--positive)' }}>
                  <Icon name="upload" size={13} color="var(--positive)"/>
                  <div style={{ flex: 1, minWidth: 0 }}>
                    <div style={{ fontSize: 12, fontWeight: 500 }}>{f.name}</div>
                    <div style={{ fontSize: 10.5, color: 'var(--positive)' }}>{f.size} · จะ upload เมื่อกด บันทึก</div>
                  </div>
                  <button onClick={() => setUntypedFiles(p => p.filter(x => x.name !== f.name))} style={{ background: 'none', border: 'none', cursor: 'pointer', color: 'var(--ink-4)', padding: 4 }}>
                    <Icon name="close" size={11}/>
                  </button>
                </div>
              ))}
              {untypedExisting.length === 0 && untypedFiles.length === 0 && (
                <div style={{ padding: '10px 14px', background: 'var(--bg-2)', borderRadius: 3, fontSize: 12, color: 'var(--ink-4)', textAlign: 'center', border: '1px dashed var(--line-3)' }}>
                  ยังไม่มีเอกสารอื่นๆ
                </div>
              )}
            </div>
          </div>
        </div>
      )}
    </Modal>
  );
};

// ---------- Missing Docs Modal ----------
const MissingDocsModal = ({ missingDocs, onClose, onSubmitAnyway }) => (
  <Modal open title="เอกสาร Required ยังไม่ครบ" onClose={onClose} width={440}
    footer={<>
      <Button variant="ghost" onClick={onClose}>แก้ไขเอกสาร</Button>
      <Button variant="primary" icon="arrowRight" onClick={onSubmitAnyway}>Submit ต่อไปโดยไม่มีเอกสาร</Button>
    </>}>

    <div style={{ display: 'flex', gap: 10, padding: '10px 14px', background: '#fffbeb', border: '1px solid #fde68a', borderRadius: 4, marginBottom: 18 }}>
      <Icon name="alert" size={14} color="#d97706"/>
      <div style={{ fontSize: 12, color: '#92400e', lineHeight: 1.5 }}>
        Order นี้ยังขาดเอกสารที่<strong>จำเป็น (Required)</strong> — Approver อาจส่งกลับมาแก้ไขในภายหลัง
      </div>
    </div>

    <div style={{ fontSize: 12, color: 'var(--ink-3)', marginBottom: 10 }}>เอกสารที่ยังไม่ได้อัปโหลด:</div>
    <div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
      {missingDocs.map(d => (
        <div key={d.id} style={{
          display: 'flex', alignItems: 'center', gap: 10,
          padding: '9px 12px', border: '1px solid #fde68a',
          background: '#fffbeb', borderRadius: 3,
        }}>
          <Icon name="file" size={13} color="#d97706"/>
          <div>
            <div style={{ fontSize: 12.5, fontWeight: 500 }}>{d.label}</div>
            {d.en && <div style={{ fontSize: 11, color: 'var(--ink-3)' }}>{d.en}</div>}
            {d.desc && <div style={{ fontSize: 11, color: 'var(--ink-3)', marginTop: 1 }}>{d.desc}</div>}
          </div>
        </div>
      ))}
    </div>
  </Modal>
);

// ---------- Delete Draft Modal ----------
const DeleteDraftModal = ({ order, ownerName, onClose, onDeleted }) => {
  const [deleting, setDeleting] = React.useState(false);
  const prods = window.PRODUCTS || PRODUCTS || [];

  const lineItems = (order.items || []).map(it => {
    const product = prods.find(p => p.id === it.productId);
    const pkg     = product?.packages?.find(p => p.id === it.packageId);
    return { product, pkg, qty: it.qty, unitPrice: it.unitPrice || pkg?.price || 0 };
  });

  const handleDelete = async () => {
    setDeleting(true);
    try {
      const r = await window.apiFetch(`/api/orders/${order.id}`, { method: 'DELETE' });
      if (r.ok) {
        onDeleted();
      } else {
        const e = await r.json().catch(() => ({}));
        showToast(e.error || 'ไม่สามารถลบได้', { variant: 'error' });
        setDeleting(false);
      }
    } catch {
      showToast('เกิดข้อผิดพลาด', { variant: 'error' });
      setDeleting(false);
    }
  };

  return (
    <Modal open title="ยืนยันการลบ Draft" subtitle={`Order ${order.id}`} onClose={onClose} width={460}
      footer={<>
        <Button variant="ghost" onClick={onClose} disabled={deleting}>Cancel</Button>
        <Button variant="accent" icon={deleting ? 'clock' : 'trash'} onClick={handleDelete} disabled={deleting}
          style={{ background: 'var(--negative)', borderColor: 'var(--negative)' }}>
          {deleting ? 'กำลังลบ…' : 'ลบ Draft นี้'}
        </Button>
      </>}>

      {/* Warning banner */}
      <div style={{ display: 'flex', gap: 10, padding: '10px 14px', background: '#fef2f2', border: '1px solid #fecaca', borderRadius: 4, marginBottom: 18 }}>
        <Icon name="alert" size={14} color="var(--negative)"/>
        <div style={{ fontSize: 12, color: '#b91c1c', lineHeight: 1.5 }}>
          <strong>การลบไม่สามารถกู้คืนได้</strong> — ข้อมูล order รวมถึงสินค้า เอกสาร และ activity ทั้งหมดจะถูกลบถาวร
        </div>
      </div>

      {/* Order summary */}
      <div style={{ display: 'flex', flexDirection: 'column', gap: 0, border: '1px solid var(--line)', borderRadius: 4, overflow: 'hidden', fontSize: 12 }}>
        {/* Header row */}
        <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', padding: '10px 14px', background: 'var(--bg-2)', borderBottom: '1px solid var(--line)' }}>
          <span style={{ fontWeight: 600, fontSize: 13 }}>{order.id}</span>
          <span style={{ fontSize: 11, color: 'var(--ink-3)' }}>Draft · สร้างเมื่อ {fmtDate(order.createdAt)}</span>
        </div>

        {/* Company */}
        <div style={{ display: 'grid', gridTemplateColumns: '110px 1fr', padding: '9px 14px', borderBottom: '1px solid var(--line-2)', background: 'var(--panel)' }}>
          <span style={{ color: 'var(--ink-3)' }}>บริษัท</span>
          <span style={{ fontWeight: 500 }}>{order.company?.name || '—'}</span>
        </div>

        {/* Owner */}
        <div style={{ display: 'grid', gridTemplateColumns: '110px 1fr', padding: '9px 14px', borderBottom: '1px solid var(--line-2)', background: 'var(--bg-2)' }}>
          <span style={{ color: 'var(--ink-3)' }}>สร้างโดย</span>
          <span>{ownerName}</span>
        </div>

        {/* Products */}
        <div style={{ display: 'grid', gridTemplateColumns: '110px 1fr', padding: '9px 14px', borderBottom: lineItems.length > 0 ? '1px solid var(--line-2)' : 'none', background: 'var(--panel)' }}>
          <span style={{ color: 'var(--ink-3)', paddingTop: 1 }}>สินค้า</span>
          <div style={{ display: 'flex', flexDirection: 'column', gap: 3 }}>
            {lineItems.length > 0 ? lineItems.map(({ product, pkg, qty }, i) => (
              <div key={i} style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
                {product && <ProductGlyph productId={product.id} size={16}/>}
                <span style={{ fontWeight: 500 }}>{product?.name || '—'}</span>
                {pkg && <span style={{ color: 'var(--ink-3)' }}>· {pkg.name}</span>}
                {qty > 1 && <span style={{ color: 'var(--ink-4)', fontSize: 11 }}>×{qty}</span>}
              </div>
            )) : <span style={{ color: 'var(--ink-4)' }}>—</span>}
          </div>
        </div>

        {/* MRR + contract */}
        {order.monthly > 0 && (
          <div style={{ display: 'grid', gridTemplateColumns: '110px 1fr', padding: '9px 14px', background: 'var(--bg-2)' }}>
            <span style={{ color: 'var(--ink-3)' }}>MRR</span>
            <span className="num" style={{ fontWeight: 500 }}>
              {fmtBaht(order.monthly)} / เดือน
              {order.contractMonths > 0 && <span style={{ color: 'var(--ink-3)', marginLeft: 6, fontWeight: 400, fontSize: 11 }}>· สัญญา {order.contractMonths} เดือน</span>}
            </span>
          </div>
        )}
      </div>

      {/* Docs count */}
      {(order.documents?.length > 0) && (
        <div style={{ marginTop: 10, fontSize: 11.5, color: 'var(--ink-3)', display: 'flex', alignItems: 'center', gap: 6 }}>
          <Icon name="file" size={11} color="var(--ink-4)"/>
          เอกสารที่แนบไว้ {order.documents.length} ไฟล์จะถูกลบด้วย
        </div>
      )}
    </Modal>
  );
};

// ---------- Edit Order Modal ----------
const SECTORS = ['Manufacturing','Retail','Banking','Logistics','Hospitality','Healthcare','Agriculture','Trading','Technology','Government','Education','Other'];
const SIZES   = ['1–49','50–199','200–499','500–999','1,000+'];

const EditOrderModal = ({ order, statusId, onClose, onSaved }) => {
  const isResubmit = statusId === 'submitted';
  const { perms = {} } = React.useContext(window.PermCtx);
  const canEditCustomer = perms['Edit customer'] === true;

  const allProds = window.PRODUCTS || PRODUCTS || [];
  const liveProds = allProds.filter(p => (p.status || 'live') === 'live');

  const [items, setItems] = React.useState(
    order.items.map(it => ({ ...it }))
  );
  const [contractMonths, setContractMonths] = React.useState(order.contractMonths || 12);
  const [notes, setNotes] = React.useState(order.notes || '');
  const [saving, setSaving] = React.useState(false);
  const [docError, setDocError] = React.useState('');
  const [activeProductId, setActiveProductId] = React.useState(null);

  // Company & Contact state
  const [company, setCompany] = React.useState({
    name:     order.company?.name     || '',
    taxId:    order.company?.taxId    || '',
    sector:   order.company?.sector   || '',
    province: order.company?.province || '',
    size:     order.company?.size     || '',
  });
  const [contact, setContact] = React.useState({
    name:        order.contact?.name        || '',
    role:        order.contact?.role        || '',
    email:       order.contact?.email       || '',
    phone:       order.contact?.phone       || '',
    mobile:      order.contact?.mobile      || '',
    contactType: order.contact?.contactType || 'Primary',
  });
  const updCo  = (k, v) => setCompany(prev => ({ ...prev, [k]: v }));
  const updCt  = (k, v) => setContact(prev => ({ ...prev, [k]: v }));

  // Additional contacts state
  const [allContacts, setAllContacts] = React.useState([]);
  const [newContacts, setNewContacts] = React.useState([]); // unsaved new rows
  React.useEffect(() => {
    if (!order.company?.id) return;
    window.apiFetch(`/api/companies/${order.company.id}/contacts`)
      .then(r => r.ok ? r.json() : [])
      .then(rows => setAllContacts(rows))
      .catch(() => {});
  }, [order.company?.id]);

  const addNewContactRow = () => setNewContacts(prev => [
    ...prev, { _key: Date.now(), name: '', role: '', email: '', phone: '', mobile: '', contactType: 'Primary' }
  ]);
  const updNewContact = (key, field, val) => setNewContacts(prev =>
    prev.map(c => c._key === key ? { ...c, [field]: val } : c)
  );
  const removeNewContact = key => setNewContacts(prev => prev.filter(c => c._key !== key));

  // Documents state
  const [docs, setDocs] = React.useState(order.documents || []);
  const [newFiles, setNewFiles] = React.useState([]);      // untyped generic uploads
  const [typedFiles, setTypedFiles] = React.useState({});  // docTypeId → { file, name, size }
  const [deletedDocIds, setDeletedDocIds] = React.useState([]);
  const docInputRef = React.useRef(null);

  const markDeleteDoc = (docId) => {
    setDeletedDocIds(prev => [...prev, docId]);
    setDocs(prev => prev.filter(d => d.id !== docId));
  };

  // Generic (untyped) upload
  const handleDocFilePick = (e) => {
    const files = Array.from(e.target.files || []);
    e.target.value = '';
    setNewFiles(prev => [...prev, ...files.map(f => ({
      file: f, name: f.name,
      size: f.size < 1024*1024 ? `${Math.round(f.size/1024)} KB` : `${(f.size/1024/1024).toFixed(1)} MB`,
    }))]);
  };
  const removeNewFile = (name) => setNewFiles(prev => prev.filter(f => f.name !== name));

  // Typed upload (per doc type)
  const handleTypedUpload = (docTypeId) => (fileInfo) => {
    const existing = docs.find(d => normDocTypeId(d) === docTypeId);
    if (existing) markDeleteDoc(existing.id);
    setTypedFiles(prev => ({ ...prev, [docTypeId]: fileInfo }));
    setDocError(''); // clear validation error when user uploads
  };
  const handleTypedRemove = (docTypeId) => () => {
    setTypedFiles(prev => { const n = { ...prev }; delete n[docTypeId]; return n; });
  };
  const normDocTypeId = (d) => d.doc_type_id || d.docTypeId || null;
  const getExistingDoc = (docTypeId) => docs.find(d => normDocTypeId(d) === docTypeId) || null;

  // Compute required/optional docs from current items × DOC_REQUIREMENTS
  const docTypes = window.DOC_TYPES || DOC_TYPES || [];
  const docReqs  = window.DOC_REQUIREMENTS || DOC_REQUIREMENTS || {};
  const docLevels = {};
  for (const it of items) {
    const reqs = docReqs[it.productId] || {};
    for (const [docId, level] of Object.entries(reqs)) {
      if (level === 'none') continue;
      const cur = docLevels[docId];
      if (!cur || (level === 'required' && cur !== 'required')) docLevels[docId] = level;
    }
  }
  const requiredDocs = docTypes.filter(d => docLevels[d.id] === 'required');
  const optionalDocs = docTypes.filter(d => docLevels[d.id] === 'optional');
  const typedDocIds  = new Set([...requiredDocs, ...optionalDocs].map(d => d.id));
  // Untyped = existing docs not matched to any doc-type requirement
  const untypedDocs = docs.filter(d => { const tid = normDocTypeId(d); return !tid || !typedDocIds.has(tid); });

  const getProduct = id => allProds.find(p => p.id === id);
  const getPkg     = (prod, pkgId) => prod?.packages?.find(p => p.id === pkgId);

  const setQty = (productId, qty) => setItems(prev =>
    prev.map(it => it.productId === productId ? { ...it, qty: Math.max(1, parseInt(qty) || 1) } : it)
  );
  const removeItem = productId => setItems(prev => prev.filter(it => it.productId !== productId));
  const addPkg = (productId, packageId) => {
    const prod = getProduct(productId);
    const pkg  = getPkg(prod, packageId);
    if (!pkg) return;
    setItems(prev => {
      const without = prev.filter(it => it.productId !== productId);
      return [...without, { productId, packageId, qty: pkg.seats || 1, unitPrice: pkg.price }];
    });
    setActiveProductId(null);
  };

  const monthly = items.reduce((sum, it) => {
    const prod = getProduct(it.productId);
    const pkg  = getPkg(prod, it.packageId);
    return sum + (pkg ? pkg.price * it.qty : it.unitPrice * it.qty);
  }, 0);

  const handleSave = async () => {
    if (!items.length) { showToast('ต้องมีอย่างน้อย 1 product', { variant: 'error' }); return; }

    // Validate required documents (only enforce on Resubmit)
    if (isResubmit && requiredDocs.length > 0) {
      const missingLabels = requiredDocs
        .filter(d => {
          // Passes if: existing doc (not deleted) OR new typed file queued
          const existingOk = docs.find(ex => (ex.doc_type_id || ex.docTypeId) === d.id && !deletedDocIds.includes(ex.id));
          const queuedOk   = !!typedFiles[d.id];
          return !existingOk && !queuedOk;
        })
        .map(d => d.label);
      if (missingLabels.length > 0) {
        setDocError(`กรุณาอัปโหลดเอกสาร Required ที่ยังขาดอยู่: ${missingLabels.join(', ')}`);
        return;
      }
    }
    setDocError('');
    setSaving(true);
    try {
      // 1. Update order (items + contract terms)
      const payload = {
        contract_months: contractMonths,
        notes: notes.trim() || null,
        items: items.map(it => {
          const prod = getProduct(it.productId);
          const pkg  = getPkg(prod, it.packageId);
          return { product_id: it.productId, package_id: it.packageId, qty: it.qty, unit_price: pkg?.price ?? it.unitPrice };
        }),
      };
      const r = await window.apiFetch(`/api/orders/${order.id}`, {
        method: 'PATCH', body: JSON.stringify(payload),
      });
      if (!r.ok) { const d = await r.json(); showToast(d.error || 'บันทึกไม่สำเร็จ', { variant: 'error' }); return; }

      // 2. Update company & primary contact if user has permission
      if (canEditCustomer && order.company?.id) {
        await window.apiFetch(`/api/companies/${order.company.id}`, {
          method: 'PATCH',
          body: JSON.stringify({
            name: company.name, taxId: company.taxId,
            sector: company.sector, province: company.province, size: company.size,
          }),
        }).catch(() => {});
      }
      if (canEditCustomer && order.contact?.id) {
        await window.apiFetch(`/api/contacts/${order.contact.id}`, {
          method: 'PATCH',
          body: JSON.stringify({
            name: contact.name, role: contact.role,
            email: contact.email, phone: contact.phone, mobile: contact.mobile,
            isPrimary: true, contactType: contact.contactType || 'Primary',
          }),
        }).catch(() => {});
      }

      // 3. Save new additional contacts
      if (canEditCustomer) {
        for (const nc of newContacts) {
          if (!nc.name.trim()) continue;
          await window.apiFetch('/api/contacts', {
            method: 'POST',
            body: JSON.stringify({
              companyId: order.company?.id, name: nc.name.trim(),
              role: nc.role || null, email: nc.email || null,
              phone: nc.phone || null, mobile: nc.mobile || null,
              isPrimary: false, contactType: nc.contactType || 'Primary',
            }),
          }).catch(() => {});
        }
      }

      // 4. Delete removed documents
      for (const docId of deletedDocIds) {
        await window.apiFetch(`/api/orders/${order.id}/documents/${docId}`, {
          method: 'DELETE',
        }).catch(() => {});
      }

      // 5a. Upload new typed files (with doc_type_id)
      for (const [doc_type_id, f] of Object.entries(typedFiles)) {
        const fd = new FormData();
        fd.append('file', f.file);
        fd.append('doc_type_id', doc_type_id);
        await fetch(`/api/orders/${order.id}/documents/upload`, {
          method: 'POST',
          headers: { Authorization: `Bearer ${localStorage.getItem('sol_auth_token')}` },
          body: fd,
        }).catch(() => {});
      }

      // 5b. Upload new untyped files
      for (const f of newFiles) {
        const fd = new FormData();
        fd.append('file', f.file);
        await fetch(`/api/orders/${order.id}/documents/upload`, {
          method: 'POST',
          headers: { Authorization: `Bearer ${localStorage.getItem('sol_auth_token')}` },
          body: fd,
        }).catch(() => {});
      }

      // 6. Log activity — resubmitted when editing while submitted, otherwise edited
      const activityAction = isResubmit ? 'resubmitted' : 'edited';
      const activityNote = notes.trim()
        ? (isResubmit ? `ข้อความถึง Approver: ${notes.trim()}` : `หมายเหตุถึง Approver: ${notes.trim()}`)
        : (isResubmit ? 'แก้ไขและส่งคืน Approver ขณะรออนุมัติ' : null);
      await window.apiFetch(`/api/orders/${order.id}/activities`, {
        method: 'POST',
        body: JSON.stringify({ action: activityAction, note: activityNote }),
      }).catch(() => {});

      showToast(isResubmit ? 'แก้ไขและแจ้ง Approver แล้ว' : 'บันทึก order แล้ว', { variant: 'success' });
      const fresh = await window.apiFetch(`/api/orders/${order.id}`).then(r => r.ok ? r.json() : null);
      if (fresh) onSaved(fresh);
      onClose();
    } catch { showToast('เกิดข้อผิดพลาด', { variant: 'error' }); }
    finally { setSaving(false); }
  };

  return (
    <Modal open
      title={isResubmit ? 'Edit & Resubmit' : 'Edit order'}
      subtitle={isResubmit ? `แก้ไขและแจ้ง Approver — ${order.id} กำลังรออนุมัติอยู่` : `แก้ไขรายละเอียดคำสั่งซื้อ ${order.id}`}
      onClose={onClose} width={760}
      footer={<>
        <div style={{ flex: 1 }}>
          {docError && (
            <div style={{ display: 'flex', alignItems: 'center', gap: 6, fontSize: 11.5, color: 'var(--negative)' }}>
              <Icon name="alert" size={12} color="var(--negative)"/>
              {docError}
            </div>
          )}
        </div>
        <Button variant="ghost" onClick={onClose}>Cancel</Button>
        <Button variant="primary" icon={isResubmit ? 'arrowRight' : 'check'} disabled={saving} onClick={handleSave}>
          {saving ? 'กำลังบันทึก…' : (isResubmit ? 'Save & Notify Approver' : 'Save changes')}
        </Button>
      </>}>
      <div style={{ display: 'flex', flexDirection: 'column', gap: 20 }}>

        {/* Resubmit context banner */}
        {isResubmit && (
          <div style={{
            background: '#eff6ff', border: '1px solid #bfdbfe', borderLeft: '3px solid #3b82f6',
            borderRadius: 3, padding: '10px 14px', fontSize: 12.5,
            display: 'flex', alignItems: 'flex-start', gap: 10,
          }}>
            <Icon name="alert" size={14} color="#3b82f6" style={{ marginTop: 1 }}/>
            <div>
              <div style={{ fontWeight: 600, color: '#1d4ed8', marginBottom: 2 }}>แก้ไขขณะรออนุมัติ</div>
              <div style={{ color: 'var(--ink-2)' }}>
                การแก้ไขนี้จะบันทึกใน Activity log และแจ้ง Approver ทันที — ระบุข้อความถึง Approver ด้านล่างเพื่อให้บริบทชัดเจน
              </div>
            </div>
          </div>
        )}

        {/* Products section */}
        <div>
          <div style={{ fontSize: 13, fontWeight: 600, marginBottom: 10, display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
            <span>Products & packages</span>
            <Button variant="ghost" size="sm" icon="plus" onClick={() => setActiveProductId('__pick__')}>
              Add product
            </Button>
          </div>

          {/* Product picker */}
          {activeProductId === '__pick__' && (
            <div style={{ marginBottom: 12, padding: 14, background: 'var(--bg-2)', borderRadius: 4, border: '1px solid var(--line)' }}>
              <div style={{ fontSize: 12, fontWeight: 500, marginBottom: 10, color: 'var(--ink-2)' }}>เลือก product</div>
              <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(200px,1fr))', gap: 8 }}>
                {liveProds.filter(p => !items.some(it => it.productId === p.id)).map(p => (
                  <button key={p.id} onClick={() => setActiveProductId(p.id)} style={{
                    padding: '8px 12px', background: 'var(--panel)', border: '1px solid var(--line)',
                    borderRadius: 3, cursor: 'pointer', textAlign: 'left', fontFamily: 'Kanit, sans-serif',
                    display: 'flex', alignItems: 'center', gap: 8,
                  }}>
                    <ProductGlyph productId={p.id} size={24}/>
                    <div>
                      <div style={{ fontSize: 12, fontWeight: 500 }}>{p.name}</div>
                      <div style={{ fontSize: 10.5, color: 'var(--ink-3)' }}>{p.packages.length} packages</div>
                    </div>
                  </button>
                ))}
              </div>
              <Button variant="ghost" size="sm" onClick={() => setActiveProductId(null)} style={{ marginTop: 8 }}>ยกเลิก</Button>
            </div>
          )}

          {/* Package picker for selected product */}
          {activeProductId && activeProductId !== '__pick__' && (() => {
            const prod = getProduct(activeProductId);
            if (!prod) return null;
            return (
              <div style={{ marginBottom: 12, padding: 14, background: 'var(--bg-2)', borderRadius: 4, border: '1px solid var(--line)' }}>
                <div style={{ fontSize: 12, fontWeight: 500, marginBottom: 10, display: 'flex', alignItems: 'center', gap: 8 }}>
                  <ProductGlyph productId={prod.id} size={20}/>
                  <span>{prod.name} — เลือก package</span>
                </div>
                <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(180px,1fr))', gap: 8 }}>
                  {prod.packages.map(pk => (
                    <button key={pk.id} onClick={() => addPkg(prod.id, pk.id)} style={{
                      padding: '10px 12px', background: 'var(--panel)',
                      border: `1px solid var(--line)`, borderRadius: 3,
                      cursor: 'pointer', textAlign: 'left', fontFamily: 'Kanit, sans-serif',
                    }}>
                      <div style={{ fontSize: 12.5, fontWeight: 500 }}>{pk.name}</div>
                      <div className="num" style={{ fontSize: 13, marginTop: 4 }}>฿{pk.price.toLocaleString()}<span style={{ fontSize: 10, color: 'var(--ink-3)', marginLeft: 3 }}>/{prod.unit}/mo</span></div>
                    </button>
                  ))}
                </div>
                <Button variant="ghost" size="sm" onClick={() => setActiveProductId(null)} style={{ marginTop: 8 }}>ยกเลิก</Button>
              </div>
            );
          })()}

          {/* Current items */}
          <div style={{ border: '1px solid var(--line)', borderRadius: 4, overflow: 'hidden' }}>
            {items.length === 0 && (
              <div style={{ padding: '16px', fontSize: 12, color: 'var(--ink-4)', textAlign: 'center' }}>
                ยังไม่มี product — กด Add product เพื่อเพิ่ม
              </div>
            )}
            {items.map((it, i) => {
              const prod = getProduct(it.productId);
              const pkg  = getPkg(prod, it.packageId);
              if (!prod) return null;
              return (
                <div key={it.productId} style={{
                  display: 'grid', gridTemplateColumns: '36px 1fr auto auto',
                  gap: 12, alignItems: 'center', padding: '12px 14px',
                  borderBottom: i < items.length - 1 ? '1px solid var(--line-2)' : 'none',
                }}>
                  <ProductGlyph productId={it.productId} size={32}/>
                  <div>
                    <div style={{ fontWeight: 500, fontSize: 13 }}>{prod.name}</div>
                    <div style={{ fontSize: 11, color: 'var(--ink-3)' }}>
                      {pkg?.name} · <span className="num">฿{(pkg?.price || it.unitPrice).toLocaleString()}</span>/{prod.unit}/mo
                    </div>
                  </div>
                  {/* Qty stepper */}
                  <div style={{ display: 'flex', alignItems: 'center', gap: 4 }}>
                    <button onClick={() => setQty(it.productId, it.qty - 1)}
                      style={{ width: 26, height: 26, border: '1px solid var(--line)', background: 'var(--panel)', borderRadius: 3, cursor: 'pointer', display: 'grid', placeItems: 'center', color: 'var(--ink-2)' }}>
                      <Icon name="close" size={9}/>
                    </button>
                    <input type="number" value={it.qty} onChange={e => setQty(it.productId, e.target.value)}
                      style={{ ...inputStyle, width: 56, textAlign: 'center', fontFamily: 'IBM Plex Mono', padding: '4px 6px', fontSize: 13 }}/>
                    <button onClick={() => setQty(it.productId, it.qty + 1)}
                      style={{ width: 26, height: 26, border: '1px solid var(--line)', background: 'var(--panel)', borderRadius: 3, cursor: 'pointer', display: 'grid', placeItems: 'center', color: 'var(--ink-2)' }}>
                      <Icon name="plus" size={9}/>
                    </button>
                  </div>
                  <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
                    <span className="num" style={{ fontSize: 13, fontWeight: 500, minWidth: 80, textAlign: 'right' }}>
                      {fmtBaht((pkg?.price || it.unitPrice) * it.qty)}
                    </span>
                    <button onClick={() => removeItem(it.productId)}
                      title="ลบ" style={{ background: 'none', border: 'none', cursor: 'pointer', color: 'var(--ink-4)', padding: 2, display: 'grid', placeItems: 'center' }}>
                      <Icon name="close" size={13}/>
                    </button>
                  </div>
                </div>
              );
            })}
            {items.length > 0 && (
              <div style={{ padding: '10px 14px', background: 'var(--bg-2)', display: 'flex', justifyContent: 'flex-end', alignItems: 'center', gap: 8 }}>
                <span style={{ fontSize: 11, color: 'var(--ink-3)' }}>Monthly MRR</span>
                <span className="num" style={{ fontSize: 16, fontWeight: 500 }}>{fmtBaht(monthly)}</span>
              </div>
            )}
          </div>
        </div>

        {/* Contract terms */}
        <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 14 }}>
          <Field label="Contract term">
            <Select value={contractMonths} onChange={e => setContractMonths(parseInt(e.target.value))}>
              <option value={12}>12 months</option>
              <option value={24}>24 months</option>
              <option value={36}>36 months</option>
            </Select>
          </Field>
          <Field label="Total contract value">
            <div style={{ ...inputStyle, background: 'var(--bg-2)', color: 'var(--ink-2)', fontFamily: 'IBM Plex Mono', fontSize: 13 }}>
              {fmtBaht(monthly * contractMonths)}
            </div>
          </Field>
        </div>

        {/* Notes / Message to approver */}
        <div style={isResubmit ? {
          background: '#fefce8', border: '1px solid #fde68a', borderRadius: 4, padding: '14px 16px',
        } : {}}>
          <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 6 }}>
            <span style={{ fontSize: 12, fontWeight: 500, color: isResubmit ? '#92400e' : 'var(--ink-2)' }}>
              ข้อความถึง Approver
            </span>
            {isResubmit ? (
              <span style={{ fontSize: 10.5, padding: '1px 6px', background: '#fde68a', color: '#92400e', borderRadius: 2, fontWeight: 600 }}>
                แนะนำให้กรอก
              </span>
            ) : (
              <span style={{ fontSize: 10.5, padding: '1px 6px', background: '#fef3e8', color: '#d97b2e', borderRadius: 2, fontWeight: 500 }}>
                จะแสดงใน Approval inbox
              </span>
            )}
          </div>
          <Textarea value={notes} onChange={e => setNotes(e.target.value)} rows={isResubmit ? 4 : 3}
            placeholder={isResubmit
              ? 'อธิบายการเปลี่ยนแปลงที่ทำ เช่น แก้ไขจำนวน license ตามที่หารือ, เพิ่มเอกสารครบถ้วนแล้ว, ลูกค้ายืนยันข้อมูลแล้ว…'
              : 'เช่น ลูกค้ามีความเร่งด่วน, แก้ไขจำนวน license ตามที่หารือ, เอกสารครบถ้วนแล้ว…'}
          />
          <div style={{ fontSize: 11, color: isResubmit ? '#92400e' : 'var(--ink-4)', marginTop: 4 }}>
            {isResubmit
              ? 'ข้อความนี้จะปรากฏใน Activity log และ Approver จะเห็นทันทีเมื่อ save'
              : 'ข้อความนี้จะบันทึกใน Activity log และแสดงต่อ approver ทุกคนในสาย'}
          </div>
        </div>

        {/* Divider */}
        <div style={{ borderTop: '1px solid var(--line)', margin: '4px 0' }}/>

        {/* Company information */}
        <div>
          <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 12 }}>
            <Icon name="building" size={13} color="var(--ink-3)"/>
            <span style={{ fontSize: 13, fontWeight: 600 }}>ข้อมูลบริษัท</span>
            {!canEditCustomer && (
              <span style={{ fontSize: 10.5, padding: '1px 7px', background: 'var(--bg-2)', border: '1px solid var(--line)', borderRadius: 2, color: 'var(--ink-4)', display: 'flex', alignItems: 'center', gap: 4 }}>
                <Icon name="lock" size={9}/> Read only
              </span>
            )}
          </div>
          <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 12 }}>
            <Field label="ชื่อบริษัท" required={canEditCustomer}>
              <TextInput value={company.name} disabled={!canEditCustomer}
                onChange={e => updCo('name', e.target.value)}
                style={!canEditCustomer ? { background: 'var(--bg-2)', color: 'var(--ink-3)', cursor: 'default' } : {}}/>
            </Field>
            <Field label="เลขประจำตัวผู้เสียภาษี (Tax ID)">
              <TextInput value={company.taxId} disabled={!canEditCustomer}
                onChange={e => updCo('taxId', e.target.value)}
                style={!canEditCustomer ? { background: 'var(--bg-2)', color: 'var(--ink-3)', cursor: 'default' } : {}}/>
            </Field>
            <Field label="ประเภทธุรกิจ (Sector)">
              {canEditCustomer ? (
                <Select value={company.sector} onChange={e => updCo('sector', e.target.value)}>
                  <option value="">— เลือกประเภท —</option>
                  {SECTORS.map(s => <option key={s} value={s}>{s}</option>)}
                </Select>
              ) : (
                <TextInput value={company.sector} disabled style={{ background: 'var(--bg-2)', color: 'var(--ink-3)', cursor: 'default' }}/>
              )}
            </Field>
            <Field label="จังหวัด (Province)">
              <TextInput value={company.province} disabled={!canEditCustomer}
                onChange={e => updCo('province', e.target.value)}
                style={!canEditCustomer ? { background: 'var(--bg-2)', color: 'var(--ink-3)', cursor: 'default' } : {}}/>
            </Field>
            <Field label="ขนาดองค์กร">
              {canEditCustomer ? (
                <Select value={company.size} onChange={e => updCo('size', e.target.value)}>
                  <option value="">— เลือกขนาด —</option>
                  {SIZES.map(s => <option key={s} value={s}>{s} emp.</option>)}
                </Select>
              ) : (
                <TextInput value={company.size} disabled style={{ background: 'var(--bg-2)', color: 'var(--ink-3)', cursor: 'default' }}/>
              )}
            </Field>
          </div>
        </div>

        {/* Primary contact */}
        <div>
          <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 12 }}>
            <Icon name="user" size={13} color="var(--ink-3)"/>
            <span style={{ fontSize: 13, fontWeight: 600 }}>ผู้ติดต่อหลัก</span>
            {!canEditCustomer && (
              <span style={{ fontSize: 10.5, padding: '1px 7px', background: 'var(--bg-2)', border: '1px solid var(--line)', borderRadius: 2, color: 'var(--ink-4)', display: 'flex', alignItems: 'center', gap: 4 }}>
                <Icon name="lock" size={9}/> Read only
              </span>
            )}
          </div>
          <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 12 }}>
            {[
              { label: 'ชื่อ-นามสกุล', key: 'name', required: true },
              { label: 'ตำแหน่ง', key: 'role' },
              { label: 'อีเมล', key: 'email', type: 'email' },
              { label: 'เบอร์โทรที่ทำงาน', key: 'phone' },
              { label: 'เบอร์มือถือ', key: 'mobile' },
            ].map(({ label, key, required, type }) => (
              <Field key={key} label={label} required={required && canEditCustomer}>
                <TextInput type={type || 'text'} value={contact[key]} disabled={!canEditCustomer}
                  onChange={e => updCt(key, e.target.value)}
                  style={!canEditCustomer ? { background: 'var(--bg-2)', color: 'var(--ink-3)', cursor: 'default' } : {}}/>
              </Field>
            ))}
            <Field label="ประเภทผู้ติดต่อ · Contact type">
              {canEditCustomer ? (
                <Select value={contact.contactType || 'Primary'} onChange={e => updCt('contactType', e.target.value)}>
                  <option value="Primary">Primary</option>
                  <option value="Executive">Executive</option>
                  <option value="Accounting">Accounting</option>
                  <option value="Sales">Sales</option>
                  <option value="Technical">Technical</option>
                </Select>
              ) : (
                <TextInput value={contact.contactType || 'Primary'} disabled style={{ background: 'var(--bg-2)', color: 'var(--ink-3)', cursor: 'default' }}/>
              )}
            </Field>
          </div>
        </div>

        {/* Additional contacts */}
        <div>
          <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 10 }}>
            <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
              <Icon name="users" size={13} color="var(--ink-3)"/>
              <span style={{ fontSize: 13, fontWeight: 600 }}>ผู้ติดต่ออื่นๆ</span>
              <span style={{ fontSize: 11, color: 'var(--ink-4)' }}>({allContacts.filter(c => c.id !== order.contact?.id).length} รายการ)</span>
            </div>
            {canEditCustomer && (
              <Button variant="ghost" size="sm" icon="plus" onClick={addNewContactRow}>เพิ่ม contact</Button>
            )}
          </div>

          {/* Existing non-primary contacts */}
          {allContacts.filter(c => c.id !== order.contact?.id).map(ct => (
            <div key={ct.id} style={{
              display: 'grid', gridTemplateColumns: 'auto 1fr 1fr 1fr 1fr auto',
              gap: 8, marginBottom: 8, padding: '10px 12px',
              background: 'var(--bg-2)', borderRadius: 3, border: '1px solid var(--line)',
              alignItems: 'center',
            }}>
              <span style={{ fontSize: 10, padding: '2px 7px', background: 'var(--bg-3)', border: '1px solid var(--line)', borderRadius: 2, color: 'var(--ink-3)', whiteSpace: 'nowrap' }}>{ct.contactType || 'Primary'}</span>
              <div style={{ fontSize: 12, fontWeight: 500 }}>{ct.name}</div>
              <div style={{ fontSize: 11.5, color: 'var(--ink-3)' }}>{ct.role || '—'}</div>
              <div style={{ fontSize: 11.5, color: 'var(--ink-3)', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{ct.email || '—'}</div>
              <div style={{ fontSize: 11.5, color: 'var(--ink-3)' }}>{ct.mobile || ct.phone || '—'}</div>
              {canEditCustomer && (
                <button onClick={async () => {
                  await window.apiFetch(`/api/contacts/${ct.id}`, { method: 'DELETE' }).catch(() => {});
                  setAllContacts(prev => prev.filter(x => x.id !== ct.id));
                }} title="ลบ" style={{ background: 'none', border: 'none', cursor: 'pointer', color: 'var(--negative)', padding: 4, display: 'grid', placeItems: 'center' }}>
                  <Icon name="close" size={12}/>
                </button>
              )}
            </div>
          ))}

          {/* New contact rows */}
          {newContacts.map(nc => (
            <div key={nc._key} style={{
              padding: 12, marginBottom: 8, background: '#f0fdf4',
              border: '1px solid var(--positive)', borderRadius: 3,
            }}>
              <div style={{ fontSize: 10.5, color: 'var(--positive)', fontWeight: 600, marginBottom: 8, textTransform: 'uppercase', letterSpacing: '0.05em' }}>
                New contact
              </div>
              <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 10 }}>
                {[
                  { label: 'ชื่อ-นามสกุล', key: 'name', required: true },
                  { label: 'ตำแหน่ง', key: 'role' },
                  { label: 'อีเมล', key: 'email' },
                  { label: 'เบอร์มือถือ', key: 'mobile' },
                ].map(({ label, key, required }) => (
                  <Field key={key} label={label} required={required}>
                    <TextInput value={nc[key]} placeholder={label}
                      onChange={e => updNewContact(nc._key, key, e.target.value)}/>
                  </Field>
                ))}
                <Field label="ประเภทผู้ติดต่อ · Contact type">
                  <Select value={nc.contactType || 'Primary'} onChange={e => updNewContact(nc._key, 'contactType', e.target.value)}>
                    <option value="Primary">Primary</option>
                    <option value="Executive">Executive</option>
                    <option value="Accounting">Accounting</option>
                    <option value="Sales">Sales</option>
                    <option value="Technical">Technical</option>
                  </Select>
                </Field>
              </div>
              <div style={{ display: 'flex', justifyContent: 'flex-end', marginTop: 8 }}>
                <Button variant="ghost" size="sm" icon="close" onClick={() => removeNewContact(nc._key)}>Remove</Button>
              </div>
            </div>
          ))}

          {allContacts.filter(c => c.id !== order.contact?.id).length === 0 && newContacts.length === 0 && (
            <div style={{ padding: '12px 14px', background: 'var(--bg-2)', borderRadius: 3, fontSize: 12, color: 'var(--ink-4)', textAlign: 'center', border: '1px dashed var(--line-3)' }}>
              ยังไม่มีผู้ติดต่ออื่นๆ
            </div>
          )}
        </div>

        {/* Documents */}
        <div style={{ borderTop: '1px solid var(--line)', paddingTop: 20 }}>
          <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 14 }}>
            <Icon name="file" size={13} color="var(--ink-3)"/>
            <span style={{ fontSize: 13, fontWeight: 600 }}>เอกสารแนบ</span>
          </div>

          {requiredDocs.length === 0 && optionalDocs.length === 0 ? (
            /* No doc requirements — show generic upload */
            <div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
              <div style={{ display: 'flex', justifyContent: 'flex-end', marginBottom: 4 }}>
                <Button variant="ghost" size="sm" icon="upload" onClick={() => docInputRef.current?.click()}>Upload</Button>
                <input ref={docInputRef} type="file" multiple accept=".pdf,.jpg,.jpeg,.png,.xlsx,.docx" style={{ display: 'none' }} onChange={handleDocFilePick}/>
              </div>
              {untypedDocs.map((d, i) => (
                <div key={d.id || i} style={{ display: 'flex', alignItems: 'center', gap: 10, padding: '8px 12px', background: 'var(--bg-2)', borderRadius: 3, border: '1px solid var(--line)' }}>
                  <Icon name="fileCheck" size={13} color="var(--positive)"/>
                  <div style={{ flex: 1, minWidth: 0 }}>
                    <div style={{ fontSize: 12, fontWeight: 500, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{d.filename}</div>
                    {d.size && <div style={{ fontSize: 10.5, color: 'var(--ink-4)' }}>{d.size}</div>}
                  </div>
                  <button onClick={() => markDeleteDoc(d.id)} title="ลบ" style={{ background: 'none', border: 'none', cursor: 'pointer', color: 'var(--negative)', padding: 4, display: 'grid', placeItems: 'center' }}>
                    <Icon name="close" size={12}/>
                  </button>
                </div>
              ))}
              {newFiles.map((f, i) => (
                <div key={i} style={{ display: 'flex', alignItems: 'center', gap: 10, padding: '8px 12px', background: '#f0fdf4', borderRadius: 3, border: '1px solid var(--positive)' }}>
                  <Icon name="upload" size={13} color="var(--positive)"/>
                  <div style={{ flex: 1, minWidth: 0 }}>
                    <div style={{ fontSize: 12, fontWeight: 500 }}>{f.name}</div>
                    <div style={{ fontSize: 10.5, color: 'var(--positive)' }}>{f.size} · จะ upload เมื่อกด Save</div>
                  </div>
                  <button onClick={() => removeNewFile(f.name)} title="ยกเลิก" style={{ background: 'none', border: 'none', cursor: 'pointer', color: 'var(--ink-4)', padding: 4, display: 'grid', placeItems: 'center' }}>
                    <Icon name="close" size={12}/>
                  </button>
                </div>
              ))}
              {untypedDocs.length === 0 && newFiles.length === 0 && (
                <div style={{ padding: '12px 14px', background: 'var(--bg-2)', borderRadius: 3, fontSize: 12, color: 'var(--ink-4)', textAlign: 'center', border: '1px dashed var(--line-3)' }}>
                  ยังไม่มีเอกสาร — กด Upload เพื่อเพิ่มไฟล์
                </div>
              )}
            </div>
          ) : (
            /* Has doc requirements — show DocRow per type */
            <>
              {requiredDocs.length > 0 && (
                <div style={{ marginBottom: 14 }}>
                  <div className="eyebrow" style={{ fontSize: 10, color: 'var(--ink-3)', marginBottom: 8, letterSpacing: '.07em' }}>
                    เอกสารที่จำเป็น · Required
                  </div>
                  <div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
                    {requiredDocs.map(d => {
                      const existing = getExistingDoc(d.id);
                      const queued   = typedFiles[d.id];
                      const fileForRow = queued || (existing ? { name: existing.filename, size: existing.size } : null);
                      const accept = d.formats ? d.formats.replace(/·/g,',').replace(/ /g,'').toLowerCase().split(',').map(x => '.'+x.trim()).join(',') : undefined;
                      return (
                        <DocRow key={d.id} label={d.label} en={d.en} hint={d.desc || ''}
                          file={fileForRow}
                          onUpload={handleTypedUpload(d.id)}
                          onRemove={existing && !queued ? () => markDeleteDoc(existing.id) : handleTypedRemove(d.id)}
                          accept={accept}/>
                      );
                    })}
                  </div>
                </div>
              )}

              {optionalDocs.length > 0 && (
                <div style={{ marginBottom: 14 }}>
                  <div className="eyebrow" style={{ fontSize: 10, color: 'var(--ink-3)', marginBottom: 8, letterSpacing: '.07em' }}>
                    เอกสารเพิ่มเติม · Optional
                  </div>
                  <div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
                    {optionalDocs.map(d => {
                      const existing = getExistingDoc(d.id);
                      const queued   = typedFiles[d.id];
                      const fileForRow = queued || (existing ? { name: existing.filename, size: existing.size } : null);
                      const accept = d.formats ? d.formats.replace(/·/g,',').replace(/ /g,'').toLowerCase().split(',').map(x => '.'+x.trim()).join(',') : undefined;
                      return (
                        <DocRow key={d.id} label={d.label} en={d.en} hint={d.desc || ''}
                          file={fileForRow}
                          onUpload={handleTypedUpload(d.id)}
                          onRemove={existing && !queued ? () => markDeleteDoc(existing.id) : handleTypedRemove(d.id)}
                          accept={accept}/>
                      );
                    })}
                  </div>
                </div>
              )}

              {/* Other documents (not tied to a doc type) */}
              <div>
                <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 8 }}>
                  <div className="eyebrow" style={{ fontSize: 10, color: 'var(--ink-3)', letterSpacing: '.07em' }}>เอกสารอื่นๆ · Other</div>
                  <Button variant="ghost" size="sm" icon="upload" onClick={() => docInputRef.current?.click()}>Upload</Button>
                  <input ref={docInputRef} type="file" multiple accept=".pdf,.jpg,.jpeg,.png,.xlsx,.docx" style={{ display: 'none' }} onChange={handleDocFilePick}/>
                </div>
                <div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
                  {untypedDocs.map((d, i) => (
                    <div key={d.id || i} style={{ display: 'flex', alignItems: 'center', gap: 10, padding: '8px 12px', background: 'var(--bg-2)', borderRadius: 3, border: '1px solid var(--line)' }}>
                      <Icon name="fileCheck" size={13} color="var(--positive)"/>
                      <div style={{ flex: 1, minWidth: 0 }}>
                        <div style={{ fontSize: 12, fontWeight: 500, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{d.filename}</div>
                        {d.size && <div style={{ fontSize: 10.5, color: 'var(--ink-4)' }}>{d.size}</div>}
                      </div>
                      <button onClick={() => markDeleteDoc(d.id)} title="ลบ" style={{ background: 'none', border: 'none', cursor: 'pointer', color: 'var(--negative)', padding: 4, display: 'grid', placeItems: 'center' }}>
                        <Icon name="close" size={12}/>
                      </button>
                    </div>
                  ))}
                  {newFiles.map((f, i) => (
                    <div key={i} style={{ display: 'flex', alignItems: 'center', gap: 10, padding: '8px 12px', background: '#f0fdf4', borderRadius: 3, border: '1px solid var(--positive)' }}>
                      <Icon name="upload" size={13} color="var(--positive)"/>
                      <div style={{ flex: 1, minWidth: 0 }}>
                        <div style={{ fontSize: 12, fontWeight: 500 }}>{f.name}</div>
                        <div style={{ fontSize: 10.5, color: 'var(--positive)' }}>{f.size} · จะ upload เมื่อกด Save</div>
                      </div>
                      <button onClick={() => removeNewFile(f.name)} title="ยกเลิก" style={{ background: 'none', border: 'none', cursor: 'pointer', color: 'var(--ink-4)', padding: 4, display: 'grid', placeItems: 'center' }}>
                        <Icon name="close" size={12}/>
                      </button>
                    </div>
                  ))}
                  {untypedDocs.length === 0 && newFiles.length === 0 && (
                    <div style={{ padding: '10px 14px', background: 'var(--bg-2)', borderRadius: 3, fontSize: 12, color: 'var(--ink-4)', textAlign: 'center', border: '1px dashed var(--line-3)' }}>
                      ยังไม่มีเอกสารอื่นๆ
                    </div>
                  )}
                </div>
              </div>
            </>
          )}
        </div>
      </div>
    </Modal>
  );
};

Object.assign(window, { OrderDetailView });
