// ===========================================================
// Customers screen — list + filters, opens Customer 360 drawer
// Role-aware: telesale role sees "Khách của tôi" + follow-up calendar
// ===========================================================

// Người phụ trách: đọc từ trường owner của khách (backend gán); chưa gán → 'Chưa gán'
const OWNERS = [];
const OWNER_MAP = {};
function ownerOf(c) {
  // Chấp nhận cả object khách hàng hoặc id
  if (c && typeof c === 'object') {
    if (c.owner) return c.owner;              // chủ sở hữu được gán tường minh
    return ownerOf(c.id);
  }
  const custId = c;
  if (OWNER_MAP[custId]) return OWNER_MAP[custId];
  return 'Chưa gán';
}

// Simple session store for notes & follow-ups — persisted to localStorage
// so demo data survives page refresh. In production this is a real backend.
// v2: đổi key vì "hôm nay" chuyển từ ngày pin cứng sang ngày thật — dữ liệu cũ đã sai ngữ nghĩa
const __TVS_STORE_KEY = 'tvs_crm_session_v2';
function __loadSession() {
  try {
    const raw = localStorage.getItem(__TVS_STORE_KEY);
    if (raw) {
      const s = JSON.parse(raw);
      // Lọc lịch quá hạn phi lý (>14 ngày trước) — tàn dư của phiên demo cũ
      const floor = Date.now() - 14*86400000;
      Object.keys(s.followupsByCustomer || {}).forEach(k => {
        s.followupsByCustomer[k] = (s.followupsByCustomer[k] || []).filter(f => !f.date || new Date(f.date).getTime() >= floor);
        if (!s.followupsByCustomer[k].length) delete s.followupsByCustomer[k];
      });
      return { notesByCustomer: s.notesByCustomer || {}, followupsByCustomer: s.followupsByCustomer || {}, ordersByCustomer: s.ordersByCustomer || {}, users: s.users || [], userEdits: s.userEdits || {}, doneFollowups: s.doneFollowups || {}, customers: s.customers || [], partners: s.partners || [], products: s.products || [], removedSkus: s.removedSkus || [], permissions: s.permissions || {} };
    }
  } catch (e) {}
  return { notesByCustomer: {}, followupsByCustomer: {}, ordersByCustomer: {}, users: [], userEdits: {}, doneFollowups: {}, customers: [], partners: [], products: [], removedSkus: [], permissions: {} };
}
window.TVS_SESSION = window.TVS_SESSION || Object.assign(__loadSession(), { _listeners: new Set(), __ver: 0 });
window.TVS_SESSION._persist = function() {
  try {
    localStorage.setItem(__TVS_STORE_KEY, JSON.stringify({
      notesByCustomer: this.notesByCustomer,
      followupsByCustomer: this.followupsByCustomer,
      ordersByCustomer: this.ordersByCustomer || {},
      users: this.users || [],
      userEdits: this.userEdits || {},
      doneFollowups: this.doneFollowups || {},
      customers: this.customers || [],
      partners: this.partners || [],
      products: this.products || [],
      removedSkus: this.removedSkus || [],
      permissions: this.permissions || {},
    }));
  } catch (e) {}
};
window.TVS_SESSION.subscribe = function(fn) {
  this._listeners.add(fn);
  return () => this._listeners.delete(fn);
};
window.TVS_SESSION.emit = function() {
  this.__ver = (this.__ver || 0) + 1;
  this._persist();
  this._listeners.forEach(fn => fn());
};
// Nhãn thời gian tương đối, tính lúc render (không lưu cứng "Vừa xong")
function __agoLabel(ts) {
  if (!ts) return 'Vừa xong';
  const m = Math.round((Date.now() - ts) / 60000);
  if (m < 1) return 'Vừa xong';
  if (m < 60) return `${m} phút trước`;
  const h = Math.round(m/60);
  if (h < 24) return `${h} giờ trước`;
  return `${Math.round(h/24)} ngày trước`;
}
window.__agoLabel = __agoLabel;
window.TVS_SESSION.addNote = function(custId, payload) {
  if (!this.notesByCustomer[custId]) this.notesByCustomer[custId] = [];
  this.notesByCustomer[custId].unshift({ id: 'N-' + Date.now(), ...payload, ts: Date.now() });
  this.emit();
};
window.TVS_SESSION.addFollowUp = function(custId, payload) {
  if (!this.followupsByCustomer[custId]) this.followupsByCustomer[custId] = [];
  this.followupsByCustomer[custId].unshift({ id: 'F-' + Date.now(), ...payload, ts: Date.now() });
  this.emit();
};
window.TVS_SESSION.addUser = function(payload) {
  if (!this.users) this.users = [];
  this.users.unshift({ id: 'S-' + Date.now(), ...payload, ts: Date.now() });
  this.emit();
};
// Admin SỬA tài khoản: tài khoản tạo trong app → sửa thẳng; tài khoản cài sẵn (accounts.js) → lưu phần chỉnh vào userEdits[id]
window.TVS_SESSION.updateUser = function(id, patch) {
  const u = (this.users || []).find(x => (x.id || ('S-' + (x.phone || x.email || x.name))) === id);
  if (u) Object.assign(u, patch);
  else { if (!this.userEdits) this.userEdits = {}; this.userEdits[id] = { ...(this.userEdits[id] || {}), ...patch }; }
  this.emit();
};
window.TVS_SESSION.addOrder = function(custId, payload) {
  if (!this.ordersByCustomer) this.ordersByCustomer = {};
  if (!this.ordersByCustomer[custId]) this.ordersByCustomer[custId] = [];
  const idDate = (payload.date || D.iso(0)).replace(/-/g, '').slice(0, 8);
  this.ordersByCustomer[custId].unshift({ id: 'DH-' + idDate + '-' + String(Date.now()).slice(-4), ...payload, ts: Date.now() });
  this.emit();
};
// Khách hàng / đối tác tạo trong app — lưu lại để sống qua reload (data.js reset mỗi lần tải)
window.TVS_SESSION.addCustomer = function(record) {
  if (!this.customers) this.customers = [];
  this.customers.unshift(record);
  this.emit();
};
window.TVS_SESSION.addPartner = function(record) {
  if (!this.partners) this.partners = [];
  this.partners.unshift(record);
  this.emit();
};
// Import nhiều khách một lần (từ file CSV/Excel) — 1 lần persist + 1 lần render
window.TVS_SESSION.addCustomers = function(records) {
  if (!this.customers) this.customers = [];
  this.customers.unshift(...records);
  this.emit();
};
// Sản phẩm thêm/xoá trong app — data.js reset mỗi lần tải nên lưu lại đây.
// Xoá sản phẩm mẫu: ghi tombstone SKU; xoá sản phẩm tự thêm: bỏ khỏi danh sách.
window.TVS_SESSION.addProduct = function(p) {
  if (!this.products) this.products = [];
  this.products.unshift(p);
  this.emit();
};
// Quản lý lưu ma trận phân quyền — map { roleId: [permId, ...] }; {} = dùng mặc định
window.TVS_SESSION.setPermissions = function(map) {
  this.permissions = map || {};
  this.emit();
};
window.TVS_SESSION.removeProduct = function(sku) {
  const wasAdded = (this.products || []).some(x => x.sku === sku);
  if (wasAdded) {
    this.products = this.products.filter(x => x.sku !== sku);
  } else {
    if (!this.removedSkus) this.removedSkus = [];
    if (!this.removedSkus.includes(sku)) this.removedSkus.push(sku);
  }
  this.emit();
};
// Đánh dấu follow-up hiện tại của khách là ĐÃ XONG (tombstone theo ngày hẹn)
window.TVS_SESSION.completeFollowUp = function(custId, due) {
  if (!this.doneFollowups) this.doneFollowups = {};
  this.doneFollowups[custId + '|' + due] = true;
  this.emit();
};
// HOÃN follow-up hiện tại: đánh dấu bản cũ đã xử lý + tạo lịch mới
window.TVS_SESSION.postponeFollowUp = function(custId, oldDue, newFollowUp) {
  if (!this.doneFollowups) this.doneFollowups = {};
  this.doneFollowups[custId + '|' + oldDue] = true;
  this.addFollowUp(custId, newFollowUp); // addFollowUp tự emit + persist
};

// Replay đơn đã ghi trong các phiên trước vào chỉ số khách/đối tác trong bộ nhớ —
// data.js reset về số liệu mẫu mỗi lần tải, còn đơn ghi nhận thì nằm lại localStorage
(function __replaySessionOrders() {
  const S = window.TVS_SESSION;
  if (S.__replayed) return;
  S.__replayed = true;
  // 0) Sản phẩm: gỡ SKU đã xoá + khôi phục sản phẩm đã thêm ở phiên trước
  (S.removedSkus || []).forEach(sku => {
    const i = D.products.findIndex(p => p.sku === sku);
    if (i >= 0) D.products.splice(i, 1);
  });
  (S.products || []).slice().reverse().forEach(p => {
    if (!D.products.find(x => x.sku === p.sku)) D.products.unshift({ ...p });
  });
  // 1) Khôi phục khách/đối tác đã tạo ở phiên trước (trước khi cộng dồn đơn)
  (S.customers || []).slice().reverse().forEach(c => {
    // Hạng lưu từ phiên cũ có thể là tên tiếng Anh (Bronze/Gold…) → quy về tên tiếng Việt thống nhất
    const VI_TIER = { bronze: 'Đồng', silver: 'Bạc', gold: 'Vàng', platinum: 'Kim cương', 'bạch kim': 'Kim cương' };
    const tier = VI_TIER[String(c.tier || '').toLowerCase()] || c.tier || 'Đồng';
    if (!D.customers.find(x => x.id === c.id)) D.customers.unshift({ ...c, tier, ltv: 0, orders: 0, avgOrder: 0 });
  });
  (S.partners || []).slice().reverse().forEach(p => {
    if (!D.partners.find(x => x.id === p.id)) D.partners.unshift({ ...p, gmv30d: 0, gmvYTD: 0, commissionEarned: 0 });
  });
  // 2) Cộng dồn đơn đã ghi vào chỉ số
  Object.keys(S.ordersByCustomer || {}).forEach(id => {
    const list = S.ordersByCustomer[id] || [];
    if (!list.length) return;
    const sum = list.reduce((a, o) => a + (o.total || 0), 0);
    const c = D.customers.find(x => x.id === id);
    if (c) {
      c.orders = (c.orders || 0) + list.length;
      c.ltv = (c.ltv || 0) + sum;
      c.avgOrder = c.orders ? Math.round(c.ltv / c.orders) : 0;
      const newest = list.map(o => o.date).filter(Boolean).sort().pop();
      if (newest && (!c.lastOrder || newest > c.lastOrder)) c.lastOrder = newest;
      return;
    }
    const p = D.partners.find(x => x.id === id);
    if (p) {
      p.gmvYTD = (p.gmvYTD || 0) + sum;
      const cutoff = D.iso(-30);
      const sum30 = list.filter(o => (o.date || '') >= cutoff).reduce((a, o) => a + (o.total || 0), 0);
      p.gmv30d = (p.gmv30d || 0) + sum30;
      p.commissionEarned = Math.round((p.commissionEarned || 0) + sum * (p.commission || 0) / 100);
    }
  });
})();
function useTvsSession() {
  const [, setN] = useState(0);
  useEffect(() => window.TVS_SESSION.subscribe(() => setN(n => n+1)), []);
  return window.TVS_SESSION;
}
// Lịch follow-up hiệu lực: lịch vừa đặt trong phiên đè lên lịch mẫu.
// Lịch đã bấm "Đã xong"/"Hoãn" (tombstone trong doneFollowups) không hiện lại.
function effFollowUp(c) {
  const done = window.TVS_SESSION.doneFollowups || {};
  const s = (window.TVS_SESSION.followupsByCustomer[c.id] || []).find(f => !done[c.id + '|' + f.date]);
  if (s) return { due: s.date, reason: s.reason, time: s.time, fresh: true };
  if (c.followUp && done[c.id + '|' + c.followUp.due]) return null;
  return c.followUp;
}
window.effFollowUp = effFollowUp;
window.ownerOf = ownerOf;

// Mốc "Liên hệ cuối" mẫu của khách demo — dùng chung cho cột trong bảng
const CONTACT_MOCK = {}; // Lần liên hệ cuối — backend cung cấp

// Tag hiển thị Loại khách hàng — 1 khách có thể thuộc nhiều loại
const TYPE_META = {
  retail:            ['Khách lẻ',        'outline'],
  doctor:            ['Bác sĩ',          'success'],
  pharmacist:        ['Dược sĩ',         'success'],
  affiliate_tiktok:  ['TikTok Aff.',    'info'],
  affiliate_shopee:  ['Shopee Aff.',    'warning'],
  koc:               ['KOC',            'danger'],
  investor:          ['Chủ đầu tư',     'gold'],
  pharmacy:          ['Nhà thuốc',      'success'],
  gym:               ['Phòng gym / PT',  'info'],
  spa:               ['Spa / PK',       'danger'],
  dealer:            ['Đại lý / Mua sỉ', 'platinum'],
  business:          ['Doanh nghiệp',   'platinum'],
  partner:           ['Đối tác B2B',    'info'],
};
window.TYPE_META = TYPE_META;

// Nhãn ĐẦY ĐỦ của loại khách (không viết tắt) — ưu tiên danh mục typeOptions trong data.js
function typeFull(id) {
  const o = (D.typeOptions || []).find(t => t.id === id);
  return o ? o.label : (TYPE_META[id] ? TYPE_META[id][0] : id);
}
// Danh sách loại của 1 khách: loại chính (segment) đứng đầu + các loại phụ (roles)
function customerTypes(c) {
  const all = [c.segment, ...(c.roles || [])].filter(t => t && (TYPE_META[t] || (D.typeOptions || []).some(o => o.id === t)));
  return all.length ? all : ['retail'];
}
// Tag loại khách nhãn đầy đủ — dùng trong hồ sơ 360° (lg = cỡ to cho dải nhận diện)
function TypeTagsFull({ customer, lg }) {
  const style = lg ? {fontSize:12, padding:'4px 12px'} : undefined;
  return (
    <span style={{display:'inline-flex', gap:6, flexWrap:'wrap'}}>
      {customerTypes(customer).map(t => (
        <Tag key={t} variant={TYPE_META[t] ? TYPE_META[t][1] : 'outline'} style={style}>{typeFull(t)}</Tag>
      ))}
    </span>
  );
}
window.typeFull = typeFull;
window.customerTypes = customerTypes;
window.TypeTagsFull = TypeTagsFull;

