Optimoroute POD - Gray Order ID by Darrien

Mark Order ID gray when POD is viewed (row click + modal fallback) with debug logs

// ==UserScript==
// @name         Optimoroute POD - Gray Order ID by Darrien
// @namespace    pod-gray-debug
// @version      1.2.0
// @description  Mark Order ID gray when POD is viewed (row click + modal fallback) with debug logs
// @match        https://*.optimoroute.com/*
// @match        https://optimoroute.com/*
// @run-at       document-idle
// @inject-into  content
// @license MIT 
// ==/UserScript==
(function () {
  'use strict';

  const DEBUG = true;
  const log = (...a) => DEBUG && console.debug('[POD]', ...a);

  /* 文案(如界面是中文可替换) */
  const TXT_ORDER_DETAILS = 'Order Details';
  const TXT_POD           = 'Proof of Delivery';
  const TXT_ORDER_ID_LBL  = 'Order ID';

  /* 宽松一点:2~3个大写字母 + ≥5位数字(ZX..., WP... 都能匹配)*/
  const ORDER_ID_REGEX = /[A-Z]{2,3}\d{5,}/;

  /* 颜色样式(尽量提高优先级) */
  const style = document.createElement('style');
  style.textContent = `
  td .x-grid-cell-inner.pod-orderid-viewed,
  td.pod-orderid-viewed .x-grid-cell-inner,
  .x-grid-cell-inner.pod-orderid-viewed {
    color: #9ca3af !important;
  }`;
  document.head.appendChild(style);

  /* 按天存储 */
  const STORAGE_PREFIX = 'podViewedIDs:';
  const todayKey = () => {
    const d = new Date();
    return `${STORAGE_PREFIX}${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}-${String(d.getDate()).padStart(2,'0')}`;
  };
  const loadSet = () => { try { return new Set(JSON.parse(localStorage.getItem(todayKey())||'[]')); } catch { return new Set(); } };
  const saveSet = (s) => localStorage.setItem(todayKey(), JSON.stringify([...s]));
  const viewed = loadSet();

  /* 小工具 */
  const byTextEq  = (el,t)=>!!el && (el.textContent||'').trim()===t;
  const findByText= (root,t)=>[...root.querySelectorAll('*')].find(el=>byTextEq(el,t));

  // 从行里拿 Order ID:优先“第3列”,不行再全行正则
  function getOrderIdFromRow(tr) {
    if (!tr) return null;
    const third = tr.querySelector('td:nth-child(3) .x-grid-cell-inner, td:nth-child(3)');
    const s = (third?.textContent || '').trim();
    if (s) {
      const m = s.match(ORDER_ID_REGEX);
      if (m) return m[0];
    }
    const text = (tr.textContent || '');
    const m2 = text.match(ORDER_ID_REGEX);
    return m2 ? m2[0] : null;
  }

  // 找到“Order ID 那格”的 DOM
  function findOrderIdCellInRow(tr, orderId) {
    if (!tr || !orderId) return null;
    // 精确匹配
    let cell = [...tr.querySelectorAll('td .x-grid-cell-inner, td')].find(c => (c.textContent||'').trim() === orderId);
    if (cell) return cell;
    // 包含匹配
    cell = [...tr.querySelectorAll('td .x-grid-cell-inner, td')].find(c => (c.textContent||'').includes(orderId));
    return cell || null;
  }

  function grayCell(cell) {
    if (!cell) return;
    if (cell.classList.contains('x-grid-cell-inner')) cell.classList.add('pod-orderid-viewed');
    else {
      cell.classList.add('pod-orderid-viewed');
      const inner = cell.querySelector('.x-grid-cell-inner');
      inner && inner.classList.add('pod-orderid-viewed');
    }
  }

  // 扫描整表,应用灰色
  function markTable() {
    const rows = document.querySelectorAll('tr.x-grid-row, table tr');
    rows.forEach(tr => {
      const oid = getOrderIdFromRow(tr);
      if (!oid) return;
      const cell = findOrderIdCellInRow(tr, oid);
      if (!cell) return;
      if (viewed.has(oid)) grayCell(cell); else {
        cell.classList.remove('pod-orderid-viewed');
        cell.querySelector?.('.x-grid-cell-inner')?.classList.remove('pod-orderid-viewed');
      }
    });
  }

  // —— A. 行内点击(相机/POD 列)立即记录 —— //
  // 兼容多种点击目标:相机图标、整个 POD 列、Action 列等;用捕获阶段拿到被阻止冒泡的点击
  ['pointerdown','click','mousedown'].forEach(evt => {
    document.addEventListener(evt, (e) => {
      const icon = e.target.closest('.podIndicatorPhotoIconSmall, .pod-indicator-icon, [data-qtip*="Photo attached"]');
      const podCell = icon ? icon.closest('td')
                      : e.target.closest('td[class*="pod-indicator-column"], td.x-action-col-cell');
      if (!icon && !podCell) return;

      const tr = (podCell || icon)?.closest('tr.x-grid-row, tr');
      if (!tr) return;

      const oid = getOrderIdFromRow(tr);
      log('row-click:', {evt, oid, tr});
      if (!oid) return;

      viewed.add(oid);
      saveSet(viewed);

      const cell = findOrderIdCellInRow(tr, oid);
      grayCell(cell);
    }, true); // capture
  });

  // —— B. 弹窗兜底(打开详情时也记录) —— //
  function extractOrderIdFromModal(modalRoot) {
    const label = findByText(modalRoot, TXT_ORDER_ID_LBL);
    if (!label) return null;
    const valueEl = label.parentElement?.querySelector(':scope > *:nth-child(2)');
    const raw = (valueEl?.textContent || '').trim();
    if (!raw) return null;
    const m = raw.match(ORDER_ID_REGEX);
    return m ? m[0] : raw;
  }
  function tryRecordFromNode(node) {
    const candidates = [...(node.querySelectorAll?.('div') || [])].filter(el => {
      const t = (el.textContent || '');
      return t.includes(TXT_ORDER_DETAILS) && t.includes(TXT_POD);
    });
    candidates.forEach(modalRoot => {
      const oid = extractOrderIdFromModal(modalRoot);
      log('modal-detect:', {oid, modalRoot});
      if (!oid) return;
      if (!viewed.has(oid)) {
        viewed.add(oid);
        saveSet(viewed);
        markTable();
      }
    });
  }

  // 初次/监听
  markTable();
  tryRecordFromNode(document);

  const mo = new MutationObserver(muts => {
    let needMark = false;
    muts.forEach(m => {
      m.addedNodes && m.addedNodes.forEach(n => {
        if (n.nodeType !== 1) return;
        tryRecordFromNode(n); // 可能是弹窗
        if (n.matches?.('table,tbody,tr,td,div')) needMark = true; // 表格更新
      });
    });
    if (needMark) markTable();
  });
  mo.observe(document.body, { childList:true, subtree:true });

  // 便捷:Alt+Shift+V 将“当前选中行”置灰(用于测试)
  document.addEventListener('keydown', (e) => {
    if (e.altKey && e.shiftKey && e.key.toLowerCase() === 'v') {
      const tr = document.querySelector('tr.x-grid-row-selected, tr.x-grid-row-focused') || document.querySelector('tr.x-grid-row');
      const oid = getOrderIdFromRow(tr);
      log('hotkey Alt+Shift+V:', {tr, oid});
      if (oid) {
        viewed.add(oid); saveSet(viewed);
        grayCell(findOrderIdCellInRow(tr, oid));
      }
    }
  });
})();