function SegmentTag({ segment, roles }) {
  const all = [segment, ...(roles || [])].filter(t => TYPE_META[t]);
  if (!all.length) return <Tag variant="outline">Khách lẻ</Tag>;
  return (
    <span style={{display:'inline-flex',gap:4,flexWrap:'wrap'}}>
      {all.map(t => <Tag key={t} variant={TYPE_META[t][1]}>{TYPE_META[t][0]}</Tag>)}
    </span>
  );
}
window.SegmentTag = SegmentTag;

function CustomersScreen({ role, onOpenCustomer, onGoToTelesale, onLogOrder, onAddCustomer }) {
  const session = useTvsSession(); // re-render khi có ghi chú / lịch follow-up mới
  const sessVer = session.__ver || 0; // đổi mỗi lần session ghi — dùng làm dependency cho memo
  const isTelesale = role.id === 'telesale';
  const isInstore  = role.id === 'instore';
  const isManager  = role.id === 'manager';
  // Giao diện thống nhất "Khách của tôi" cho nhân viên; quản lý xem toàn bộ
  const isOperator = !isManager;
  const myName = role.person;

  // Quick-action popups (note / follow-up)
  const [quickAction, setQuickAction] = useState(null); // { type: 'note'|'followup'|'outcome', customer }
  const [selectedPartner, setSelectedPartner] = useState(null); // đối tác B2B trong bảng gộp
  const [showImport, setShowImport] = useState(false); // modal nhập khách từ file CSV/Excel
  // Sheets-style: sắp xếp cột, chọn nhiều dòng, sửa trực tiếp trên ô
  const [sort, setSort] = useState({ key: null, dir: 'asc' });
  const [sel, setSel] = useState([]);
  const [editCell, setEditCell] = useState(null); // { id, field }
  const [edits, setEdits] = useState({});         // { [id]: { owner, note } }
  const [moreOpen, setMoreOpen] = useState(false);

  // Đối tác B2B hiển thị chung trong bảng khách hàng (1 danh sách duy nhất)
  const partnerRows = useMemo(() => D.partners.map(p => ({
    _partner: p,
    id: p.id, name: p.name, phone: p.phone, tier: p.tier,
    segment: 'partner', source: p.type, city: p.city, owner: p.owner,
    ltv: p.gmvYTD, orders: 0, lastOrder: null,
    tags: [p.stage],
    followUp: p.nextAction ? { due: p.nextAction.due, reason: p.nextAction.type } : null,
    preferredContact: 'Gọi điện',
  })), [D.partners.length]);

  const [search, setSearch] = useState('');
  const [tierFilter, setTierFilter] = useState('all');
  const [segment, setSegment] = useState(isManager ? 'all' : 'mine');
  // Admin: xem khách của TỪNG nhân viên ('' = tất cả). Nhân viên/Giám đốc không dùng.
  const [ownerFilter, setOwnerFilter] = useState('');
  const [custType, setCustType] = useState('all'); // loại khách (segment 2 trục)
  const [view, setView] = useState('list'); // list | followups | pipeline

  // Tab "Cần follow-up" mặc định sắp theo lịch gần nhất lên đầu (quá hạn → hôm nay → ngày mai…)
  // cho tới khi người dùng tự bấm sắp xếp cột khác.
  const effSort = (!sort.key && segment === 'followup') ? { key: 'follow', dir: 'asc' } : sort;
  // Danh sách nhân viên đang phụ trách khách — cho bộ lọc "Theo nhân viên" của Admin
  const ownerOptions = useMemo(() => [...new Set([...D.customers, ...partnerRows].map(c => ownerOf(c)).filter(Boolean))].sort((a, b) => a.localeCompare(b, 'vi')), [partnerRows, D.customers.length, sessVer]);

  const data = useMemo(() => {
    let r = [...D.customers, ...partnerRows];
    if (segment === 'mine' && myName)    r = r.filter(c => ownerOf(c) === myName);
    // "Chưa có lịch hẹn": khách KHÔNG có lịch follow-up nào để nhắc liên hệ — dễ bị bỏ quên.
    // Nhân viên xem trong phạm vi khách của mình, quản lý xem toàn hệ thống.
    if (segment === 'nofollowup') r = r.filter(c => (isManager || ownerOf(c) === myName) && !effFollowUp(c));
    if (segment === 'atrisk')  r = r.filter(c => c.tags.includes('At-risk'));
    if (segment === 'new')     r = r.filter(c => c.tags.includes('New'));
    if (segment === 'vip')     r = r.filter(c => ['platinum','gold'].includes(tierVariant(c.tier)));
    if (segment === 'followup')r = r.filter(c => effFollowUp(c));
    if (isManager && ownerFilter) r = r.filter(c => ownerOf(c) === ownerFilter);
    if (custType !== 'all') {
      if (custType === 'partner') r = r.filter(c => c._partner);
      else if (custType === 'affiliate') r = r.filter(c => [c.segment, ...(c.roles||[])].some(x => (x||'').startsWith('affiliate') || x === 'koc'));
      else if (custType === 'medical') r = r.filter(c => [c.segment, ...(c.roles||[])].some(x => x === 'doctor' || x === 'pharmacist' || x === 'pharmacy'));
      else if (custType === 'business') r = r.filter(c => [c.segment, ...(c.roles||[])].some(x => x === 'business' || x === 'gym' || x === 'spa' || x === 'dealer'));
      else r = r.filter(c => c.segment === custType || (c.roles||[]).includes(custType));
    }
    // tierVariant quy cả hai hệ tên hạng (Gold/Vàng, Platinum/Bạch kim…) về 1 chuẩn
    if (tierFilter !== 'all') r = r.filter(c => tierVariant(c.tier) === tierFilter);
    if (search) {
      const s = searchNorm(search);
      const digits = phoneDigits(search);
      r = r.filter(c => searchNorm(c.name).includes(s) || c.id.toLowerCase().includes(search.toLowerCase()) ||
        (digits.length >= 3 && phoneDigits(c.phone).includes(digits)) || searchNorm(c.email).includes(s));
    }
    if (effSort.key) {
      const dir = effSort.dir === 'asc' ? 1 : -1;
      const val = (c) => {
        if (effSort.key === 'name')  return c.name.toLowerCase();
        if (effSort.key === 'ltv')   return c.ltv || 0;
        if (effSort.key === 'orders')return c.orders || 0;
        if (effSort.key === 'last')  return c.lastOrder || '';
        // Ngày + giờ hẹn — 'YYYY-MM-DD HH:MM' so sánh chuỗi là đúng thứ tự thời gian
        if (effSort.key === 'follow'){ const f = effFollowUp(c); return f ? f.due + ' ' + (f.time || '00:00') : '9999'; }
        if (effSort.key === 'owner') return (edits[c.id]?.owner || ownerOf(c)).toLowerCase();
        if (effSort.key === 'tier')  return ['Bronze','Đồng','Silver','Bạc','Gold','Vàng','Platinum','Bạch kim','Kim cương'].indexOf(c.tier);
        return '';
      };
      r = [...r].sort((a,b) => { const x = val(a), y = val(b); return x < y ? -dir : x > y ? dir : 0; });
    }
    return r;
    // D.customers.length + sessVer: khách mới thêm / follow-up mới lưu phải hiện ra ngay
  }, [search, tierFilter, segment, custType, ownerFilter, partnerRows, sort, edits, D.customers.length, sessVer]);

  const toggleSort = (key) => setSort(s => s.key === key ? { key, dir: s.dir === 'asc' ? 'desc' : 'asc' } : { key, dir: 'asc' });
  const SortTh = ({ k, children, align }) => (
    <th onClick={() => toggleSort(k)} className={`sheet-th-sort ${align === 'num' ? 'num' : ''}`}>
      <span>{children}</span>
      <i className={`sheet-caret ${effSort.key === k ? 'on ' + effSort.dir : ''}`}/>
    </th>
  );
  const setEdit = (id, field, value) => {
    setEdits(e => ({ ...e, [id]: { ...(e[id]||{}), [field]: value } }));
    // Ghi thẳng vào bản ghi để mọi màn hình (360°, tìm kiếm, "Của tôi") thấy cùng dữ liệu
    const cust = D.customers.find(x => x.id === id);
    const part = cust ? null : D.partners.find(x => x.id === id);
    if (cust) cust[field] = value;
    else if (part) part[field] = value;
  };
  const allIds = data.map(c => c.id);
  const allSelected = sel.length > 0 && sel.length === allIds.length;
  const toggleAll = () => setSel(allSelected ? [] : allIds);
  const toggleOne = (id) => setSel(s => s.includes(id) ? s.filter(x => x !== id) : [...s, id]);
  // Bỏ chọn khi đổi bộ lọc — tránh thao tác hàng loạt lên dòng không còn hiển thị
  useEffect(() => { setSel([]); }, [search, segment, custType, tierFilter, ownerFilter]);

  // Operator KPIs computed from data
  const myScope = isManager ? [...D.customers, ...D.partners.map(p => ({...p, followUp: p.nextAction ? {due:p.nextAction.due, reason:p.nextAction.type} : null}))].filter(c => !ownerFilter || ownerOf(c) === ownerFilter)
                            : [...D.customers, ...partnerRows].filter(c => ownerOf(c) === myName);
  const myCustomers = myScope;
  const myFollowUpsToday = myCustomers.filter(c => { const f = effFollowUp(c); return f && D.parseISO(f.due) <= D.today; });

  return (
    <>
      <div className="page-header">
        <div>
          {isOperator && <div className="eyebrow">{role.team}</div>}
          {/* Admin: ghi RÕ phạm vi đang xem — toàn hệ thống hay khách của 1 nhân viên */}
          {isManager && <div className="eyebrow" style={{color:'var(--status-sale)'}}>Admin · {ownerFilter ? 'Khách của 1 nhân viên' : 'Toàn hệ thống'}</div>}
          <h1>{isOperator ? 'Khách của tôi' : ownerFilter ? `Khách của ${ownerFilter}` : 'Khách hàng — Toàn hệ thống'}</h1>
          <div className="sub">
            {isOperator
              ? <>Bạn đang phụ trách <b style={{color:'var(--vs-dark-blue)'}}>{myCustomers.length}</b> khách hàng · <b style={{color:'var(--status-sale)'}}>{myFollowUpsToday.length} cần follow-up hôm nay</b></>
              : ownerFilter
                ? <>Đang xem <b style={{color:'var(--vs-dark-blue)'}}>{myCustomers.length}</b> khách do <b>{ownerFilter}</b> phụ trách · <b style={{color:'var(--status-sale)'}}>{myFollowUpsToday.length} cần follow-up hôm nay</b></>
                : <>Khách của <b>tất cả nhân viên</b> · {D.customers.length} khách hàng + {D.partners.length} đối tác · <b style={{color:'var(--status-sale)'}}>{myFollowUpsToday.length} cần follow-up hôm nay</b></>}
          </div>
        </div>
        <div className="row gap-2">
          <Segmented value={view} options={[
            {value:'list',     label:'Danh sách'},
            {value:'followups',label:'Lịch Follow-up'},
          ]} onChange={setView}/>
          {tvsCan(role, 'customers.import') && (
            <button className="btn btn-secondary" onClick={() => setShowImport(true)}><Icon name="upload" size={14}/>Nhập từ file</button>
          )}
          {tvsCan(role, 'customers.edit') && (
            <button className="btn btn-cta" onClick={onAddCustomer}><Icon name="plus" size={14}/>Thêm khách hàng</button>
          )}
        </div>
      </div>

      {/* KPI — cùng bố cục cho mọi vai trò, chỉ khác phạm vi dữ liệu (RBAC) */}
      <div className="kpi-grid">
        <KPI feature label={isManager ? (ownerFilter ? 'Khách của nhân viên này' : 'Khách toàn hệ thống') : 'Khách đang phụ trách'} value={myCustomers.length} sub={isManager && ownerFilter ? ownerFilter : role.team}/>
        <KPI label="Cần follow-up hôm nay" value={myFollowUpsToday.length} delta={myFollowUpsToday.length > 0 ? `${myFollowUpsToday.length} đến hạn` : 'Không có'} deltaDir={myFollowUpsToday.length > 0 ? 'up' : 'flat'}/>
        <KPI label="Đã liên hệ tuần này" value="0"/>
        <KPI label="Đơn ghi nhận tháng này" value="0"/>
      </div>

      {/* ===== VIEW: LIST — bảng kiểu Google Sheets ===== */}
      {view === 'list' && (
      <Card pad={false}>
        {/* Hàng lọc 1: tìm kiếm + phạm vi + lọc thêm */}
        <div style={{padding:'var(--space-3) var(--space-4)',display:'flex',gap:'var(--space-3)',alignItems:'center',borderBottom:'1px solid var(--border-1)',flexWrap:'wrap'}}>
          <div className="topbar-search" style={{flex:'0 1 300px'}}>
            <Icon name="search" size={14} style={{color:'var(--fg-muted)'}}/>
            <input placeholder="Tìm tên, SĐT, mã khách..." value={search} onChange={(e)=>setSearch(e.target.value)}/>
          </div>
          <Segmented value={segment} options={[
            {value:'mine',       label:'Của tôi'},
            {value:'nofollowup', label:'Chưa có lịch hẹn'},
            {value:'followup',   label:'Cần follow-up'},
            {value:'atrisk',  label:'Nguy cơ'},
            {value:'vip',     label:'VIP'},
            {value:'all',     label:'Toàn bộ'},
          ].filter(o => !(isManager && o.value === 'mine'))} onChange={setSegment}/>
          {/* Admin: xem khách của từng tài khoản nhân viên */}
          {isManager && (
            <select className="select" value={ownerFilter} onChange={(e) => setOwnerFilter(e.target.value)} title="Xem khách của từng nhân viên"
              style={{width:'auto',padding:'5px 24px 5px 8px',fontSize:'var(--t-xs)',fontWeight:700,color: ownerFilter ? 'var(--vs-dark-blue)' : 'var(--fg-2)',borderColor: ownerFilter ? 'var(--vs-light-blue)' : undefined}}>
              <option value="">👤 Tất cả nhân viên</option>
              {ownerOptions.map(o => <option key={o} value={o}>{o}</option>)}
            </select>
          )}
          <div style={{position:'relative',marginLeft:'auto'}}>
            <button className={tierFilter !== 'all' ? 'btn btn-cta btn-sm' : 'btn btn-secondary btn-sm'} onClick={() => setMoreOpen(o => !o)}>
              <Icon name="filter" size={13}/>Lọc thêm{tierFilter !== 'all' ? ' (1)' : ''}
            </button>
            {moreOpen && (
              <>
                <div style={{position:'fixed',inset:0,zIndex:40}} onClick={() => setMoreOpen(false)}/>
                <div className="pop-sheet" style={{position:'absolute',right:0,top:'calc(100% + 6px)',zIndex:41,background:'var(--bg-page)',border:'1px solid var(--border-1)',borderRadius:'var(--radius-md)',boxShadow:'var(--shadow-3)',padding:'var(--space-4)',minWidth:230}}>
                  <div className="field-label">Hạng thành viên</div>
                  <select className="select" value={tierFilter} onChange={(e) => setTierFilter(e.target.value)}>
                    <option value="all">Tất cả hạng</option>
                    <option value="platinum">Kim cương</option>
                    <option value="gold">Vàng</option>
                    <option value="silver">Bạc</option>
                    <option value="bronze">Đồng</option>
                  </select>
                  <button className="btn btn-ghost btn-sm mt-3" style={{width:'100%'}} onClick={() => { setTierFilter('all'); setMoreOpen(false); }}>Xóa bộ lọc</button>
                </div>
              </>
            )}
          </div>
        </div>

        {/* Hàng lọc 2: Loại khách */}
        <div style={{padding:'var(--space-2) var(--space-4)',display:'flex',gap:'var(--space-2)',alignItems:'center',borderBottom:'1px solid var(--border-1)',flexWrap:'wrap'}}>
          <span className="text-xs fg-muted" style={{fontWeight:700}}>Loại khách:</span>
          {[
            {value:'all',        label:'Tất cả'},
            {value:'retail',     label:'🛒 Khách lẻ'},
            {value:'investor',   label:'🏗 Chủ đầu tư'},
            {value:'medical',    label:'🩺 Bác sĩ / Dược sĩ'},
            {value:'affiliate',  label:'📱 Affiliate'},
            {value:'business',   label:'🏢 Doanh nghiệp'},
            {value:'partner',    label:'🤝 Đối tác B2B'},
          ].map(o => (
            <button key={o.value}
              className={custType === o.value ? 'btn btn-cta btn-sm' : 'btn btn-ghost btn-sm'}
              onClick={() => setCustType(o.value)}>
              {o.label}
              <span style={{opacity:0.65,marginLeft:2}}>
                {(() => {
                  const has = (c, ...ids) => [c.segment, ...(c.roles||[])].some(x => ids.includes(x));
                  // Admin đang lọc theo 1 nhân viên → đếm trong phạm vi nhân viên đó
                  const CS = (isManager && ownerFilter) ? D.customers.filter(c => ownerOf(c) === ownerFilter) : D.customers;
                  const PS = (isManager && ownerFilter) ? D.partners.filter(p => ownerOf(p) === ownerFilter) : D.partners;
                  if (o.value === 'all')       return CS.length + PS.length;
                  if (o.value === 'partner')   return PS.length;
                  if (o.value === 'medical')   return CS.filter(c => has(c,'doctor','pharmacist','pharmacy')).length;
                  if (o.value === 'affiliate') return CS.filter(c => has(c,'affiliate_tiktok','affiliate_shopee','koc')).length;
                  if (o.value === 'business')  return CS.filter(c => has(c,'business','gym','spa','dealer')).length;
                  return CS.filter(c => has(c, o.value)).length;
                })()}
              </span>
            </button>
          ))}
        </div>

        {/* Thanh thao tác hàng loạt — hiện khi chọn dòng */}
        {sel.length > 0 && (
          <div className="bulkbar">
            <span className="bold">Đã chọn {sel.length} khách</span>
            <div className="row gap-2" style={{marginLeft:'auto'}}>
              <button className="btn btn-sm btn-secondary" onClick={() => window.tvsDemo('Gán người phụ trách cho ' + sel.length + ' khách')}><Icon name="user" size={12}/>Gán phụ trách</button>
              <button className="btn btn-sm btn-secondary" onClick={() => window.tvsDemo('Gắn tag cho ' + sel.length + ' khách')}><Icon name="flag" size={12}/>Gắn tag</button>
              <button className="btn btn-sm btn-secondary" onClick={() => window.tvsDemo('Đặt follow-up cho ' + sel.length + ' khách')}><Icon name="clock" size={12}/>Đặt follow-up</button>
              <button className="btn btn-sm btn-ghost" onClick={() => setSel([])}><Icon name="close" size={12}/>Bỏ chọn</button>
            </div>
          </div>
        )}

        <div className="table-wrap">
          <table className="tbl sheet tbl--cust">
            <thead>
              <tr>
                <th className="sheet-check">
                  <input type="checkbox" checked={allSelected} onChange={toggleAll} aria-label="Chọn tất cả"/>
                </th>
                <SortTh k="name">Khách hàng</SortTh>
                <th>Loại</th>
                <SortTh k="tier">Hạng</SortTh>
                <th>SĐT</th>
                <SortTh k="ltv" align="num">LTV</SortTh>
                <SortTh k="last">Mua cuối</SortTh>
                <th>Liên hệ cuối</th>
                <SortTh k="follow">Follow-up</SortTh>
                <SortTh k="owner">Phụ trách</SortTh>
                <th>Ghi chú</th>
                <th className="sheet-actions-th"></th>
              </tr>
            </thead>
            <tbody>
              {data.map(c => {
                const owner = edits[c.id]?.owner || ownerOf(c);
                const isMine = owner === myName;
                const note = edits[c.id]?.note ?? (c.note || '');
                const checked = sel.includes(c.id);
                return (
                <tr key={c.id} className={checked ? 'selected' : ''}
                    onClick={() => c._partner ? setSelectedPartner(c._partner) : onOpenCustomer(c)}>
                  <td className="sheet-check" onClick={(e) => e.stopPropagation()}>
                    <input type="checkbox" checked={checked} onChange={() => toggleOne(c.id)} aria-label={'Chọn ' + c.name}/>
                  </td>
                  <td>
                    <div className="row gap-2">
                      <span className={`tier-dot ${tierVariant(c.tier)}`} title={c.tier}/>
                      <div style={{minWidth:0}}>
                        <div className="strong" style={{whiteSpace:'nowrap'}}>{c.name}</div>
                        <div className="text-xs fg-muted" style={{whiteSpace:'nowrap'}}>{c._partner ? c._partner.contact : `${c.id} · ${c.city}`}</div>
                      </div>
                    </div>
                  </td>
                  <td>
                    {c._partner
                      ? <Tag variant="info">🤝 {c._partner.type}</Tag>
                      : <SegmentTag segment={c.segment} roles={c.roles}/>}
                  </td>
                  <td><Tag variant={tierVariant(c.tier)}>{c.tier}</Tag></td>
                  <td className="tnum text-sm">{c.phone}</td>
                  <td className="num strong">{fmt.vndFull(c.ltv)}</td>
                  <td className="text-sm fg-muted">{fmt.rel(c.lastOrder)}</td>
                  <td className="text-sm fg-muted">
                    {(() => {
                      // Liên hệ cuối = ghi chú kết quả liên hệ mới nhất trong phiên; nếu chưa có, dùng mốc mẫu
                      const contact = (session.notesByCustomer[c.id] || []).find(n => n.tag === 'contact');
                      if (contact) return __agoLabel(contact.ts);
                      return CONTACT_MOCK[c.id] || '—';
                    })()}
                  </td>
                  <td>
                    {(() => {
                      const f = effFollowUp(c);
                      if (!f) return <span className="text-xs fg-muted">—</span>;
                      const overdue = D.parseISO(f.due) <= D.today;
                      return (
                        <span title={f.reason}>
                          <Tag variant={f.fresh ? 'info' : overdue ? 'danger' : 'warning'}>
                            {f.fresh && <Icon name="check" size={10} stroke={3} style={{marginRight:2}}/>}
                            {fmt.rel(f.due)}{f.time ? ` ${f.time}` : ''}
                          </Tag>
                        </span>
                      );
                    })()}
                  </td>
                  {/* Sửa trực tiếp: người phụ trách */}
                  <td className="sheet-edit" onClick={(e) => { e.stopPropagation(); if (!tvsCan(role, 'customers.assign')) return window.tvsToast('Chỉ Admin / Marketing mới được đổi người phụ trách', 'demo'); setEditCell({id:c.id, field:'owner'}); }}>
                    {editCell && editCell.id === c.id && editCell.field === 'owner' ? (
                      <select className="sheet-input" autoFocus value={owner}
                        onChange={(e) => { setEdit(c.id, 'owner', e.target.value); setEditCell(null); }}
                        onBlur={() => setEditCell(null)}>
                        {[...new Set([owner, ...window.tvsSalesStaff()])].map(o => <option key={o} value={o}>{o}</option>)}
                      </select>
                    ) : isMine
                      ? <Tag variant="info" dot>Bạn</Tag>
                      : <span className="text-xs fg-muted">{owner.split(' ').slice(-2).join(' ')}</span>}
                  </td>
                  {/* Sửa trực tiếp: ghi chú */}
                  <td className="sheet-edit sheet-note" onClick={(e) => { e.stopPropagation(); if (!tvsCan(role, 'customers.edit')) return window.tvsDenied(); setEditCell({id:c.id, field:'note'}); }}>
                    {editCell && editCell.id === c.id && editCell.field === 'note' ? (
                      <input className="sheet-input" autoFocus defaultValue={note}
                        placeholder="Nhập ghi chú..."
                        onBlur={(e) => { setEdit(c.id, 'note', e.target.value); setEditCell(null); }}
                        onKeyDown={(e) => { if (e.key === 'Enter') e.currentTarget.blur(); if (e.key === 'Escape') setEditCell(null); }}/>
                    ) : (
                      <span className={note ? 'text-sm' : 'text-xs fg-muted'} title={note}>{note || 'Nhấp để ghi…'}</span>
                    )}
                  </td>
                  <td onClick={(e) => e.stopPropagation()} style={{textAlign:'right'}}>
                    <div className="row gap-1" style={{justifyContent:'flex-end'}}>
                      <button className="btn btn-icon btn-sm btn-cta" title="Ghi kết quả liên hệ" onClick={() => setQuickAction({type:'outcome', customer: c})}>
                        <Icon name="phone" size={13}/>
                      </button>
                      <button className="btn btn-icon btn-sm btn-secondary" title="Ghi nhận đơn hàng" onClick={() => onLogOrder && onLogOrder(c)}>
                        <Icon name="receipt" size={13}/>
                      </button>
                      <button className="btn btn-icon btn-sm btn-secondary" title="Đặt lịch follow-up" onClick={() => setQuickAction({type:'followup', customer: c})}>
                        <Icon name="clock" size={13}/>
                      </button>
                      <button className="btn btn-icon btn-sm btn-ghost" title="Xem hồ sơ 360°" onClick={() => c._partner ? setSelectedPartner(c._partner) : onOpenCustomer(c)}>
                        <Icon name="eye" size={13}/>
                      </button>
                    </div>
                  </td>
                </tr>
              );})}
            </tbody>
          </table>
          {data.length === 0 && (
            <EmptyState icon="search" title="Không tìm thấy khách nào"
              desc="Thử đổi từ khoá tìm kiếm hoặc bỏ bớt bộ lọc."
              action={<button className="btn btn-secondary btn-sm" onClick={() => { setSearch(''); setSegment('all'); setCustType('all'); setTierFilter('all'); }}>Xoá tất cả bộ lọc</button>}/>
          )}
        </div>
        <div style={{padding:'var(--space-3) var(--space-5)',display:'flex',justifyContent:'space-between',alignItems:'center',borderTop:'1px solid var(--border-1)',flexWrap:'wrap',gap:8}}>
          <span className="text-sm fg-muted">Hiển thị {data.length} dòng · nhấp tiêu đề cột để sắp xếp · nhấp ô Phụ trách / Ghi chú để sửa tại chỗ</span>
        </div>
      </Card>
      )}

      {/* ===== VIEW: FOLLOW-UPS CALENDAR (operator) ===== */}
      {/* Admin cũng xem được lịch toàn hệ thống — FollowUpView tự scope theo vai trò */}
      {view === 'followups' && (
        <FollowUpView myName={myName} role={role} onOpenCustomer={onOpenCustomer} onLogOutcome={(c) => setQuickAction({type:'outcome', customer: c})} onLogOrder={onLogOrder}/>
      )}

      {/* ===== VIEW: PIPELINE — đã bỏ (20/07/2026): nhân viên bán lẻ không dùng kanban ===== */}

      {/* ===== Quick-action modal ===== */}
      {quickAction && (
        <QuickActionModal
          mode={quickAction.type}
          customer={quickAction.customer}
          role={role}
          onClose={() => setQuickAction(null)}
        />
      )}

      {/* ===== Partner drawer (đối tác trong bảng gộp) ===== */}
      {selectedPartner && window.PartnerDrawer && (
        <PartnerDrawer partner={selectedPartner} onClose={() => setSelectedPartner(null)} onLogOrder={onLogOrder}/>
      )}

      {/* ===== Import khách hàng từ file CSV / Excel ===== */}
      {showImport && window.ImportCustomersModal && (
        <ImportCustomersModal role={role} onClose={() => setShowImport(false)}/>
      )}
    </>
  );
}

// ===== Follow-up calendar / timeline view =====
function FollowUpView({ myName, role, onOpenCustomer, onLogOutcome, onLogOrder }) {
  const session = useTvsSession(); // re-render khi đặt lịch mới / hoãn / hoàn thành
  const isManager = role && role.id === 'manager';
  // Quản lý xem lịch của cả team; nhân viên xem lịch của mình
  const myCustomers = D.customers.filter(c => (isManager || ownerOf(c) === myName) && effFollowUp(c))
    .map(c => ({ ...c, followUp: effFollowUp(c) }))
    // Gần nhất lên đầu trong từng nhóm (ngày + giờ)
    .sort((a, b) => (a.followUp.due + ' ' + (a.followUp.time || '00:00')).localeCompare(b.followUp.due + ' ' + (b.followUp.time || '00:00')));
  const groups = {
    overdue: [],
    today: [],
    tomorrow: [],
    week: [],
    later: [],
  };
  myCustomers.forEach(c => {
    const d = D.parseISO(c.followUp.due);
    const diff = Math.round((d - D.today) / 86400000);
    if (diff < 0) groups.overdue.push(c);
    else if (diff === 0) groups.today.push(c);
    else if (diff === 1) groups.tomorrow.push(c);
    else if (diff <= 7) groups.week.push(c);
    else groups.later.push(c);
  });

  const postpone = (c) => {
    window.TVS_SESSION.postponeFollowUp(c.id, c.followUp.due, {
      reason: c.followUp.reason, date: D.iso(1), time: c.followUp.time || '10:00',
      channel: c.preferredContact || 'Gọi điện', who: role ? role.person : myName, role: role ? role.team : '',
    });
    window.tvsToast(`Đã hoãn follow-up của ${c.name} sang ngày mai`);
  };
  const complete = (c) => {
    window.TVS_SESSION.completeFollowUp(c.id, c.followUp.due);
    window.tvsToast(`Đã hoàn thành follow-up của ${c.name} ✓`);
  };

  const sections = [
    {key:'overdue', label: 'Quá hạn', variant: 'danger',  icon: 'flame'},
    {key:'today',   label: 'Hôm nay', variant: 'danger',  icon: 'flag'},
    {key:'tomorrow',label: 'Ngày mai',variant: 'warning', icon: 'clock'},
    {key:'week',    label: '7 ngày tới', variant: 'info', icon: 'calendar'},
    {key:'later',   label: 'Sau đó',  variant: 'outline', icon: 'calendar'},
  ];

  return (
    <Card pad={false}>
      <CardHeader title="Lịch follow-up của tôi"
        sub={`${myCustomers.length} khách cần liên hệ · sắp xếp theo deadline`}
        actions={
          <button className="btn btn-secondary btn-sm" onClick={() => window.tvsDemo('Nhắc tất cả follow-up quá hạn')}><Icon name="bell" size={12}/>Nhắc tôi</button>
        }/>
      <div className="card-pad">
        {myCustomers.length === 0 && (
          <EmptyState icon="check" title="Không còn follow-up nào chờ xử lý"
            desc="Tuyệt vời! Bạn đã xử lý hết lịch chăm sóc khách. Lịch mới sẽ hiện ở đây khi bạn đặt hẹn hoặc ghi kết quả liên hệ."/>
        )}
        {sections.map(s => {
          if (groups[s.key].length === 0) return null;
          return (
            <div key={s.key} style={{marginBottom: 24}}>
              <div className="row mb-3" style={{gap:10}}>
                <Tag variant={s.variant} dot><Icon name={s.icon} size={11} style={{marginRight:2}}/>{s.label}</Tag>
                <span className="text-xs fg-muted">{groups[s.key].length} khách</span>
                <div style={{flex:1,height:1,background:'var(--border-1)'}}/>
              </div>
              <div className="grid grid-2">
                {groups[s.key].map(c => (
                  <div key={c.id} className="pipeline-card" onClick={() => onOpenCustomer(c)} style={{margin:0}}>
                    <div className="row gap-3" style={{alignItems:'flex-start'}}>
                      <Avatar name={c.name} tier={tierVariant(c.tier)}/>
                      <div style={{flex:1,minWidth:0}}>
                        <div className="row gap-2">
                          <div className="strong fg-1">{c.name}</div>
                          <Tag variant={tierVariant(c.tier)}>{c.tier}</Tag>
                        </div>
                        <div className="text-xs fg-muted mt-1 tnum">{c.phone} · LTV {fmt.vndFull(c.ltv)}</div>
                        <div className="text-sm fg-2 mt-2" style={{lineHeight:1.5}}>
                          <Icon name="flag" size={12} style={{display:'inline-block',marginRight:4,color:'var(--vs-light-blue)'}}/>
                          {c.followUp.reason}
                        </div>
                        <div className="text-xs fg-muted mt-1 tnum" style={{paddingLeft:16}}>
                          Hẹn {fmt.date(c.followUp.due)}{c.followUp.time ? ` · ${c.followUp.time}` : ''} · {fmt.rel(c.followUp.due)}
                        </div>
                        <div className="row mt-3 gap-1" style={{flexWrap:'wrap'}} onClick={(e) => e.stopPropagation()}>
                          <button className="btn btn-cta btn-sm" onClick={() => onLogOutcome && onLogOutcome(c)}>
                            <Icon name="phone" size={12}/>Ghi kết quả liên hệ
                          </button>
                          <button className="btn btn-secondary btn-sm" onClick={() => onLogOrder && onLogOrder(c)}>
                            <Icon name="receipt" size={12}/>Ghi nhận đơn
                          </button>
                          <button className="btn btn-secondary btn-sm" title="Hoãn sang ngày mai" onClick={() => postpone(c)}>
                            <Icon name="clock" size={12}/>Hoãn
                          </button>
                          <button className="btn btn-ghost btn-sm" title="Đánh dấu đã hoàn thành" onClick={() => complete(c)}>
                            <Icon name="check" size={12}/>Đã xong
                          </button>
                          <span style={{flex:1}}/>
                          <span className="text-xs fg-muted" style={{whiteSpace:'nowrap'}}>{c.preferredContact}</span>
                        </div>
                      </div>
                    </div>
                  </div>
                ))}
              </div>
            </div>
          );
        })}
      </div>
    </Card>
  );
}

// ===========================================================
// Customer 360 — drawer
// ===========================================================
function Customer360({ customer, onClose, onCall, onLogOrder, onSchedule, role }) {
  const [tab, setTab] = useState('overview');
  const isInstore  = role && role.id === 'instore';
  const isTelesale = role && role.id === 'telesale';
  const session = useTvsSession();
  const sessionOrders = (session.ordersByCustomer && session.ordersByCustomer[customer.id]) || [];
  const orders = [...sessionOrders, ...(D.ordersByCustomer[customer.id] || [])];
  const sessionNotes     = session.notesByCustomer[customer.id]     || [];
  const sessionFollowUps = session.followupsByCustomer[customer.id] || [];
  const latestNote = sessionNotes[0];

  // 12 months of monthly spend (mock)
  const monthly = useMemo(() => {
    const seed = customer.id.charCodeAt(2);
    return Array.from({length: 12}, (_, i) => {
      const v = ((seed * (i+1) * 37) % 9) / 10 + 0.3;
      return Math.round(v * (customer.avgOrder/1000) * (1 + i/12));
    });
  }, [customer.id]);

  return (
    <div className="drawer-overlay" onClick={onClose}>
      <div className="drawer" onClick={(e) => e.stopPropagation()}>
        <div className="drawer-h">
          <Avatar name={customer.name} size="lg" tier={tierVariant(customer.tier)}/>
          <div style={{flex:1,minWidth:0}}>
            <div className="row gap-3">
              <h2 style={{color:'#fff',margin:0,fontFamily:'var(--font-display)',fontSize:24,fontWeight:700,whiteSpace:'nowrap',overflow:'hidden',textOverflow:'ellipsis'}}>{customer.name}</h2>
              <Tag variant={tierVariant(customer.tier)}>{customer.tier}</Tag>
            </div>
            <div className="row gap-4 mt-2" style={{color:'rgba(255,255,255,0.78)',fontSize:13,flexWrap:'wrap'}}>
              <span>📞 <span className="tnum">{customer.phone}</span></span>
              {customer.email && <span>✉ {customer.email}</span>}
              <span>📍 {customer.city}</span>
              {customer.age
                ? <span>🎂 {customer.gender}, {customer.age} tuổi</span>
                : (customer.gender && customer.gender !== '—') ? <span>👤 {customer.gender}</span> : null}
              {customer.affiliateId && <span>🔗 Mã aff: <span className="tnum">{customer.affiliateId}</span></span>}
            </div>
          </div>
          {/* Mọi vai trò có cùng bộ hành động */}
          <button className="btn btn-cta" onClick={onCall}>
            <Icon name="phone" size={14}/>Ghi kết quả liên hệ
          </button>
          <button className="btn btn-secondary" style={{background:'rgba(255,255,255,0.10)',color:'#fff',borderColor:'transparent'}} onClick={() => onLogOrder && onLogOrder(customer)}>
            <Icon name="receipt" size={14}/>Ghi nhận đơn
          </button>
          <button className="btn btn-secondary" style={{background:'rgba(255,255,255,0.10)',color:'#fff',borderColor:'transparent'}}
            onClick={() => window.tvsDemo('Nhắn Zalo cho ' + customer.name)}>
            <Icon name="chat" size={14}/>Zalo
          </button>
          <button className="close" onClick={onClose}>
            <Icon name="close" size={18}/>
          </button>
        </div>

        {/* Mobile: thanh hành động thay cho các nút header bị ẩn trên phone */}
        <div className="drawer-actions-mobile">
          <button className="btn btn-cta btn-sm" onClick={onCall}><Icon name="phone" size={13}/>Ghi kết quả</button>
          <button className="btn btn-secondary btn-sm" onClick={() => onLogOrder && onLogOrder(customer)}><Icon name="receipt" size={13}/>Ghi đơn</button>
          {onSchedule && <button className="btn btn-secondary btn-sm" onClick={onSchedule}><Icon name="calendar" size={13}/>Đặt lịch</button>}
          <button className="btn btn-secondary btn-sm" onClick={() => window.tvsDemo('Nhắn Zalo cho ' + customer.name)}><Icon name="chat" size={13}/>Zalo</button>
        </div>

        {/* Dải nhận diện & chỉ số chính — Loại khách hàng đứng đầu, có nhãn rõ ràng */}
        <div className="drawer-strip has-type">
          <div>
            <div className="cell-label">Loại khách hàng</div>
            <TypeTagsFull customer={customer} lg/>
          </div>
          {[
            {l:'LTV', v:fmt.vndFull(customer.ltv), dim:'tích luỹ'},
            {l:'Tổng đơn', v:customer.orders, dim: customer.lastOrder ? `gần nhất ${fmt.rel(customer.lastOrder)}` : 'chưa có đơn'},
            {l:'Khách từ', v: customer.joined ? fmt.date(customer.joined).slice(3) : '—', dim: 'nguồn ' + D.channelLabel(customer.source || customer.channel)},
          ].map((s,i) => (
            <div key={i}>
              <div className="cell-label">{s.l}</div>
              <div className="cell-value">{s.v}</div>
              {s.dim && <div className="text-xs fg-muted mt-2">{s.dim}</div>}
            </div>
          ))}
        </div>

        {/* Tabs */}
        <div className="tabs" style={{padding:'0 var(--space-6)'}}>
          {[
            ['overview','Tổng quan'],
            ['orders','Đơn hàng'],
            ['activity','Hoạt động'],
            ['notes','Ghi chú & Follow-up'],
          ].map(([k,l]) => (
            <button key={k} className={`tab ${tab===k?'active':''}`} onClick={()=>setTab(k)}>{l}</button>
          ))}
        </div>

        <div style={{padding:'var(--space-5) var(--space-6)'}}>
          {tab === 'overview' && (
            <>
              {(() => {
                // MỘT banner duy nhất: lịch hiệu lực (lịch vừa đặt trong phiên đè lên lịch mẫu).
                // Ngày giờ hẹn hiện RÕ: thứ + ngày đầy đủ + giờ, chữ đậm — khoảng cách tương đối chỉ là phụ chú.
                const f = effFollowUp(customer);
                if (!f) return null;
                const d = D.parseISO(f.due);
                const WEEKDAYS = ['Chủ nhật', 'Thứ 2', 'Thứ 3', 'Thứ 4', 'Thứ 5', 'Thứ 6', 'Thứ 7'];
                return (
                  <div className="banner mb-4">
                    <Icon name={f.fresh ? 'clock' : 'flag'} size={18}/>
                    <div style={{flex:1}}>
                      <div className="bold">Follow-up: {f.reason}</div>
                      <div className="mt-1" style={{fontSize:15,fontWeight:700,color:'var(--fg-1)',fontFamily:'var(--font-display)'}}>
                        📅 {WEEKDAYS[d.getDay()]}, {fmt.date(f.due)}{f.time ? ` · ${f.time}` : ''}
                        <span style={{marginLeft:8,color: d <= D.today ? 'var(--status-sale)' : 'var(--vs-light-blue)'}}>
                          — {fmt.rel(f.due)}
                        </span>
                        <span className="text-xs fg-muted" style={{fontWeight:400,fontFamily:'var(--font-sans)',marginLeft:8}}>
                          · Kênh ưa thích: {customer.preferredContact}
                        </span>
                      </div>
                    </div>
                    {f.fresh && <Tag variant="info" dot>Mới</Tag>}
                    <button className="btn btn-cta btn-sm" onClick={onCall}>Xử lý ngay</button>
                  </div>
                );
              })()}

              {/* Hàng 1 — Hồ sơ khách hàng (loại khách rõ ràng) + biểu đồ doanh số */}
              <div className="grid grid-5-7">
                <Card>
                  <h3 style={{margin:'0 0 var(--space-3)',fontSize:'var(--t-md)',color:'var(--fg-1)'}}>Hồ sơ khách hàng</h3>
                  <div className="kv">
                    <span className="k">Phụ trách</span>
                    <span className="v strong">{ownerOf(customer)}</span>
                  </div>
                  <div className="kv">
                    <span className="k">Liên hệ</span>
                    <span className="v" style={{display:'flex',gap:6,flexWrap:'wrap'}}>
                      {[['Gọi điện','call'],['SMS','sms'],['Email','email'],['Zalo','zalo']].map(([l,k]) => (
                        <Tag key={k} variant={(customer.consent || {})[k] ? 'success' : 'outline'}
                          style={(customer.consent || {})[k] ? undefined : {opacity:0.55}}>
                          {customer.preferredContact === l && '★ '}{l}{(customer.consent || {})[k] ? '' : ' ✕'}
                        </Tag>
                      ))}
                    </span>
                  </div>
                  {customer.contactName && (
                    <div className="kv">
                      <span className="k">Người liên hệ</span>
                      <span className="v">{customer.contactName}{customer.contactRole ? ` — ${customer.contactRole}` : ''}</span>
                    </div>
                  )}
                  {customer.taxId && (
                    <div className="kv">
                      <span className="k">Mã số thuế</span>
                      <span className="v tnum">{customer.taxId}</span>
                    </div>
                  )}
                  {customer.address && (
                    <div className="kv">
                      <span className="k">Địa chỉ</span>
                      <span className="v">{customer.address}</span>
                    </div>
                  )}
                  <div className="kv">
                    <span className="k">Tags</span>
                    <span className="v" style={{display:'flex',gap:6,flexWrap:'wrap',alignItems:'center'}}>
                      {(customer.tags || []).map(t => (
                        <Tag key={t} variant={t === 'At-risk' ? 'danger' : t === 'New' ? 'info' : 'outline'}>{t}</Tag>
                      ))}
                      <button className="btn btn-ghost btn-sm" onClick={() => window.tvsDemo('Thêm tag')}><Icon name="plus" size={12}/>Thêm tag</button>
                    </span>
                  </div>
                  <div className="kv">
                    <span className="k">Ghi chú mới nhất</span>
                    <span className="v">
                      {(latestNote || customer.note) ? (
                        <>
                          <span style={{display:'-webkit-box',WebkitLineClamp:2,WebkitBoxOrient:'vertical',overflow:'hidden',lineHeight:1.5}}>
                            {latestNote ? latestNote.text : customer.note}
                          </span>
                          <div className="text-xs fg-muted mt-1">
                            {latestNote ? `${latestNote.who} · ${latestNote.when || __agoLabel(latestNote.ts)}` : ownerOf(customer)}
                            {' · '}<button className="btn btn-ghost btn-sm" style={{padding:'0 4px',fontSize:11}} onClick={() => setTab('notes')}>Xem tất cả →</button>
                          </div>
                        </>
                      ) : <span className="fg-muted">Chưa có</span>}
                    </span>
                  </div>
                </Card>

                <Card>
                  <div className="row mb-4">
                    <h3 style={{margin:0,fontSize:'var(--t-md)',color:'var(--fg-1)'}}>Doanh số 12 tháng qua</h3>
                    <span className="text-xs fg-muted" style={{marginLeft:'auto'}}>Tổng: {customer.orders > 0 ? fmt.vndFull(monthly.reduce((a,b)=>a+b*1000,0)) : '0đ'}</span>
                  </div>
                  {customer.orders > 0 ? (
                    <>
                      <BarChart data={monthly} height={140} highlight={11}/>
                      <div className="row mt-3" style={{justifyContent:'space-between',fontSize:11,color:'var(--fg-muted)'}}>
                        {Array.from({length: 12}, (_, i) => {
                          const m = new Date(D.today.getFullYear(), D.today.getMonth() - 11 + i, 1).getMonth() + 1;
                          return <span key={i}>T{m}</span>;
                        })}
                      </div>
                    </>
                  ) : (
                    <EmptyState icon="chart" title="Chưa có doanh số" desc="Khách chưa phát sinh đơn nào — biểu đồ sẽ hiện khi có đơn đầu tiên."/>
                  )}
                </Card>
              </div>
            </>
          )}

          {tab === 'orders' && (
            <Card pad={false}>
              <CardHeader title={`${orders.length} đơn hàng${customer.orders > orders.length ? ` gần nhất (tổng ${customer.orders} đơn)` : ''}`}
                sub={sessionOrders.length > 0 ? `${sessionOrders.length} đơn vừa ghi nhận trong phiên` : 'Sắp xếp theo thời gian mới nhất'}/>
              {orders.length > 0 ? (
                <div className="table-wrap">
                  <table className="tbl">
                    <thead>
                      <tr>
                        <th>Mã đơn</th><th>Ngày</th><th>Kênh</th><th className="num">SP</th><th className="num">Tổng</th><th>Trạng thái</th>
                      </tr>
                    </thead>
                    <tbody>
                      {orders.map(o => (
                        <tr key={o.id} style={o._new ? {background:'rgba(65,182,230,0.07)'} : undefined}>
                          <td className="strong tnum">{o.id}{o._new && <Tag variant="info" style={{marginLeft:6}}>Mới</Tag>}</td>
                          <td className="text-sm">{fmt.date(o.date)}</td>
                          <td><Tag variant="outline">{o.channel}</Tag></td>
                          <td className="num tnum">{o.items}</td>
                          <td className="num strong">{fmt.vndFull(o.total)}</td>
                          <td><Tag variant="success" dot>{o.status || 'Đã ghi nhận'}</Tag></td>
                        </tr>
                      ))}
                    </tbody>
                  </table>
                </div>
              ) : (
                <EmptyState icon="receipt" title="Chưa có đơn hàng trong CRM"
                  desc={customer.orders > 0
                    ? `Khách có ${customer.orders} đơn theo hệ thống POS/Sapo — chi tiết chưa đồng bộ về CRM. Dùng "Ghi nhận đơn" để lưu đơn mới.`
                    : 'Dùng nút "Ghi nhận đơn" để lưu đơn đầu tiên của khách.'}
                  action={<button className="btn btn-cta btn-sm" onClick={() => onLogOrder && onLogOrder(customer)}><Icon name="receipt" size={13}/>Ghi nhận đơn</button>}/>
              )}
            </Card>
          )}

          {tab === 'activity' && (
            <Card>
              <h3 style={{margin:'0 0 var(--space-4)',fontSize:'var(--t-md)',color:'var(--fg-1)'}}>Dòng thời gian</h3>
              {(() => {
                // Dựng dòng thời gian từ dữ liệu thật: đơn hàng + ghi chú/kết quả liên hệ trong phiên
                const events = [];
                orders.slice(0, 4).forEach(o => events.push({
                  ico:'cart', title:`Đặt đơn ${o.id}`, meta:`${fmt.rel(o.date)} · ${o.channel} · ${fmt.vndFull(o.total)}`, sort: o.date || '',
                }));
                sessionNotes.slice(0, 3).forEach(n => events.push({
                  ico: n.tag === 'contact' ? 'phone' : 'note',
                  title: n.tag === 'contact' ? `Liên hệ khách — ${n.who}` : `Ghi chú — ${n.who}`,
                  meta: `${__agoLabel(n.ts)} · ${n.text.slice(0, 60)}${n.text.length > 60 ? '…' : ''}`,
                  sort: '9999-99', // ghi chú phiên luôn mới nhất
                }));
                if (customer.nps >= 9) events.push({ ico:'star', title:`Khảo sát NPS — chấm ${customer.nps}/10`, meta:'Khách hài lòng (Promoter)', sort: customer.joined || '' });
                events.sort((a, b) => b.sort < a.sort ? -1 : 1);
                if (!events.length) return <EmptyState icon="activity" title="Chưa có hoạt động" desc="Đơn hàng, cuộc gọi và ghi chú của khách sẽ hiện ở đây."/>;
                return events.map((a, i) => (
                  <div key={i} className="activity">
                    <div className="ico"><Icon name={a.ico} size={14}/></div>
                    <div className="body">
                      <div className="title">{a.title}</div>
                      <div className="meta">{a.meta}</div>
                    </div>
                  </div>
                ));
              })()}
            </Card>
          )}

          {tab === 'notes' && (
            <>
              {/* Session follow-ups (newest first) */}
              {sessionFollowUps.length > 0 && (
                <Card pad={false} className="mb-4">
                  <CardHeader title="Lịch follow-up đã đặt" sub={`${sessionFollowUps.length} lịch · vừa tạo trong phiên`}/>
                  <div className="card-pad">
                    {sessionFollowUps.map(f => (
                      <div key={f.id} className="activity">
                        <div className="ico" style={{color:'var(--vs-light-blue)'}}><Icon name="clock" size={14}/></div>
                        <div className="body">
                          <div className="title">{f.reason}</div>
                          <div className="meta">Hẹn: {fmt.date(f.date)} {f.time} · Kênh {f.channel} · {f.who} · {f.createdAt || __agoLabel(f.ts)}</div>
                        </div>
                        <Tag variant="info" dot>Đã đặt</Tag>
                      </div>
                    ))}
                  </div>
                </Card>
              )}

              <Card className="mb-4">
                <h3 style={{margin:'0 0 var(--space-4)',fontSize:'var(--t-md)',color:'var(--fg-1)'}}>Thêm ghi chú mới</h3>
                <textarea className="textarea" placeholder="VD: Khách quan tâm Whey vị mới, sẽ quyết định cuối tuần..." id="qa-quick-note"/>
                <div className="row mt-3">
                  <button className="btn btn-cta" style={{marginLeft:'auto'}}
                    onClick={() => {
                      const el = document.getElementById('qa-quick-note');
                      const v = el && el.value.trim();
                      if (!v) { window.tvsToast('Nhập nội dung ghi chú trước khi lưu', 'demo'); return; }
                      const meName = role ? role.person : 'Nhân viên';
                      const meRole = role ? role.team : 'Telesale TP.HCM';
                      window.TVS_SESSION.addNote(customer.id, { text: v, tag: 'general', who: meName, role: meRole });
                      el.value = '';
                      window.tvsToast('Đã lưu ghi chú vào hồ sơ khách');
                    }}>Lưu ghi chú</button>
                </div>
              </Card>
              <Card pad={false}>
                <CardHeader title="Ghi chú trước đây" sub={`${sessionNotes.length + (customer.note ? 1 : 0)} mục`}/>
                <div className="card-pad">
                  {[
                    ...sessionNotes.map(n => ({...n, _session: true})),
                    ...(customer.note ? [{who: ownerOf(customer), role:'Phụ trách', when:'—', text: customer.note}] : []),
                  ].map((n,i) => (
                    <div key={n.id || i} className="activity">
                      <Avatar name={n.who} size="sm"/>
                      <div className="body">
                        <div className="row gap-2">
                          <div className="title">{n.who} <span className="text-xs fg-muted">· {n.role} · {n.when || __agoLabel(n.ts)}</span></div>
                          {n._session && <Tag variant="info">Mới</Tag>}
                          {n.tag && n.tag !== 'general' && (
                            <Tag variant={n.tag==='complaint'?'danger':n.tag==='feedback'?'success':n.tag==='opportunity'?'warning':'info'}>
                              {({preference:'Sở thích',feedback:'Phản hồi',complaint:'Khiếu nại',opportunity:'Cơ hội bán',contact:'Kết quả liên hệ'})[n.tag] || 'Ghi chú'}
                            </Tag>
                          )}
                        </div>
                        <div className="text-sm fg-2 mt-2" style={{whiteSpace:'pre-wrap'}}>{n.text}</div>
                      </div>
                    </div>
                  ))}
                </div>
              </Card>
            </>
          )}
        </div>
      </div>
    </div>
  );
}

window.CustomersScreen = CustomersScreen;
window.Customer360 = Customer360;

// ===========================================================
// QuickActionModal — Ghi chú nhanh / Đặt lịch follow-up
// (popup nhỏ giữa màn hình, không cần mở drawer 360°)
// ===========================================================
function QuickActionModal({ mode, customer, role, onClose }) {
  const isNote = mode === 'note';
  const isOutcome = mode === 'outcome';
  const isTelesale = role && role.id === 'telesale';

  const [text, setText] = useState('');
  const [reason, setReason] = useState('');
  const [date, setDate] = useState(D.iso(isTelesale ? 1 : 2));
  const [time, setTime] = useState('10:00');
  const [channel, setChannel] = useState(customer.preferredContact || 'Gọi điện');
  const [tag, setTag] = useState('general');
  const [outcome, setOutcome] = useState(null);
  const [alsoSchedule, setAlsoSchedule] = useState(true); // LUÔN đặt lịch liên hệ lần sau, kể cả khi đã liên hệ thành công
  const [saved, setSaved] = useState(false);

  const OUTCOMES = [
    {k:'success',   l:'Đã liên hệ — thành công', ic:'check',  c:'#06A77D'},
    {k:'order',     l:'Chốt đơn',                ic:'cart',   c:'#012169'},
    {k:'callback',  l:'Hẹn liên hệ lại',         ic:'clock',  c:'#41B6E6'},
    {k:'noanswer',  l:'Không nghe máy',          ic:'phone',  c:'#777777'},
    {k:'declined',  l:'Không quan tâm',          ic:'close',  c:'#BF0923'},
  ];

  const handleSave = () => {
    const meName = role ? role.person : 'Nhân viên';
    const meRole = role ? role.team : 'Telesale TP.HCM';
    if (isNote) {
      window.TVS_SESSION.addNote(customer.id, { text, tag, who: meName, role: meRole });
    } else if (isOutcome) {
      const o = OUTCOMES.find(x => x.k === outcome);
      window.TVS_SESSION.addNote(customer.id, { text: `[Kết quả liên hệ: ${o.l}]${text ? ' ' + text : ''}`, tag: 'contact', who: meName, role: meRole });
      if (alsoSchedule) {
        const reasons = {
          success:  'Chăm sóc định kỳ sau liên hệ thành công',
          order:    'Hỏi thăm sau khi mua hàng',
          callback: 'Gọi lại theo hẹn',
          noanswer: 'Gọi lại — lần trước không nghe máy',
          declined: 'Liên hệ lại sau — lần trước chưa quan tâm',
        };
        window.TVS_SESSION.addFollowUp(customer.id, { reason: (reasons[outcome] || 'Liên hệ lại') + (text ? ' — ' + text : ''), date, time, channel, who: meName, role: meRole });
      }
    } else {
      // Lý do là tuỳ chọn — để trống thì ghi mặc định để lịch vẫn hiện rõ ở tooltip / tab Lịch Follow-up / banner hồ sơ
      window.TVS_SESSION.addFollowUp(customer.id, { reason: reason.trim() || 'Liên hệ lại', date, time, channel, who: meName, role: meRole });
    }
    setSaved(true);
    setTimeout(onClose, 1100);
  };

  return (
    <div className="drawer-overlay" style={{justifyContent:'center',alignItems:'center'}} onClick={onClose}>
      <div onClick={(e) => e.stopPropagation()}
        style={{background:'var(--bg-page)',width:'min(560px, 94vw)',maxHeight:'90vh',display:'flex',flexDirection:'column',borderRadius:'var(--radius-lg)',overflow:'hidden',boxShadow:'var(--shadow-pop)',animation:'slidein 0.28s var(--ease-out)'}}>

        {/* Header */}
        <div style={{padding:'var(--space-4) var(--space-5)',background:isOutcome ? 'var(--vs-dark-blue)' : isNote ? 'var(--vs-dark-blue)' : 'var(--vs-light-blue)',color:'#fff',display:'flex',gap:'var(--space-3)',alignItems:'center'}}>
          <div style={{width:40,height:40,borderRadius:'50%',background:'rgba(255,255,255,0.18)',display:'flex',alignItems:'center',justifyContent:'center',color:'#fff'}}>
            <Icon name={isOutcome ? 'phone' : isNote ? 'note' : 'clock'} size={20}/>
          </div>
          <div style={{flex:1}}>
            <div style={{fontSize:11,letterSpacing:'0.14em',textTransform:'uppercase',color:'rgba(255,255,255,0.7)',fontWeight:700}}>
              {isOutcome ? 'Ghi kết quả liên hệ' : isNote ? 'Ghi chú nhanh' : 'Đặt lịch follow-up'}
            </div>
            <h3 style={{margin:'2px 0 0',fontFamily:'var(--font-display)',fontSize:18,fontWeight:700,color: (isNote || isOutcome) ? '#fff' : 'var(--vs-dark-blue)'}}>{customer.name}</h3>
          </div>
          <button onClick={onClose}
            style={{background:'rgba(255,255,255,0.18)',border:'none',color:'#fff',width:32,height:32,borderRadius:'50%',cursor:'pointer',display:'inline-flex',alignItems:'center',justifyContent:'center'}}>
            <Icon name="close" size={14}/>
          </button>
        </div>

        {/* Body */}
        {saved ? (
          <div style={{padding:'var(--space-8) var(--space-6)',textAlign:'center'}}>
            <div style={{width:56,height:56,borderRadius:'50%',background:'var(--status-success)',color:'#fff',display:'inline-flex',alignItems:'center',justifyContent:'center',marginBottom:'var(--space-3)'}}>
              <Icon name="check" size={28} stroke={3}/>
            </div>
            <div className="strong fg-1" style={{fontSize:16,marginBottom:4}}>
              {isOutcome ? 'Đã ghi kết quả liên hệ' : isNote ? 'Đã lưu ghi chú' : 'Đã đặt lịch follow-up'}
            </div>
            <div className="text-sm fg-muted">
              {isOutcome ? (alsoSchedule ? `Đã lưu vào hồ sơ + tạo lịch liên hệ lại ngày ${fmt.date(date)} lúc ${time}.` : 'Đã lưu vào hồ sơ khách hàng.') : isNote ? 'Ghi chú đã được gắn vào hồ sơ khách hàng.' : `Hệ thống sẽ nhắc bạn ngày ${fmt.date(date)} lúc ${time}.`}
            </div>
          </div>
        ) : (
          <>
            <div style={{padding:'var(--space-4) var(--space-5)'}}>
              {/* Customer mini-card — riêng modal Đặt lịch giữ tối giản (tên khách đã có trên header) */}
              {(isOutcome || isNote) && (
                <div className="row gap-3 mb-4" style={{padding:'10px 12px',background:'var(--bg-soft)',borderRadius:'var(--radius-md)'}}>
                  <Avatar name={customer.name} size="sm" tier={tierVariant(customer.tier)}/>
                  <div style={{flex:1,minWidth:0}}>
                    <div className="row gap-2">
                      <div className="strong fg-1">{customer.name}</div>
                      <Tag variant={tierVariant(customer.tier)}>{customer.tier}</Tag>
                    </div>
                    <div className="text-xs fg-muted tnum">{customer.phone} · {customer.id}</div>
                  </div>
                </div>
              )}

              {isOutcome ? (
                <>
                  <div className="field-label">Kết quả liên hệ <span style={{color:'var(--status-sale)'}}>*</span></div>
                  <div style={{display:'grid',gridTemplateColumns:'1fr 1fr',gap:8}} className="mb-3 qa-options">
                    {OUTCOMES.map(o => (
                      <button key={o.k}
                        className="btn btn-secondary"
                        style={{justifyContent:'flex-start',padding:'10px 12px',borderColor: outcome===o.k ? o.c : 'var(--border-1)',background: outcome===o.k ? o.c+'14' : 'var(--bg-page)'}}
                        onClick={() => { setOutcome(o.k); if (o.k === 'callback') setAlsoSchedule(true); }}>
                        <Icon name={o.ic} size={14} style={{color:o.c}}/>
                        <span style={{color: outcome===o.k ? o.c : 'var(--fg-2)',fontWeight: outcome===o.k ? 700 : 500,fontSize:13}}>{o.l}</span>
                      </button>
                    ))}
                  </div>
                  {/* LUÔN cho phép đặt lịch liên hệ lần sau — với MỌI kết quả */}
                  {outcome && (
                    <div style={{border:'1px solid var(--border-1)',borderRadius:'var(--radius-md)',padding:'var(--space-3)',marginBottom:'var(--space-3)',background: alsoSchedule ? 'rgba(65,182,230,0.06)' : 'var(--bg-soft)'}}>
                      <label style={{display:'flex',gap:10,alignItems:'flex-start',cursor:'pointer'}}>
                        <input type="checkbox" checked={alsoSchedule} disabled={outcome === 'callback'}
                          onChange={(e) => setAlsoSchedule(e.target.checked)}
                          style={{width:16,height:16,marginTop:2,accentColor:'var(--vs-dark-blue)',cursor:'pointer'}}/>
                        <span style={{flex:1}}>
                          <span className="bold" style={{color:'var(--vs-dark-blue)',fontSize:13.5}}>Đặt lịch liên hệ lần sau</span>
                          <span className="text-xs fg-muted" style={{display:'block',marginTop:2}}>
                            {outcome === 'callback'
                              ? 'Bắt buộc với kết quả “Hẹn liên hệ lại”'
                              : 'Mọi khách đều nên có lịch chăm sóc tiếp theo — sẽ hiện ở tab Lịch Follow-up'}
                          </span>
                        </span>
                      </label>
                      {alsoSchedule && (
                        <>
                          <div className="grid grid-2 mt-3">
                            <div>
                              <div className="field-label">Ngày liên hệ lại</div>
                              <input className="input tnum" type="date" value={date} onChange={(e)=>setDate(e.target.value)}/>
                            </div>
                            <div>
                              <div className="field-label">Giờ</div>
                              <input className="input tnum" type="time" value={time} onChange={(e)=>setTime(e.target.value)}/>
                            </div>
                          </div>
                          <div className="row gap-1 mt-3" style={{flexWrap:'wrap'}}>
                            <span className="text-xs fg-muted" style={{marginRight:4}}>Nhanh:</span>
                            {[[D.iso(1),'Ngày mai'],[D.iso(7),'1 tuần nữa'],[D.iso(30),'1 tháng nữa'],[D.iso(90),'3 tháng nữa']].map(([d,l]) => (
                              <button key={d} className={date === d ? 'btn btn-cta btn-sm' : 'btn btn-secondary btn-sm'} onClick={() => setDate(d)}>{l}</button>
                            ))}
                          </div>
                        </>
                      )}
                    </div>
                  )}
                  <div className="field-label">Ghi chú thêm (không bắt buộc)</div>
                  <textarea className="textarea"
                    placeholder="VD: Khách bận, hẹn gọi sau 14h. Quan tâm combo Whey + Creatine..."
                    value={text} onChange={(e)=>setText(e.target.value)}
                    style={{minHeight:70,fontSize:14,lineHeight:1.5}}/>
                </>
              ) : isNote ? (
                <>
                  <div className="field-label">Loại ghi chú</div>
                  <div className="row gap-1 mb-3" style={{flexWrap:'wrap'}}>
                    {[
                      {k:'general',  l:'Chung',         c:'outline'},
                      {k:'preference',l:'Sở thích',    c:'info'},
                      {k:'feedback', l:'Phản hồi',     c:'success'},
                      {k:'complaint',l:'Khiếu nại',    c:'danger'},
                      {k:'opportunity',l:'Cơ hội bán', c:'warning'},
                    ].map(t => (
                      <button key={t.k}
                        className={tag===t.k ? 'btn btn-cta btn-sm' : 'btn btn-secondary btn-sm'}
                        onClick={() => setTag(t.k)}>
                        {t.l}
                      </button>
                    ))}
                  </div>
                  <div className="field-label">Nội dung</div>
                  <textarea className="textarea" autoFocus
                    placeholder="VD: Khách quan tâm Whey vị mới, sẽ quyết định cuối tuần. Đã hỏi giá combo gym tăng cơ..."
                    value={text} onChange={(e)=>setText(e.target.value)}
                    style={{minHeight:120,fontSize:14,lineHeight:1.5}}/>
                  <div className="row mt-3" style={{gap:8,flexWrap:'wrap'}}>
                    <span className="text-xs fg-muted">Gợi ý nhanh:</span>
                    {['Khách chốt đơn, đợi giao', 'Cần gọi lại tuần sau', 'Quan tâm sản phẩm mới', 'Đã giới thiệu chương trình VIP'].map(s => (
                      <button key={s} className="btn btn-ghost btn-sm" style={{fontSize:11,padding:'2px 8px'}}
                        onClick={() => setText(text ? text + '\n' + s : s)}>
                        + {s}
                      </button>
                    ))}
                  </div>
                </>
              ) : (
                <>
                  {/* Modal tối giản theo yêu cầu: chỉ Lý do → Ngày/Giờ → Kênh */}
                  <div className="field-label">Lý do follow-up <span className="fg-muted" style={{fontWeight:400,textTransform:'none',letterSpacing:0}}>(tuỳ chọn)</span></div>
                  <input className="input mb-3" autoFocus
                    placeholder="VD: Gọi lại tư vấn combo gym, nhắc khách hết hàng Whey..."
                    value={reason} onChange={(e)=>setReason(e.target.value)}/>

                  <div className="grid grid-2 mb-3">
                    <div>
                      <div className="field-label">Ngày</div>
                      <input className="input tnum" type="date" value={date} onChange={(e)=>setDate(e.target.value)}/>
                    </div>
                    <div>
                      <div className="field-label">Giờ</div>
                      <input className="input tnum" type="time" value={time} onChange={(e)=>setTime(e.target.value)}/>
                    </div>
                  </div>

                  <div className="field-label">Kênh liên hệ</div>
                  <div className="row gap-1" style={{flexWrap:'wrap'}}>
                    {['Gọi điện','Zalo','SMS','Email','Cửa hàng'].map(c => (
                      <button key={c}
                        title={c === customer.preferredContact ? 'Kênh khách ưa thích' : undefined}
                        className={channel===c ? 'btn btn-cta btn-sm' : 'btn btn-secondary btn-sm'}
                        onClick={() => setChannel(c)}>
                        {c === customer.preferredContact && '★ '}{c}
                      </button>
                    ))}
                  </div>
                </>
              )}
            </div>

            {/* Footer */}
            <div style={{padding:'var(--space-3) var(--space-5)',background:'var(--bg-soft)',borderTop:'1px solid var(--border-1)',display:'flex',gap:'var(--space-3)',alignItems:'center'}}>
              <div className="text-xs fg-muted">
                {isOutcome ? (!outcome ? 'Chọn kết quả liên hệ ở trên' : alsoSchedule ? 'Lưu kết quả + tạo lịch liên hệ lại' : 'Kết quả sẽ lưu vào tab Hoạt động của hồ sơ khách') : isNote ? 'Ghi chú sẽ hiển thị ở hồ sơ 360° của khách' : 'Lịch + lý do lưu vào hồ sơ khách & tab Lịch Follow-up'}
              </div>
              <div style={{flex:1}}/>
              <button className="btn btn-secondary" onClick={onClose}>Huỷ</button>
              <button className="btn btn-cta"
                onClick={handleSave}
                disabled={isOutcome ? !outcome : isNote ? !text.trim() : !date}>
                <Icon name="check" size={14}/>{isOutcome ? 'Lưu kết quả' : isNote ? 'Lưu ghi chú' : 'Đặt lịch'}
              </button>
            </div>
          </>
        )}
      </div>
    </div>
  );
}

window.QuickActionModal = QuickActionModal;

// ===========================================================
// Backend Sapo: cùng origin khi chạy `node server/index.js`.
// Nếu mở frontend bằng static server khác, khai báo window.TVS_API_BASE = 'http://localhost:5174'.
const TVS_API = (typeof window !== 'undefined' && window.TVS_API_BASE) || '';

// "Ghi nhận đơn hàng" modal
// In-store & telesale dùng để lưu LỊCH SỬ MUA của khách,
// đơn thực tế được tạo ở hệ thống POS bên ngoài.
// ===========================================================
function LogOrderModal({ subject, customer, mode = 'b2c', onClose, onSave }) {
  const subj = subject || customer; // back-compat
  const isB2B = mode === 'b2b';
  const priceOf = (p) => isB2B ? p.b2bPrice : p.price;
  const refPlaceholder = 'VD: 260826BVTWHJYG';
  const refExternal = 'Sapo';
  const [items, setItems] = useState([]);
  const [search, setSearch] = useState('');
  const [channel, setChannel] = useState(isB2B ? 'pharmacy' : 'store');
  const [posRef, setPosRef] = useState('');
  const [orderDate, setOrderDate] = useState(D.iso(0));
  const [orderTime, setOrderTime] = useState('14:30');
  const [note, setNote] = useState('');
  const [overrideTotal, setOverrideTotal] = useState(null);
  // Tra cứu Sapo: idle | loading | done | error
  const [lookupState, setLookupState] = useState('idle');
  const [lookupError, setLookupError] = useState('');
  // Đơn thật kéo về từ Sapo, đã chuẩn hoá bởi server/sapo-order.js. null = chưa tra cứu.
  const [sapoOrder, setSapoOrder] = useState(null);
  // Cách nhập đơn: 'lookup' = tra cứu mã Sapo tự điền · 'manual' = tự tìm & chọn sản phẩm
  const [entryMode, setEntryMode] = useState('lookup');

  // Tra cứu đơn THẬT trên Sapo qua backend (server/index.js -> Sapo Open API).
  // Dữ liệu lấy về là CHỈ ĐỌC: không sửa sản phẩm, số lượng, giá hay tổng đơn.
  const sapoLookup = async () => {
    const code = posRef.trim();
    if (!code) { window.tvsToast('Nhập mã đơn ' + refExternal + ' trước đã', 'demo'); return; }
    setLookupState('loading'); setLookupError(''); setSapoOrder(null); setItems([]);
    try {
      const res = await fetch(TVS_API + '/api/sapo/orders/' + encodeURIComponent(code));
      const body = await res.json().catch(() => ({}));
      if (!res.ok) throw new Error(body.error || ('Tra cứu thất bại (HTTP ' + res.status + ')'));
      const o = body.order;
      setSapoOrder(o);
      setChannel(o.channel);
      setOrderDate(o.orderDate);
      setOrderTime(o.orderTime);
      setOverrideTotal(null);
      setLookupState('done');
      window.tvsToast('Đã lấy đơn ' + o.code + ' từ Sapo — ' + o.items.length + ' sản phẩm');
    } catch (e) {
      setLookupState('error');
      setLookupError(e.message || String(e));
      window.tvsToast('Không tra cứu được đơn ' + code, 'demo');
    }
  };

  const filtered = useMemo(() => {
    if (!search) return D.products.slice(0, 8);
    const s = search.toLowerCase();
    return D.products.filter(p => p.name.toLowerCase().includes(s) || p.sku.toLowerCase().includes(s) || p.cat.toLowerCase().includes(s)).slice(0, 8);
  }, [search]);

  const addProduct = (sku) => {
    setItems(items => {
      const e = items.find(i => i.sku === sku);
      if (e) return items.map(i => i.sku === sku ? {...i, qty: i.qty + 1} : i);
      return [...items, { sku, qty: 1, discMode: 'percent', discVal: 0 }];
    });
  };
  const removeItem = (sku) => setItems(items => items.filter(i => i.sku !== sku));
  const updateQty = (sku, d) => setItems(items => items.map(i => i.sku === sku ? {...i, qty: Math.max(0, i.qty + d)} : i).filter(i => i.qty > 0));
  // Chiết khảu theo từng dòng: % hoặc số tiền
  const setDisc = (sku, patch) => setItems(items => items.map(i => i.sku === sku ? {...i, ...patch} : i));
  const lineNet = (it) => {
    const gross = priceOf(it) * it.qty;
    if (it.discMode === 'amount') return Math.max(0, gross - (Number(it.discVal) || 0));
    return Math.round(gross * (1 - Math.min(100, Math.max(0, Number(it.discVal) || 0)) / 100));
  };

  const rich = items.map(i => ({ ...i, ...D.products.find(p => p.sku === i.sku) }));
  const grossTotal = rich.reduce((s, i) => s + (priceOf(i) * i.qty), 0);
  const subtotal = rich.reduce((s, i) => s + lineNet(i), 0);
  const discTotal = grossTotal - subtotal;
  const total = overrideTotal != null ? overrideTotal : subtotal;

  // Chế độ tra cứu: mọi con số lấy thẳng từ Sapo và bị khoá, nhân viên không sửa được.
  const isSapoView = entryMode === 'lookup' && !!sapoOrder;
  const viewItems = isSapoView ? sapoOrder.items : rich;
  const viewGross = isSapoView ? sapoOrder.gross : grossTotal;
  const viewDisc  = isSapoView ? sapoOrder.discount : discTotal;
  const viewTotal = isSapoView ? sapoOrder.total : total;
  const canSave = viewItems.length > 0;

  return (
    <div className="drawer-overlay" onClick={onClose}>
      <div onClick={(e) => e.stopPropagation()}
        style={{background:'var(--bg-page)',width:'100vw',height:'100vh',display:'flex',flexDirection:'column',overflow:'hidden',animation:'slidein 0.28s var(--ease-out)'}}>
        {/* Header */}
        <div style={{padding:'var(--space-3) var(--space-6)',background:'var(--vs-dark-blue)',color:'#fff',display:'flex',gap:'var(--space-3)',alignItems:'center',flexShrink:0}}>
          <div style={{width:36,height:36,borderRadius:'50%',background:'var(--vs-light-blue)',color:'var(--vs-dark-blue)',display:'flex',alignItems:'center',justifyContent:'center',flexShrink:0}}>
            <Icon name="receipt" size={18}/>
          </div>
          <div style={{flex:1,minWidth:0}}>
            <h2 style={{margin:0,fontFamily:'var(--font-display)',fontSize:18,fontWeight:700,color:'#fff',whiteSpace:'nowrap',overflow:'hidden',textOverflow:'ellipsis'}}>Ghi nhận đơn — {subj.name}</h2>
            <div className="text-xs" style={{color:'rgba(255,255,255,0.7)',marginTop:1}}>
              {isB2B ? <>Đối tác {subj.type || ''} · {subj.tier} · <span className="tnum">{subj.phone}</span> · Bảng giá {subj.priceList}</>
                     : <>Khách hạng {subj.tier} · <span className="tnum">{subj.phone}</span> · ID {subj.id}</>}
            </div>
          </div>
          <button className="close" onClick={onClose}>
            <Icon name="close" size={18}/>
          </button>
        </div>

        {/* Chọn cách nhập đơn: tra cứu Sapo/POS hoặc tự điền thủ công */}
        <div style={{display:'flex',gap:'var(--space-3)',padding:'var(--space-3) var(--space-6)',borderBottom:'1px solid var(--border-1)',background:'var(--bg-soft)',flexShrink:0}}>
          {[
            {id:'lookup', ic:'zap',    t:'Tra cứu từ Sapo',  d:'Nhập mã đơn — hệ thống tự kéo đơn thật về (chỉ đọc)'},
            {id:'manual', ic:'search', t:'Nhập thủ công',     d:'Tìm & chọn sản phẩm, tự nhập số lượng, chiết khấu'},
          ].map(m => (
            <button key={m.id} onClick={() => {
                if (m.id === entryMode) return;
                setEntryMode(m.id);
                setItems([]); setSapoOrder(null); setOverrideTotal(null);
                setLookupState('idle'); setLookupError(''); setSearch('');
              }}
              style={{flex:1,display:'flex',alignItems:'center',gap:10,padding:'10px 14px',borderRadius:'var(--radius-md)',cursor:'pointer',textAlign:'left',
                border: entryMode === m.id ? '2px solid var(--vs-dark-blue)' : '1px solid var(--border-1)',
                background: entryMode === m.id ? 'rgba(65,182,230,0.10)' : 'var(--bg-page)'}}>
              <div style={{width:34,height:34,borderRadius:'50%',flexShrink:0,display:'flex',alignItems:'center',justifyContent:'center',
                background: entryMode === m.id ? 'var(--vs-dark-blue)' : 'var(--bg-soft)', color: entryMode === m.id ? '#fff' : 'var(--fg-muted)'}}>
                <Icon name={m.ic} size={16}/>
              </div>
              <div style={{minWidth:0,flex:1}}>
                <div className="strong text-sm" style={{color: entryMode === m.id ? 'var(--vs-dark-blue)' : 'var(--fg-1)'}}>{m.t}</div>
                <div className="text-xs fg-muted">{m.d}</div>
              </div>
              {entryMode === m.id && <Icon name="check" size={16} style={{color:'var(--vs-dark-blue)',flexShrink:0}}/>}
            </button>
          ))}
        </div>

        {/* Body: 2 cột — trái = sản phẩm trong đơn, phải = thông tin đơn */}
        <div style={{display:'flex',gap:'var(--space-5)',padding:'var(--space-4) var(--space-6)',flex:1,overflow:'hidden',minHeight:0}}>
          {/* CỘT TRÁI */}
          <div style={{flex:'1.7 1 0',minWidth:0,display:'flex',flexDirection:'column',overflow:'hidden'}}>
            {entryMode === 'lookup' ? (
              <div className="card mb-3" style={{padding:'var(--space-3)',border:'1px solid var(--vs-light-blue)',background:'rgba(65,182,230,0.05)',flexShrink:0}}>
                <div className="row gap-2" style={{flexWrap:'wrap'}}>
                  <input className="input tnum" autoFocus style={{flex:'1 1 220px'}} placeholder={'Mã đơn Sapo — ' + refPlaceholder} value={posRef}
                    onChange={(e) => { setPosRef(e.target.value); if (lookupState !== 'idle') setLookupState('idle'); }}
                    onKeyDown={(e) => { if (e.key === 'Enter') sapoLookup(); }}/>
                  <button className="btn btn-cta" disabled={lookupState === 'loading'} onClick={sapoLookup}>
                    <Icon name={lookupState === 'loading' ? 'clock' : 'zap'} size={14}/>
                    {lookupState === 'loading' ? 'Đang tra cứu ' + refExternal + '...' : 'Tra cứu & tự điền'}
                  </button>
                </div>
                {lookupState === 'done' && sapoOrder ? (
                  <div className="mt-2">
                    <div className="row gap-2" style={{color:'var(--status-success)',flexWrap:'wrap'}}>
                      <Icon name="check" size={14}/>
                      <span className="text-sm strong">Đã lấy đơn <span className="tnum">{sapoOrder.code}</span> từ Sapo</span>
                      <Tag variant="info">{sapoOrder.statusLabel}</Tag>
                      {sapoOrder.channelRaw && <Tag>{sapoOrder.channelRaw}</Tag>}
                    </div>
                    <div className="text-xs fg-muted mt-1">
                      Khách trên đơn: <b>{sapoOrder.customer.name || '—'}</b>
                      {sapoOrder.customer.phone ? <> · <span className="tnum">{sapoOrder.customer.phone}</span></> : null}
                      {' '}· {sapoOrder.items.length} sản phẩm · <span className="tnum">{fmt.vndFull(sapoOrder.total)}</span>
                    </div>
                  </div>
                ) : lookupState === 'error' ? (
                  <div className="row gap-2 mt-2" style={{color:'var(--status-danger)'}}>
                    <Icon name="close" size={14}/>
                    <span className="text-sm strong">{lookupError}</span>
                  </div>
                ) : (
                  <div className="text-xs fg-muted mt-2">Nhập mã đơn Sapo — hệ thống kéo về đúng đơn thật: sản phẩm, số lượng, giá, chiết khấu, kênh và thời gian.</div>
                )}
              </div>
            ) : (
              <>
                {/* flex/margin của .topbar-search dành cho topbar — reset lại khi đứng trong cột dọc */}
                <div className="topbar-search mb-2" style={{flex:'0 0 auto',marginLeft:0,width:'100%'}}>
                  <Icon name="search" size={14} style={{color:'var(--fg-muted)'}}/>
                  <input autoFocus placeholder="Tìm sản phẩm theo tên, SKU, danh mục — VD: Collagen, Omega, VS-3280..."
                    value={search} onChange={(e)=>setSearch(e.target.value)}/>
                </div>
                <div className="card mb-3" style={{padding:6,maxHeight:'38%',overflow:'auto',flexShrink:0}}>
                  {filtered.map(p => (
                    <button key={p.sku} onClick={() => { addProduct(p.sku); setSearch(''); }}
                      style={{display:'flex',alignItems:'center',gap:12,padding:'8px 12px',width:'100%',background:'none',border:'none',cursor:'pointer',borderRadius:'var(--radius-sm)',textAlign:'left'}}
                      className="hover-row">
                      <div style={{width:32,height:32,background:'var(--bg-soft)',borderRadius:4,display:'flex',alignItems:'center',justifyContent:'center',color:'var(--vs-light-blue)',flexShrink:0}}>
                        <Icon name="pill" size={16}/>
                      </div>
                      <div style={{flex:1,minWidth:0}}>
                        <div className="strong text-sm fg-1" style={{whiteSpace:'nowrap',overflow:'hidden',textOverflow:'ellipsis'}}>{p.name}</div>
                        <div className="text-xs fg-muted">{p.sku} · {p.cat}</div>
                      </div>
                      <div className="strong tnum fg-1">{fmt.vndFull(priceOf(p))}</div>
                      <Icon name="plus" size={14} style={{color:'var(--vs-light-blue)'}}/>
                    </button>
                  ))}
                  {filtered.length === 0 && <div className="text-sm fg-muted" style={{padding:12}}>Không tìm thấy sản phẩm phù hợp</div>}
                </div>
              </>
            )}

            <div className="field-label row gap-2" style={{marginTop:0,flexShrink:0,alignItems:'center'}}>
              <span>Sản phẩm trong đơn{viewItems.length > 0 ? ` (${viewItems.length})` : ''}</span>
              {isSapoView && <Tag variant="info"><Icon name="eye" size={11}/> Chỉ đọc — dữ liệu từ Sapo</Tag>}
            </div>
            {isSapoView ? (
              /* Đơn kéo từ Sapo: hiển thị nguyên trạng, không cho sửa số lượng / chiết khấu / xoá dòng. */
              <div className="card" style={{padding:0,flex:1,minHeight:0,overflow:'auto'}}>
                {sapoOrder.items.map((it, idx) => {
                  const gross = it.price * it.qty;
                  const hasDisc = it.lineTotal < gross;
                  return (
                    <div key={(it.sku || 'sp') + '-' + idx} style={{display:'flex',gap:12,padding:'12px 16px',borderBottom:'1px solid var(--border-1)',alignItems:'center'}}>
                      <div style={{width:36,height:36,background:'var(--bg-soft)',borderRadius:4,display:'flex',alignItems:'center',justifyContent:'center',color:'var(--vs-light-blue)',flexShrink:0}}>
                        <Icon name="pill" size={18}/>
                      </div>
                      <div style={{flex:1,minWidth:0}}>
                        <div className="strong text-sm fg-1" style={{overflow:'hidden',textOverflow:'ellipsis',whiteSpace:'nowrap'}}>{it.variantName || it.name}</div>
                        <div className="text-xs fg-muted tnum">{it.sku || '—'} · {fmt.vndFull(it.price)}{it.unit ? ' / ' + it.unit : ''}</div>
                      </div>
                      <div className="tnum bold" style={{minWidth:56,textAlign:'center',color:'var(--fg-1)'}}>× {it.qty}</div>
                      <div style={{minWidth:110,textAlign:'right'}}>
                        {hasDisc && <div className="text-xs fg-muted tnum" style={{textDecoration:'line-through'}}>{fmt.vndFull(gross)}</div>}
                        <div className="strong tnum" style={{color: hasDisc ? 'var(--status-sale)' : 'var(--fg-1)'}}>{fmt.vndFull(it.lineTotal)}</div>
                      </div>
                    </div>
                  );
                })}
              </div>
            ) : rich.length > 0 ? (
              <div className="card" style={{padding:0,flex:1,minHeight:0,overflow:'auto'}}>
                {rich.map(it => {
                  const gross = priceOf(it) * it.qty;
                  const net = lineNet(it);
                  const hasDisc = net < gross;
                  return (
                  <div key={it.sku} style={{padding:'12px 16px',borderBottom:'1px solid var(--border-1)'}}>
                    <div style={{display:'flex',gap:12,alignItems:'center'}}>
                      <div style={{width:36,height:36,background:'var(--bg-soft)',borderRadius:4,display:'flex',alignItems:'center',justifyContent:'center',color:'var(--vs-light-blue)',flexShrink:0}}>
                        <Icon name="pill" size={18}/>
                      </div>
                      <div style={{flex:1,minWidth:0}}>
                        <div className="strong text-sm fg-1" style={{overflow:'hidden',textOverflow:'ellipsis',whiteSpace:'nowrap'}}>{it.name}</div>
                        <div className="text-xs fg-muted tnum">{fmt.vndFull(priceOf(it))}{isB2B && <span style={{marginLeft:6,color:'var(--vs-light-blue)'}}>(B2B)</span>}</div>
                      </div>
                      <div className="row gap-1">
                        <button className="btn btn-icon btn-sm btn-secondary" onClick={() => updateQty(it.sku, -1)}>−</button>
                        <span className="tnum bold" style={{minWidth:20,textAlign:'center',color:'var(--fg-1)'}}>{it.qty}</span>
                        <button className="btn btn-icon btn-sm btn-secondary" onClick={() => updateQty(it.sku, 1)}>+</button>
                      </div>
                      <div style={{minWidth:110,textAlign:'right'}}>
                        {hasDisc && <div className="text-xs fg-muted tnum" style={{textDecoration:'line-through'}}>{fmt.vndFull(gross)}</div>}
                        <div className="strong tnum" style={{color: hasDisc ? 'var(--status-sale)' : 'var(--fg-1)'}}>{fmt.vndFull(net)}</div>
                      </div>
                      <button className="btn btn-icon btn-sm btn-ghost" onClick={() => removeItem(it.sku)}>
                        <Icon name="close" size={12}/>
                      </button>
                    </div>
                    {/* Chiết khấu dòng */}
                    <div className="row gap-2 mt-2" style={{paddingLeft:48,flexWrap:'wrap'}}>
                      <span className="text-xs fg-muted" style={{fontWeight:700}}>Chiết khấu:</span>
                      <Segmented value={it.discMode || 'percent'} options={[{value:'percent',label:'%'},{value:'amount',label:'VNĐ'}]}
                        onChange={(v) => setDisc(it.sku, {discMode: v, discVal: 0})}/>
                      <input className="input tnum" type="number" min="0" step={it.discMode === 'amount' ? 1000 : 1}
                        max={it.discMode === 'amount' ? gross : 100}
                        value={it.discVal || 0}
                        onChange={(e) => setDisc(it.sku, {discVal: e.target.value})}
                        style={{width:100,padding:'4px 8px',fontSize:13}}/>
                      <span className="text-xs fg-muted">{it.discMode === 'amount' ? 'đ' : '%'}</span>
                      <div className="row gap-1">
                        {(it.discMode === 'amount' ? [] : [5,10,15,20,30]).map(v => (
                          <button key={v} className={Number(it.discVal) === v ? 'btn btn-cta btn-sm' : 'btn btn-ghost btn-sm'}
                            onClick={() => setDisc(it.sku, {discVal: v})}>{v}%</button>
                        ))}
                      </div>
                      {hasDisc && <Tag variant="danger">−{fmt.vndFull(gross - net)}</Tag>}
                    </div>
                  </div>
                );})}
              </div>
            ) : (
              <div className="empty" style={{padding:'var(--space-6) var(--space-4)',flex:1,minHeight:0,justifyContent:'center'}}>
                <div className="icon-wrap"><Icon name="cart" size={20}/></div>
                <div className="text-sm">{entryMode === 'lookup' ? 'Nhập mã đơn Sapo ở trên — sản phẩm của đơn sẽ tự hiện ở đây' : 'Tìm và click chọn sản phẩm ở trên để thêm vào đơn'}</div>
              </div>
            )}
          </div>

          {/* CỘT PHẢI: thông tin đơn */}
          <div style={{flex:'1 1 0',maxWidth:380,overflow:'auto'}}>
            <div className="field-label row gap-2" style={{marginTop:0,alignItems:'center'}}>
              <span>Kênh bán</span>
              {isSapoView && <Tag variant="info"><Icon name="eye" size={11}/> Theo Sapo</Tag>}
            </div>
            <select className="select" value={channel} disabled={isSapoView} onChange={(e)=>setChannel(e.target.value)}>
              <optgroup label="Tại chỗ / Trực tiếp">
                <option value="store">Cửa hàng (POS)</option>
                <option value="telesale">Telesale</option>
                <option value="direct">Bán trực tiếp / Giao tận nơi</option>
              </optgroup>
              <optgroup label="Online của TVS">
                <option value="web">Website</option>
                <option value="app">App</option>
                <option value="facebook">Facebook</option>
                <option value="zalo">Zalo</option>
              </optgroup>
              <optgroup label="Sàn thương mại điện tử">
                <option value="shopee">Shopee</option>
                <option value="tiktok">TikTok Shop</option>
                <option value="lazada">Lazada</option>
              </optgroup>
              <optgroup label="B2B / Đối tác">
                <option value="pharmacy">Nhà thuốc &amp; Bác sĩ</option>
                <option value="distrib">Đại lý / Phân phối</option>
                <option value="enterprise">Doanh nghiệp (quà tặng NV)</option>
                <option value="affiliate">Affiliate / KOC</option>
              </optgroup>
              <optgroup label="Khác">
                <option value="sapo">Nhập từ Sapo</option>
                <option value="other">Khác</option>
              </optgroup>
            </select>
            <div className="grid mt-3" style={{gridTemplateColumns:'1fr 1fr',gap:'var(--space-3)'}}>
              <div>
                <div className="field-label" style={{marginTop:0}}>Ngày mua</div>
                <input className="input tnum" type="date" value={orderDate} disabled={isSapoView} onChange={(e) => setOrderDate(e.target.value)}/>
              </div>
              <div>
                <div className="field-label" style={{marginTop:0}}>Giờ</div>
                <input className="input tnum" type="time" value={orderTime} disabled={isSapoView} onChange={(e) => setOrderTime(e.target.value)}/>
              </div>
            </div>
            <div className="field-label">Ghi chú (tuỳ chọn)</div>
            <textarea className="textarea" placeholder={isB2B ? 'VD: Đối tác đặt cho đợt chuẩn bị cuối tháng, giao trước 20/05...' : 'VD: Khách hỏi thêm về vị mới, đã tư vấn combo tăng cơ...'}
              value={note} onChange={(e)=>setNote(e.target.value)} style={{minHeight:72}}/>
            {isSapoView && (
              <div className="card mt-3" style={{padding:'var(--space-3)'}}>
                <div className="text-xs fg-muted" style={{fontWeight:700,marginBottom:6}}>Đơn gốc trên Sapo</div>
                <div className="text-xs" style={{display:'grid',gridTemplateColumns:'auto 1fr',gap:'4px 10px'}}>
                  <span className="fg-muted">Mã đơn</span><span className="tnum strong">{sapoOrder.code}</span>
                  <span className="fg-muted">Trạng thái</span><span className="strong">{sapoOrder.statusLabel}</span>
                  <span className="fg-muted">Khách trên đơn</span><span className="strong">{sapoOrder.customer.name || '—'}</span>
                  {sapoOrder.deliveryFee > 0 && <><span className="fg-muted">Phí giao</span><span className="tnum">{fmt.vndFull(sapoOrder.deliveryFee)}</span></>}
                </div>
                {sapoOrder.note && <div className="text-xs fg-muted mt-2">Ghi chú trên đơn: {sapoOrder.note}</div>}
                <div className="text-xs fg-muted mt-2">Số liệu lấy trực tiếp từ Sapo và không sửa được tại đây. Cần đổi, sửa trên Sapo rồi tra cứu lại.</div>
              </div>
            )}
            {entryMode === 'lookup' && !sapoOrder && (
              <div className="text-xs fg-muted mt-3">Chưa tra cứu đơn nào. Nhập mã đơn Sapo ở cột trái rồi bấm “Tra cứu &amp; tự điền”.</div>
            )}
          </div>
        </div>

        {/* Footer */}
        <div style={{padding:'var(--space-3) var(--space-6)',borderTop:'1px solid var(--border-1)',background:'var(--bg-soft)',display:'flex',gap:'var(--space-4)',alignItems:'center',flexShrink:0}}>
          <div>
            <div className="text-xs fg-muted">Tổng giá trị đơn</div>
            <div className="row gap-2" style={{alignItems:'baseline'}}>
              <div className="tnum bold" style={{fontSize:22,color:'var(--vs-dark-blue)',fontFamily:'var(--font-display)'}}>
                {fmt.vndFull(viewTotal)}
              </div>
              {!isSapoView && overrideTotal != null && overrideTotal !== subtotal && (
                <span className="text-xs fg-muted" style={{textDecoration:'line-through'}}>{fmt.vndFull(subtotal)}</span>
              )}
              {!isSapoView && rich.length > 0 && overrideTotal == null && (
                <button className="btn btn-ghost btn-sm" onClick={() => { const v = prompt(isB2B ? 'Tổng đơn thực trên Sapo (đã áp chiết khấu):' : 'Tổng đơn thực trên POS (đã trừ khuyến mãi):', subtotal); if (v != null && String(v).trim() !== '' && !isNaN(+v) && +v > 0) setOverrideTotal(+v); }}>
                  Sửa tổng
                </button>
              )}
            </div>
            {viewDisc > 0 && viewGross > 0 && (
              <div className="text-xs tnum mt-1">
                <span className="fg-muted">Gốc {fmt.vndFull(viewGross)}</span>
                <span style={{color:'var(--status-sale)',fontWeight:700,marginLeft:6}}>
                  − CK {fmt.vndFull(viewDisc)} ({Math.round(viewDisc/viewGross*100)}%)
                </span>
              </div>
            )}
          </div>
          <div style={{flex:1}}/>
          <button className="btn btn-secondary" onClick={onClose}>Huỷ</button>
          <button className="btn btn-cta btn-lg" disabled={!canSave} onClick={() => onSave && onSave(
            isSapoView
              ? {items: sapoOrder.items, total: sapoOrder.total, channel: sapoOrder.channel, posRef: sapoOrder.code,
                 orderDate: sapoOrder.orderDate, orderTime: sapoOrder.orderTime, note, source: 'sapo', sapoOrder}
              : {items: rich, total, channel, posRef, orderDate, orderTime, note, source: 'manual'}
          )}>
            <Icon name="check" size={16}/>Lưu lịch sử mua
          </button>
        </div>
      </div>
    </div>
  );
}

window.LogOrderModal = LogOrderModal;
