403Webshell
Server IP : 121.121.20.254  /  Your IP : 216.73.216.202
Web Server : Microsoft-IIS/10.0
System : Windows NT WEB-SERVER 10.0 build 20348 (Windows Server 2022) AMD64
User : IUSR ( 0)
PHP Version : 8.3.28
Disable Function : NONE
MySQL : ON  |  cURL : ON  |  WGET : OFF  |  Perl : OFF  |  Python : OFF  |  Sudo : OFF  |  Pkexec : OFF
Directory :  /Program Files/MariaDB 10.6/data/stockadjust/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Command :


[ Back ]     

Current File : /Program Files/MariaDB 10.6/data/stockadjust/wp_xyz_ips_short_code.ibd
��������Y�Y���
@
��������������������������&&����������������������Y�Y�L
��������IO���IO��A�X��������Y�Y�����������������������������������i������������������������������������������������������������������������������������������������������������������������������������������������������i�����	
����������������������������������������������������������������������������������������������������Y�Y�#v���������`�=E��+��

��!����2 infimumsupremum+����Module-Sample-Receiving�	&6�[xyz-ips snippet="Module-Sample-Receiving"]����/�
���Module-Sample-Receiving-New�&{[xyz-ips snippet="Module-Sample-Receiving-New"]����4��  3	(��,�0���Module-Sample-QC-Testing�&N�[xyz-ips snippet="Module-Sample-QC-Testing"]����4!� 8���Module-Sample-Receiving-Transfer<?php
if (!defined('ABSPATH')) exit;

if (!is_user_logged_in()) {
  echo '<div style="padding:12px;border:1px solid #fca5a5;border-radius:12px;background:#fff1f2;color:#991b1b;">
    Please login to use this page.
  </div>';
  return;
}

$srl = isset($_GET['srl']) ? trim((string)$_GET['srl']) : '';
$srl = preg_replace('/[\x00-\x1F\x7F]/u', '', $srl);
$srl = preg_replace('/[^A-Za-z0-9_\-\.\/]/', '', $srl);

if ($srl === '') {
  echo '<div style="padding:12px;border:1px solid #fca5a5;border-radius:12px;background:#fff1f2;color:#991b1b;">
    Missing required parameter: <b>?srl=</b>
  </div>';
  return;
}

/**
 * OPTIONAL SAFETY CHECK:
 * Only allow transfer if serial is currently in HQ and Qty = 1
 * (Matches your new QC list logic)
 */
$conn = function_exists('get_mssql') ? get_mssql() : null;
if (!$conn) {
  echo '<div style="padding:12px;border:1px solid #fca5a5;border-radius:12px;background:#fff1f2;color:#991b1b;">
    MSSQL connection failed.
  </div>';
  return;
}

$sqlCheck = "
  SELECT TOP 1
    s.SerialNumber,
    s.ItemCode,
    s.Location,
    ISNULL(s.Qty,0) AS Qty
  FROM ItemSerialNoDtl s
  WHERE s.SerialNumber = ?
    AND s.Location = 'HQ'
    AND ISNULL(s.Qty,0) = 1
";

$stmt = sqlsrv_query($conn, $sqlCheck, [$srl]);
if ($stmt === false) {
  echo '<div style="padding:12px;border:1px solid #fca5a5;border-radius:12px;background:#fff1f2;color:#991b1b;">
    SQL error during validation.
  </div>';
  return;
}

$row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC);
sqlsrv_free_stmt($stmt);

if (!$row) {
  echo '<div style="padding:12px;border:1px solid #fca5a5;border-radius:12px;background:#fff1f2;color:#991b1b;">
    Serial not available in HQ (or Qty != 1): <b>' . esc_html($srl) . '</b>
  </div>';
  return;
}

$rest_base = esc_url_raw(rest_url('ac/v1'));
$nonce     = wp_create_nonce('wp_rest');
$current   = wp_get_current_user();
$byName    = $current ? ($current->display_name ?: $current->user_login) : '';

$itemCode  = (string)($row['ItemCode'] ?? '');
?>
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>

<script>
(function(){
  const SRL       = <?php echo json_encode($srl); ?>;
  const ITEM_CODE = <?php echo json_encode($itemCode); ?>;

  const REST_BASE = <?php echo json_encode($rest_base); ?>;
  const NONCE     = <?php echo json_encode($nonce); ?>;
  const BY_NAME   = <?php echo json_encode($byName); ?>;

  window.addEventListener('load', async () => {

    const res = await Swal.fire({
      icon: 'question',
      title: 'Transfer to QC?',
      html: `
        <div style="text-align:left">
          <div><b>Serial:</b> ${escapeHtml(SRL)}</div>
          <div><b>Item:</b> ${escapeHtml(ITEM_CODE || '-')}</div>
          <div><b>From:</b> HQ</div>
          <div><b>To:</b> QC</div>
        </div>
      `,
      showCancelButton: true,
      confirmButtonText: 'Yes, Transfer',
      cancelButtonText: 'Cancel',
      reverseButtons: true,
      allowOutsideClick: false
    });

    if (!res.isConfirmed) {
      history.back();
      return;
    }

    const payload = {
      job_type: 'STOCK_TRANSFER_SERIAL',
      client_request_id: 'st_' + Date.now() + '_' + Math.random().toString(16).slice(2),
      payload: {
        serialNo: SRL,
        createdBy: BY_NAME
      }
    };

    try {
      const r = await fetch(REST_BASE + '/job', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          'X-WP-Nonce': NONCE
        },
        body: JSON.stringify(payload)
      });

      const j = await r.json().catch(() => ({}));

      if (!r.ok || j.ok === false) {
        throw new Error(j.message || 'Request failed');
      }

      await Swal.fire({
        icon: 'success',
        title: 'Transfer Queued',
        html: `
          <div style="text-align:left">
            <div>✅ Job queued successfully.</div>
            <div style="margin-top:8px"><b>Serial:</b> ${escapeHtml(SRL)}</div>
            ${j.job_id ? `<div><b>Job ID:</b> ${escapeHtml(String(j.job_id))}</div>` : ``}
          </div>
        `,
        confirmButtonText: 'OK'
      });

      history.back();

    } catch (e) {
      await Swal.fire({
        icon: 'error',
        title: 'Transfer Failed',
        text: (e && e.message) ? e.message : 'Unknown error'
      });
    }

  });

  function escapeHtml(str){
    return String(str).replace(/[&<>"']/g, m => ({
      '&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#039;'
    }[m]));
  }

})();
</script>[xyz-ips snippet="Module-Sample-Receiving-Transfer"]����3�@�ۀ�Module-Sample-Receiving-Sticker�&.Q[xyz-ips snippet="Module-Sample-Receiving-Sticker"]����/b�Hމ��Location-Page-shortcode-PHP<?php
if (!defined('ABSPATH')) exit;

$radius = 100; // meters

echo '<html>';
echo '<head>';
echo '<title>Location Test</title>';
echo '<meta name="viewport" content="width=device-width, initial-scale=1">';

echo '<style>
body{
font-family:Arial;
background:#f5f5f5;
margin:0;
padding:20px;
}
.card{
max-width:520px;
margin:auto;
background:#fff;
padding:25px;
border-radius:10px;
box-shadow:0 0 10px rgba(0,0,0,0.1);
}
button{
padding:12px 16px;
background:#0073aa;
color:white;
border:none;
border-radius:6px;
cursor:pointer;
}
.result{
margin-top:15px;
padding:12px;
border-radius:6px;
}
</style>';

echo '</head>';
echo '<body>';

echo '<div class="card">';

echo '<h2>Restaurant Location Test</h2>';
echo '<p>Allowed Radius: '.$radius.' meters</p>';

echo '<button id="checkLocation">Check My Location</button>';

echo '<div id="result"></div>';

echo '</div>';

echo '<script>';

echo 'const ajaxUrl="'.admin_url('admin-ajax.php').'";';
echo 'const nonce="'.wp_create_nonce('ac_qr_geo_nonce').'";';

echo '
document.getElementById("checkLocation").addEventListener("click",function(){

const result=document.getElementById("result");

if(!navigator.geolocation){
result.innerHTML="<div class=result style=background:#fee2e2>Geolocation not supported</div>";
return;
}

result.innerHTML="<div class=result style=background:#e0f2fe>Getting location...</div>";

navigator.geolocation.getCurrentPosition(function(pos){

const lat=pos.coords.latitude;
const lng=pos.coords.longitude;

let form=new FormData();

form.append("action","ac_validate_qr_location");
form.append("nonce",nonce);
form.append("lat",lat);
form.append("lng",lng);

fetch(ajaxUrl,{
method:"POST",
credentials:"same-origin",
body:form
})
.then(r=>r.json())
.then(res=>{

if(res.success){

let d=res.data;

result.innerHTML=
"<div class=result style=background:#dcfce7>" +
"Access allowed<br>" +
"Distance: "+d.distance+" meters" +
"</div>";

}else{

let d=res.data || {};

result.innerHTML=
"<div class=result style=background:#fee2e2>" +
"Blocked<br>"+(d.message || "Too far") +
"</div>";

}

});

},function(){

result.innerHTML="<div class=result style=background:#fee2e2>Location permission denied</div>";

},{enableHighAccuracy:true});

});
';

echo '</script>';

echo '</body>';
echo '</html>';
?>[xyz-ips snippet="Location-Page-shortcode-PHP"]����pc`�=�{����������Ics�
�?�<?php
// ============================================================
// Stock Adjustment Page (XYZ PHP Snippet)
// ✅ ZXing scan button OUTSIDE the Doc No field (better visual)
// ✅ Primary color: #0082ff
// ✅ Barcode-scan-frame icon
// ✅ Beautified layout + hardened CSS vs Elementor/theme styles
// ✅ Company:
//    - Internal = readonly cached company name
//    - External = dropdown of Debtor Maintenance codes (from REST cache)
// ✅ Stock Description = dropdown of ItemCode list filtered by prefix "AK-"
// ✅ Serial No removed (AutoCount generates later)
// ✅ Qty locked to 1
// ✅ Location dropdown loads from Bridge cache via REST: GET /ac/v1/locations
// ✅ Debtor dropdown loads from Bridge cache via REST: GET /ac/v1/debtors
// ✅ AK Items dropdown loads from Bridge cache via REST: GET /ac/v1/items?prefix=AK-
// ============================================================

if (!is_user_logged_in()) {
  echo '<div style="padding:12px;border:1px solid #fca5a5;border-radius:12px;background:#fff1f2;color:#991b1b;">
    Please login to use this page.
  </div>';
  return;
}

$rest_base    = esc_url_raw(rest_url('ac/v1'));
$nonce        = wp_create_nonce('wp_rest');
$user         = wp_get_current_user();
$display      = $user ? ($user->display_name ?: $user->user_login) : '';
$company_name = get_option('ac_bridge_company_name', 'Internal');

$uid = 'ac_sa_' . wp_generate_uuid4();

// Unique element ids (avoid clashes if snippet used multiple times)
$ID = function($suffix) use ($uid) {
  return $uid . '_' . $suffix;
};
?>

<style>
  :root{
    --ac-primary:#0082ff;
    --ac-primary-dark:#006ad1;
    --ac-soft:#eaf4ff;
    --ac-border:#cfe3ff;
    --ac-text:#0f172a;
    --ac-muted:#64748b;
    --ac-shadow:0 14px 40px rgba(2, 40, 120, .10);
  }

  .ac-sketch-wrap{max-width:980px;margin:0 auto;padding:18px 0;}

  .ac-card{
    background:linear-gradient(180deg, #ffffff, #fbfdff);
    border:1px solid var(--ac-border);
    border-radius:20px;
    padding:22px;
    box-shadow:var(--ac-shadow);
  }

  .ac-topbar{display:flex;align-items:center;justify-content:space-between;gap:12px;margin-bottom:16px;}
  .ac-title{
    font-size:22px;
    font-weight:950;
    letter-spacing:.2px;
    color:var(--ac-primary);
    display:flex;
    align-items:center;
    gap:10px;
  }
  .ac-title:before{
    content:"";
    width:10px;height:10px;border-radius:999px;
    background:var(--ac-primary);
    box-shadow:0 0 0 5px rgba(0,130,255,.14);
  }

  .ac-grid{
    display:grid;
    grid-template-columns: 200px 1fr;
    gap:14px 18px;
    align-items:start;
  }
  @media (max-width: 860px){ .ac-grid{grid-template-columns:1fr;} }

  .ac-label{font-weight:850;color:var(--ac-text);padding-top:10px;}
  .ac-row{display:flex;gap:10px;align-items:center;flex-wrap:wrap;}
  .ac-muted{color:var(--ac-muted);font-size:12px;}
  .ac-help{margin-top:6px;color:var(--ac-muted);font-size:12px;}

  .ac-input, .ac-select, .ac-textarea{
    width:100%;
    max-width:520px;
    padding:12px 14px;
    border:1px solid var(--ac-border);
    border-radius:14px;
    background:#fff;
    color:var(--ac-text);
    transition:border-color .15s ease, box-shadow .15s ease, transform .05s ease;
  }
  .ac-input::placeholder, .ac-textarea::placeholder{color:#94a3b8;}
  .ac-input:focus, .ac-select:focus, .ac-textarea:focus{
    outline:none;
    border-color:var(--ac-primary);
    box-shadow:0 0 0 4px rgba(0,130,255,.16);
  }

  .ac-small{max-width:240px;}
  .ac-qty{max-width:120px;text-align:center;font-weight:950;}

  .ac-radio{display:flex;gap:10px;align-items:center;flex-wrap:wrap;}
  .ac-radio label{
    display:flex;align-items:center;gap:10px;
    padding:9px 14px;
    border:1px solid var(--ac-border);
    border-radius:999px;
    background:#f8fbff;
    cursor:pointer;
    font-weight:850;
    color:#0b1220;
    user-select:none;
    transition:background .15s ease, border-color .15s ease;
  }
  .ac-radio label:hover{border-color:rgba(0,130,255,.55);background:#f1f7ff;}
  .ac-radio input{accent-color:var(--ac-primary);}

  .ac-divider{border:none;border-top:1px solid #e6efff;margin:18px 0 14px;}

  .ac-actions{display:flex;align-items:center;justify-content:flex-end;gap:12px;margin-top:6px;}
  .ac-btn{
    border:0;cursor:pointer;
    font-weight:950;letter-spacing:.2px;
    padding:12px 18px;border-radius:14px;
    user-select:none;
  }
  .ac-btn-primary{
    background:linear-gradient(135deg,var(--ac-primary),var(--ac-primary-dark));
    color:#fff;
    box-shadow:0 12px 28px rgba(0,130,255,.35);
  }
  .ac-btn-primary:hover{filter:brightness(1.04);}
  .ac-btn-primary:active{transform:translateY(1px);}

  .ac-status{
    margin-top:14px;
    padding:14px 16px;
    border-radius:16px;
    background:linear-gradient(180deg,#0b1220,#070d18);
    color:#dbeafe;
    font-family:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono","Courier New", monospace;
    white-space:pre-wrap;
    box-shadow:inset 0 0 0 1px rgba(255,255,255,.06);
  }

  /* ✅ Scan button OUTSIDE field */
  .ac-fieldrow{
    width:100%;
    max-width:520px;
    display:grid;
    grid-template-columns: 1fr 52px;
    gap:10px;
    align-items:center;
  }
  @media (max-width: 420px){
    .ac-fieldrow{grid-template-columns:1fr 50px;}
  }

  .ac-scanbtn{
    width:52px;
    height:46px;
    border-radius:14px;
    border:1px solid var(--ac-border);
    background:var(--ac-soft);
    display:inline-flex;
    align-items:center;
    justify-content:center;
    cursor:pointer;

    /* hard overrides vs Elementor/theme */
    padding:0 !important;
    line-height:1 !important;
    font-size:0 !important;
    text-decoration:none !important;
    box-shadow:none !important;
    appearance:none;
    -webkit-appearance:none;
  }
  .ac-scanbtn:hover{background:var(--ac-primary);border-color:var(--ac-primary);}
  .ac-scanbtn:focus{outline:none;box-shadow:0 0 0 4px rgba(0,130,255,.18) !important;}
  .ac-scanbtn svg{width:22px;height:22px;display:block;color:var(--ac-primary);}
  .ac-scanbtn:hover svg{color:#fff;}

  /* Scanner modal */
  .ac-modal-backdrop{
    display:none;position:fixed;inset:0;
    background:rgba(0,20,60,.55);
    z-index:999999;
    align-items:center;
    justify-content:center;
    padding:16px;
  }
  .ac-modal{
    width:min(560px, 96vw);
    background:#fff;
    border-radius:18px;
    border:1px solid var(--ac-border);
    padding:16px;
    box-shadow:0 30px 90px rgba(0,0,0,.35);
  }
  .ac-modal-head{display:flex;align-items:center;justify-content:space-between;gap:10px;margin-bottom:10px;}
  .ac-modal-title{font-weight:950;font-size:15px;color:var(--ac-primary);}
  .ac-modal-x{border:0;background:transparent;font-size:24px;line-height:1;cursor:pointer;padding:0 6px;color:#64748b;}
  .ac-modal-actions{display:flex;gap:10px;align-items:center;flex-wrap:wrap;margin-bottom:12px;}
  .ac-modal-actions button{padding:10px 12px;border-radius:12px;border:0;cursor:pointer;font-weight:950;}
  .ac-modal-start{background:var(--ac-primary);color:#fff;}
  .ac-modal-start:hover{background:var(--ac-primary-dark);}
  .ac-modal-stop{background:#fff;border:1px solid var(--ac-border);color:#0f172a;}
  .ac-modal-status{font-size:13px;color:var(--ac-muted);}
  .ac-modal video{width:100%;border-radius:14px;background:#000;}
</style>

<div class="ac-sketch-wrap" id="<?php echo esc_attr($uid); ?>">
  <div class="ac-card">
    <div class="ac-topbar">
      <div class="ac-title">Stock Adjustment</div>
    </div>

    <div class="ac-grid">
      <!-- Date/Time -->
      <div class="ac-label">Date / Time</div>
      <div class="ac-row">
        <input class="ac-input ac-small" type="text" id="<?php echo esc_attr($ID('dt')); ?>" readonly value="<?php echo esc_attr(date('Y-m-d H:i:s')); ?>">
        <span class="ac-muted">(current)</span>
      </div>

      <!-- User -->
      <div class="ac-label">User</div>
      <div class="ac-row">
        <input class="ac-input" type="text" readonly value="<?php echo esc_attr($display); ?>">
      </div>

      <!-- Company -->
      <div class="ac-label">Company</div>
      <div>
        <div class="ac-radio">
          <label><input type="radio" name="<?php echo esc_attr($ID('company_mode')); ?>" value="internal" checked> Internal</label>
          <label><input type="radio" name="<?php echo esc_attr($ID('company_mode')); ?>" value="external"> External</label>
        </div>

        <div class="ac-row" style="margin-top:10px;">
          <!-- Internal display label (company name) -->
          <input class="ac-input"
                type="text"
                id="<?php echo esc_attr($ID('company_internal_label')); ?>"
                value="<?php echo esc_attr($company_name); ?>"
                readonly>

          <!-- Internal actual value (debtor code / AccNo) -->
          <input type="hidden"
                id="<?php echo esc_attr($ID('company_internal_value')); ?>"
                value="000-0000">

          <!-- External debtor searchable -->
          <div id="<?php echo esc_attr($ID('company_external_wrap')); ?>" style="display:none;max-width:520px;width:100%;">
            <input class="ac-input"
                  type="text"
                  id="<?php echo esc_attr($ID('company_external_search')); ?>"
                  placeholder="Type to search debtor (code / name)..."
                  autocomplete="off">

            <div id="<?php echo esc_attr($ID('company_external_list')); ?>"
                style="display:none;position:relative;">
              <div style="position:absolute;top:8px;left:0;right:0;z-index:99999;
                          background:#fff;border:1px solid var(--ac-border);border-radius:14px;
                          box-shadow:var(--ac-shadow);max-height:260px;overflow:auto;">
                <div id="<?php echo esc_attr($ID('company_external_box')); ?>"
                    style="padding:10px 12px;color:var(--ac-muted);font-size:12px;">Loading...</div>
              </div>
            </div>

            <!-- hidden value actually submitted (debtor code) -->
            <input type="hidden" id="<?php echo esc_attr($ID('company_external')); ?>" value="">
            <div class="ac-help" id="<?php echo esc_attr($ID('company_external_hint')); ?>"></div>
          </div>
        </div>

        <div class="ac-help">External company is picked from Debtor Maintenance (debtor code).</div>
      </div>

      <!-- Doc No -->
      <div class="ac-label">Doc No</div>
      <div>
        <div class="ac-fieldrow">
          <input class="ac-input" type="text" id="<?php echo esc_attr($ID('doc_no')); ?>" placeholder="(optional) e.g. manual ref / request no">
          <button type="button"
                  class="ac-scanbtn"
                  data-scan-target="<?php echo esc_attr($ID('doc_no')); ?>"
                  title="Scan Doc No"
                  aria-label="Scan Doc No">
            <!-- Barcode scan frame icon -->
            <svg viewBox="0 0 24 24" fill="none" aria-hidden="true">
              <path d="M4 7V4h3" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
              <path d="M20 7V4h-3" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
              <path d="M4 17v3h3" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
              <path d="M20 17v3h-3" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
              <path d="M6 9v6M8 9v6M10 9v6M12 9v6M14 9v6M16 9v6M18 9v6"
                    stroke="currentColor" stroke-width="1.6" stroke-linecap="round"/>
            </svg>
          </button>
        </div>
        <div class="ac-help">Optional reference number (request, memo, etc.).</div>
      </div>

      <!-- Stock Description (AK item list) -->
      <div class="ac-label">Stock Description</div>
      <div>
        <select class="ac-select"
                id="<?php echo esc_attr($ID('stock_desc')); ?>"
                style="max-width:520px;">
          <option value="">Please choose item (AK-...)</option>
        </select>
        <div class="ac-help">Dropdown loads ItemCode starting with <b>AK-</b>.</div>
      </div>

      <!-- Location -->
      <div class="ac-label">Location</div>
      <div class="ac-row">
        <select class="ac-select ac-small" id="<?php echo esc_attr($ID('location')); ?>">
          <option value="">Please choose</option>
        </select>
        <span class="ac-muted">Loaded from AutoCount</span>
      </div>

      <!-- Qty locked -->
      <div class="ac-label">Qty</div>
      <div class="ac-row">
        <input class="ac-input ac-qty"
               type="number"
               step="1"
               id="<?php echo esc_attr($ID('qty')); ?>"
               value="1"
               readonly
               disabled>
        <span class="ac-muted">Locked</span>
      </div>
    </div>

    <hr class="ac-divider">

    <div class="ac-actions">
      <button class="ac-btn ac-btn-primary" type="button" id="<?php echo esc_attr($ID('submit')); ?>">Submit</button>
    </div>

    <div class="ac-status" id="<?php echo esc_attr($ID('status')); ?>">Ready.</div>
  </div>
</div>

<!-- ✅ Scanner Modal -->
<div class="ac-modal-backdrop" id="<?php echo esc_attr($ID('scan_modal')); ?>">
  <div class="ac-modal" role="dialog" aria-modal="true">
    <div class="ac-modal-head">
      <div class="ac-modal-title" id="<?php echo esc_attr($ID('scan_title')); ?>">Scan</div>
      <button class="ac-modal-x" type="button" id="<?php echo esc_attr($ID('scan_close')); ?>">×</button>
    </div>

    <div class="ac-modal-actions">
      <button type="button" class="ac-modal-start" id="<?php echo esc_attr($ID('scan_start')); ?>">Start Scan</button>
      <button type="button" class="ac-modal-stop"  id="<?php echo esc_attr($ID('scan_stop')); ?>">Stop</button>
      <span class="ac-modal-status" id="<?php echo esc_attr($ID('scan_status')); ?>">Idle</span>
    </div>

    <video id="<?php echo esc_attr($ID('scan_video')); ?>" muted playsinline></video>

    <div style="margin-top:10px;font-size:12px;color:#64748b;">
      Tip: Good lighting + keep barcode centered. iPhone requires HTTPS and user tap to start.
    </div>
  </div>
</div>

<!-- ZXing -->
<script src="https://unpkg.com/@zxing/library@latest"></script>

<script>
(function(){
  const REST  = <?php echo json_encode($rest_base); ?>;
  const NONCE = <?php echo json_encode($nonce); ?>;
  const WP_USER = <?php echo json_encode($display); ?>;

  // Element IDs (unique per snippet instance)
  const IDS = <?php echo json_encode([
    'dt' => $ID('dt'),
    'doc' => $ID('doc_no'),
    'location' => $ID('location'),
    'qty' => $ID('qty'),
    'submit' => $ID('submit'),
    'status' => $ID('status'),
    'company_mode' => $ID('company_mode'),
    'company_internal_label' => $ID('company_internal_label'),
    'company_internal_value' => $ID('company_internal_value'),
    'company_external' => $ID('company_external'),
    'company_external_box' => $ID('company_external_box'),
    'company_external_wrap'   => $ID('company_external_wrap'),
    'company_external_search' => $ID('company_external_search'),
    'company_external_list'   => $ID('company_external_list'),
    'company_external_hint'   => $ID('company_external_hint'),

    'stock_desc' => $ID('stock_desc'),

    'scan_modal' => $ID('scan_modal'),
    'scan_title' => $ID('scan_title'),
    'scan_close' => $ID('scan_close'),
    'scan_start' => $ID('scan_start'),
    'scan_stop' => $ID('scan_stop'),
    'scan_status' => $ID('scan_status'),
    'scan_video' => $ID('scan_video'),
  ]); ?>;

  function el(id){ return document.getElementById(id); }
  function setStatus(msg){ el(IDS.status).textContent = msg; }
  function setScanStatus(msg){ el(IDS.scan_status).textContent = msg; }

  function nowStr(){
    const d = new Date();
    const pad = n => String(n).padStart(2,'0');
    return d.getFullYear()+'-'+pad(d.getIcsź�w���������X=V�
�.Q����<?php
/**
 * Template Name: Receiving Sticker (Direct Print 60x30) - CLEAN TOOL PAGE (Admin bar for admins)
 * URL: receiving-sticker/?srl=XXXX
 *
 * ✅ Full replacement:
 * - Clean tool UI (no theme header/footer/title/breadcrumb)
 * - WP admin bar visible ONLY if user is admin (logged-in + can manage options)
 * - Admin bar hidden during print
 * - Keeps MSSQL query
 * - Print-only root 60×30mm
 * - QR points to /module/sample/receiving/receiving-transfer/?srl=XXXX
 */

if (!defined('ABSPATH')) exit;

function ac_fmt_date($dt) {
  if (!$dt) return '';
  if ($dt instanceof DateTime) return $dt->format('Y-m-d');
  $ts = strtotime((string)$dt);
  return $ts ? date('Y-m-d', $ts) : (string)$dt;
}

$srl = isset($_GET['srl']) ? trim((string)$_GET['srl']) : '';
$srl = preg_replace('/[\x00-\x1F\x7F]/u', '', $srl);

$docDate = '';
$itemCode = '';
$error = '';

if ($srl === '') {
  $error = 'Missing parameter: ?srl=XXXX';
} else {
  if (!function_exists('get_mssql')) {
    $error = 'get_mssql() not found.';
  } else {
    $conn = get_mssql();
    if (!$conn) {
      $error = 'MSSQL connection failed.';
    } else {
      $sql = "
        SELECT TOP 1 DocDate, ItemCode
        FROM [AED_TEST_API_09012026].[dbo].[SerialNoTrans]
        WHERE DocType = 'SR' AND FromSerialNo = ?
        ORDER BY DocDate DESC
      ";
      $stmt = sqlsrv_prepare($conn, $sql, [$srl]);
      if ($stmt && sqlsrv_execute($stmt)) {
        $row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC);
        if ($row) {
          $docDate  = ac_fmt_date($row['DocDate'] ?? '');
          $itemCode = trim((string)($row['ItemCode'] ?? ''));
        } else {
          $error = 'Serial not found: ' . $srl;
        }
        sqlsrv_free_stmt($stmt);
      } else {
        $error = 'SQL failed.';
      }
    }
  }
}

/** QR link target */
$qr_base = home_url('/module/sample/receiving/receiving-transfer/');
$qr_link = $qr_base . '?srl=' . rawurlencode($srl);

/** Admin bar rule:
 * - show ONLY for admin
 * - else hide completely
 */
$is_admin = is_user_logged_in() && current_user_can('manage_options');

?><!doctype html>
<html <?php language_attributes(); ?>>
<head>
  <meta charset="<?php bloginfo('charset'); ?>">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title><?php echo esc_html('Receiving Sticker'); ?></title>
  <?php wp_head(); ?>
  <style>
    :root{
      --ac-bg:#f4f7fb;
      --ac-border:#e2e8f0;
      --ac-muted:#64748b;
      --ac-text:#0f172a;
      --ac-shadow:0 20px 60px rgba(15,23,42,.08);
    }

    html, body{ height:100%; }

    body.ac-sticker-body{
      margin:0 !important;
      padding:0 !important;
      background:var(--ac-bg) !important;
      font-family: system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif;
      color:var(--ac-text);
    }

    /* Kill common theme wrappers if they exist */
    .site, .site-content, .content-area, .primary, .entry-content, main, article{
      margin:0 !important;
      padding:0 !important;
      max-width:none !important;
      width:auto !important;
    }

    /* Admin bar behavior:
       - If NOT admin: hide it and remove top margin
       - If admin: keep it, and add padding so tool page doesn't hide under it
    */
    <?php if (!$is_admin): ?>
      #wpadminbar{ display:none !important; }
      html{ margin-top:0 !important; }
    <?php else: ?>
      .ac-page{
        padding-top: calc(30px + 32px); /* room for admin bar */
      }
      @media screen and (max-width: 782px){
        .ac-page{
          padding-top: calc(30px + 46px); /* mobile admin bar */
        }
      }
    <?php endif; ?>

    .ac-page{
      min-height:100vh;
      display:flex;
      align-items:center;
      justify-content:center;
      padding:30px 16px;
      box-sizing:border-box;
    }

    .ac-card{
      width:min(820px, 100%);
      background:#fff;
      border:1px solid var(--ac-border);
      border-radius:20px;
      padding:26px;
      box-shadow:var(--ac-shadow);
      box-sizing:border-box;
    }

    .ac-head{
      display:flex;
      align-items:center;
      justify-content:space-between;
      gap:12px;
      margin-bottom:16px;
    }
    .ac-title{
      font-size:16px;
      font-weight:900;
      letter-spacing:.2px;
      margin:0;
    }
    .ac-sub{
      font-size:12px;
      color:var(--ac-muted);
      font-weight:700;
      margin:0;
    }

    .ac-center{
      display:flex;
      flex-direction:column;
      align-items:center;
      justify-content:center;
      gap:14px;
    }

    /* Screen preview sticker */
    .preview{
      width: 760px;
      max-width: 94vw;
      aspect-ratio: 60 / 30;
      border:2px dashed #94a3b8;
      border-radius:16px;
      background:#fff;
      box-sizing:border-box;
      padding:18px 22px;
      display:grid;
      grid-template-columns: 280px 1fr;
      column-gap: 22px;
      align-items:center;
      margin:0 auto;
    }

    .pv-left{
      display:flex;
      flex-direction:column;
      align-items:center;
      justify-content:center;
      gap:10px;
    }
    .pv-qr{ width:220px;height:220px;display:flex;align-items:center;justify-content:center; }
    .pv-serial{
      max-width:260px;
      font-size:18px;
      font-weight:950;
      line-height:1.05;
      text-align:center;
      word-break:break-word;
    }

    .pv-right{
      min-width:0;
      display:flex;
      flex-direction:column;
      justify-content:center;
      gap:18px;
    }
    .pv-block .pv-label{
      font-size:14px;
      color:var(--ac-muted);
      font-weight:900;
      margin-bottom:6px;
    }
    .pv-block .pv-value{
      font-size:22px;
      font-weight:950;
      letter-spacing:.2px;
      word-break:break-word;
    }

    .ac-actions{
      display:flex;
      gap:10px;
      flex-wrap:wrap;
      justify-content:center;
      margin-top:2px;
    }

    .ac-btn{
      border:1px solid #d1d5db;
      background:#fff;
      color:#111827;
      padding:10px 14px;
      border-radius:12px;
      font-weight:900;
      cursor:pointer;
    }
    .ac-btn:hover{ background:#f9fafb; }

    .ac-hint{
      font-size:12px;
      color:var(--ac-muted);
      font-weight:700;
      text-align:center;
      margin-top:4px;
      word-break:break-all;
    }

    .ac-err{
      padding:12px;
      border:1px solid #fecaca;
      background:#fff1f2;
      color:#991b1b;
      border-radius:12px;
      font-weight:800;
    }

    /* ===== PRINT ROOT ===== */
    #ac_print_root{ display:none; }

    @media print {
      @page { size: 60mm 30mm; margin: 0; }

      html, body{
        width:60mm; height:30mm;
        margin:0 !important; padding:0 !important;
        background:#fff !important;
      }

      /* Always hide admin bar during printing */
      #wpadminbar{ display:none !important; }

      /* Hide everything... */
      body *{ display:none !important; }

      /* ...except print root */
      #ac_print_root, #ac_print_root *{ display:block !important; }

      #ac_print_root{
        position:fixed;
        left:0; top:0;
        width:60mm; height:30mm;
        margin:0; padding:0;
        background:#fff;
      }
    }

    /* Exact 60x30 sticker in print */
    .sticker-print{
      width:60mm;height:30mm;
      border:1px dashed #94a3b8;border-radius:6px;
      display:flex;gap:2mm;align-items:stretch;justify-content:space-between;
      padding:2mm;box-sizing:border-box;
      background:#fff;
      font-family: Arial, sans-serif;
    }
    .p-left{
      width:26mm;display:flex;flex-direction:column;align-items:center;justify-content:center;
      gap:1mm;
    }
    .p-qr{width:22mm;height:22mm;display:flex;align-items:center;justify-content:center;}
    .p-serial{
      font-size:8pt;font-weight:900;line-height:1.05;text-align:center;
      max-width:26mm;
      word-break:break-all;
    }
    .p-right{flex:1;display:flex;flex-direction:column;justify-content:center;padding-left:1mm;min-width:0;}
    .p-label{font-size:7pt;color:#64748b;font-weight:800;margin-bottom:1mm;}
    .p-value{font-size:8pt;font-weight:850;line-height:1.15;word-break:break-word;}
  </style>
</head>

<body <?php body_class('ac-sticker-body'); ?>>
<?php wp_body_open(); ?>

<?php if (!$error): ?>
  <!-- PRINT ROOT -->
  <div id="ac_print_root">
    <div class="sticker-print">
      <div class="p-left">
        <div class="p-qr" id="qr_print"></div>
        <div class="p-serial"><?php echo esc_html($srl); ?></div>
      </div>
      <div class="p-right">
        <div class="p-label">DocDate</div>
        <div class="p-value"><?php echo esc_html($docDate ?: '-'); ?></div>
        <div style="height:2mm;"></div>
        <div class="p-label">ItemCode</div>
        <div class="p-value"><?php echo esc_html($itemCode ?: '-'); ?></div>
      </div>
    </div>
  </div>
<?php endif; ?>

<div class="ac-page">
  <div class="ac-card">
    <div class="ac-head">
      <div>
        <p class="ac-title">Receiving Sticker</p>
        <p class="ac-sub">60×30mm direct print layout</p>
      </div>
      <?php if (!$error): ?>
        <div style="text-align:right">
          <p class="ac-sub" style="margin:0;">Serial</p>
          <p class="ac-title" style="margin:0;font-size:14px;"><?php echo esc_html($srl); ?></p>
        </div>
      <?php endif; ?>
    </div>

    <?php if ($error): ?>
      <div class="ac-err"><?php echo esc_html($error); ?></div>
    <?php else: ?>
      <div class="ac-center">
        <div class="preview">
          <div class="pv-left">
            <div class="pv-qr" id="qr_preview"></div>
            <div class="pv-serial"><?php echo esc_html($srl); ?></div>
          </div>

          <div class="pv-right">
            <div class="pv-block">
              <div class="pv-label">DocDate</div>
              <div class="pv-value"><?php echo esc_html($docDate ?: '-'); ?></div>
            </div>
            <div class="pv-block">
              <div class="pv-label">ItemCode</div>
              <div class="pv-value"><?php echo esc_html($itemCode ?: '-'); ?></div>
            </div>
          </div>
        </div>

        <div class="ac-actions">
          <button class="ac-btn" id="ac_print_btn" type="button">Print / Save as PDF</button>
        </div>

        <div class="ac-hint">
          QR Link: <?php echo esc_html($qr_link); ?>
        </div>
      </div>

      <script src="https://cdnjs.cloudflare.com/ajax/libs/qrcodejs/1.0.0/qrcode.min.js"></script>
      <script>
      (function(){
        const qrLink = <?php echo json_encode($qr_link); ?>;

        new QRCode(document.getElementById("qr_preview"), {
          text: qrLink,
          width: 220,
          height: 220,
          correctLevel: QRCode.CorrectLevel.M
        });

        new QRCode(document.getElementById("qr_print"), {
          text: qrLink,
          width: 220,
          height: 220,
          correctLevel: QRCode.CorrectLevel.M
        });

        document.getElementById("ac_print_btn").addEventListener("click", function(){
          const box = document.getElementById("qr_print");
          const t0 = Date.now();

          (function waitReady(){
            if (box.querySelector("canvas,img")) { window.print(); return; }
            if (Date.now() - t0 > 2000) { window.print(); return; }
            setTimeout(waitReady, 50);
          })();
        });
      })();
      </script>
    <?php endif; ?>
  </div>
</div>

<?php wp_footer(); ?>
</body>
</html>X=VȚ����������Ics�
�;�����Month()+1)+'-'+pad(d.getDate())+' '+pad(d.getHours())+':'+pad(d.getMinutes())+':'+pad(d.getSeconds());
  }

  function makeClientRequestId(prefix){
    const rnd = Math.random().toString(36).slice(2);
    const ts  = Date.now().toString(36);
    return (prefix || "REQ") + "-" + ts + "-" + rnd;
  }

  async function sleep(ms){ return new Promise(r=>setTimeout(r, ms)); }

  // Company value:
  // - internal => cached company name (text)
  // - external => debtor code (from dropdown)
  function companyValue(){
    const mode = document.querySelector('input[name="'+IDS.company_mode+'"]:checked')?.value || 'internal';
    if (mode === 'external') return (el(IDS.company_external).value || '').trim(); // debtor code
    return (el(IDS.company_internal_value).value || '000-0000').trim();            // internal debtor code
  }

  // =========================================================
  // Load dropdown data from WP cache (BridgeWorker syncs)
  // =========================================================
  async function loadLocations(){
    const sel = el(IDS.location);
    if (!sel) return;

    sel.innerHTML = '<option value="">Please choose</option>';

    try{
      const res = await fetch(REST + "/locations", {
        method:"GET",
        headers:{ "X-WP-Nonce": NONCE },
        credentials:"same-origin",
        cache:"no-store"
      });
      const data = await res.json();
      if (!data || !data.ok || !Array.isArray(data.locations)) return;

      for (const loc of data.locations){
        const v = String(loc || "").trim();
        if (!v) continue;
        const opt = document.createElement("option");
        opt.value = v;
        opt.textContent = v;
        sel.appendChild(opt);
      }
    }catch(e){
      console.error("loadLocations failed", e);
    }
  }

  // =========================================================
  // Debtors (cached list) + Search dropdown UI
  // =========================================================
  let DEBTORS = [];

  function hideDebtorList(){
    const wrap = el(IDS.company_external_list);
    if (wrap) wrap.style.display = "none";
  }

  function showDebtorList(items){
    const wrap = el(IDS.company_external_list);
    const box  = el(IDS.company_external_box);
    if (!wrap || !box) return;

    if (!items.length){
      box.innerHTML = '<div style="padding:10px 12px;color:var(--ac-muted);font-size:12px;">No matches</div>';
      wrap.style.display = "block";
      return;
    }

    box.innerHTML = items.map(x => {
      const code = String(x.code || "").trim();
      const name = String(x.name || "").trim();
      return `
        <button type="button"
          data-code="${code.replace(/"/g,'&quot;')}"
          data-name="${name.replace(/"/g,'&quot;')}"
          style="width:100%;text-align:left;padding:10px 12px;border:0;background:#fff;cursor:pointer;">
          <div style="font-weight:900;color:var(--ac-text);">${code}</div>
          ${name ? `<div style="font-size:12px;color:var(--ac-muted);">${name}</div>` : ``}
        </button>
      `;
    }).join("");

    wrap.style.display = "block";
  }

  function setSelectedDebtor(code, name){
    code = String(code || "").trim();
    name = String(name || "").trim();

    el(IDS.company_external).value = code; // hidden code
    el(IDS.company_external_search).value = code + (name ? " — " + name : "");

    const hint = el(IDS.company_external_hint);
    if (hint) hint.textContent = "Selected: " + code + (name ? " — " + name : "");

    hideDebtorList();
  }

  async function loadDebtors(){
    const box  = el(IDS.company_external_box);
    const hint = el(IDS.company_external_hint);

    if (box)  box.innerHTML = '<div style="padding:10px 12px;color:var(--ac-muted);font-size:12px;">Loading...</div>';
    if (hint) hint.textContent = "";

    try{
      const res = await fetch(REST + "/debtors", {
        method:"GET",
        headers:{ "X-WP-Nonce": NONCE },
        credentials:"same-origin",
        cache:"no-store"
      });

      const data = await res.json();

      if (!data || !data.ok || !Array.isArray(data.debtors)) {
        if (hint) hint.textContent = "Failed to load debtors.";
        DEBTORS = [];
        return;
      }

      DEBTORS = data.debtors.map(d => ({
        code: String(d.code || "").trim(),
        name: String(d.name || "").trim()
      })).filter(x => x.code);

      if (hint) hint.textContent = "Loaded " + DEBTORS.length + " debtors.";
    }catch(e){
      console.error("loadDebtors failed", e);
      if (hint) hint.textContent = "Error loading debtors.";
      DEBTORS = [];
    }
  }


  // GET /ac/v1/items?prefix=AK- => { ok:true, items:["AK-001", ...] }
  async function loadAkItems(){
    const sel = el(IDS.stock_desc);
    if (!sel) return;

    sel.innerHTML = '<option value="">Please choose item (AK-...)</option>';

    try{
      const res = await fetch(REST + "/items?prefix=AK-", {
        method:"GET",
        headers:{ "X-WP-Nonce": NONCE },
        credentials:"same-origin",
        cache:"no-store"
      });
      const data = await res.json();
      if (!data || !data.ok || !Array.isArray(data.items)) return;

      for (const it of data.items){
        const code = String(it || "").trim();
        if (!code) continue;
        const opt = document.createElement("option");
        opt.value = code;
        opt.textContent = code;
        sel.appendChild(opt);
      }
    }catch(e){
      console.error("loadAkItems failed", e);
    }
  }

  // =========================================================
  // Scanner modal logic (Doc No only)
  // =========================================================
  let scanTargetId = null;
  let codeReader = null;

  function openScanner(targetId, title){
    scanTargetId = targetId;
    el(IDS.scan_title).textContent = title || "Scan";
    el(IDS.scan_modal).style.display = "flex";
    setScanStatus("Idle");
  }

  function closeScanner(){
    stopScan();
    el(IDS.scan_modal).style.display = "none";
    scanTargetId = null;
  }

  async function startScan(){
    try{
      if (!scanTargetId) return;
      if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) {
        throw new Error("Camera API not available in this browser.");
      }
      if (!window.ZXing) throw new Error("ZXing not loaded");

      codeReader = new ZXing.BrowserMultiFormatReader();
      setScanStatus("Requesting camera permission...");

      const constraints = {
        video: {
          facingMode: { ideal: "environment" },
          width: { ideal: 1280 },
          height: { ideal: 720 }
        },
        audio: false
      };

      setScanStatus("Scanning...");

      await codeReader.decodeFromConstraints(constraints, el(IDS.scan_video), (result, err) => {
        if (!result) return;

        // If you want to allow other formats, remove this filter
        if (result.getBarcodeFormat && result.getBarcodeFormat() !== ZXing.BarcodeFormat.CODE_128) return;

        const text = result.getText ? result.getText() : String(result || "");
        const input = document.getElementById(scanTargetId);

        if (input) {
          input.value = text;
          input.dispatchEvent(new Event("input", {bubbles:true}));
        }

        setScanStatus("✅ Found: " + text);

        stopScan();
        setTimeout(closeScanner, 350);
      });

    }catch(e){
      console.error(e);
      setScanStatus("❌ " + (e.message || "Failed to start camera"));
    }
  }

  function stopScan(){
    try{
      if (codeReader) {
        codeReader.reset();
        codeReader = null;
      }
      const video = el(IDS.scan_video);
      if (video && video.srcObject) {
        video.srcObject.getTracks().forEach(t => t.stop());
        video.srcObject = null;
      }
      setScanStatus("Stopped");
    }catch(e){
      console.error(e);
      setScanStatus("Stopped");
    }
  }

  // Click scan button (Doc No only)
  document.addEventListener("click", function(e){
    const btn = e.target.closest(".ac-scanbtn");
    if (!btn) return;

    const targetId = btn.getAttribute("data-scan-target");
    if (!targetId) return;

    openScanner(targetId, "Scan Doc No");
  });

  // Modal controls
  el(IDS.scan_close).addEventListener("click", closeScanner);
  el(IDS.scan_start).addEventListener("click", startScan);
  el(IDS.scan_stop).addEventListener("click", stopScan);

  // Close if click outside modal
  el(IDS.scan_modal).addEventListener("click", function(e){
    if (e.target === el(IDS.scan_modal)) closeScanner();
  });

  // ESC to close
  document.addEventListener("keydown", function(e){
    if (e.key === "Escape" && el(IDS.scan_modal).style.display === "flex") closeScanner();
  });

  // Company toggle: internal vs external dropdown (guarded)
  document.addEventListener("change", function(e){
    if (!(e.target && e.target.name === IDS.company_mode)) return;

    const mode = e.target.value;

    const extWrap = el(IDS.company_external_wrap);
    const internalLabel = el(IDS.company_internal_label);
    const searchInput = el(IDS.company_external_search);

    if (mode === "external"){
      if (extWrap) extWrap.style.display = "";
      if (internalLabel) internalLabel.style.display = "none";
      if (searchInput) searchInput.focus();
    } else {
      if (extWrap) extWrap.style.display = "none";
      if (internalLabel) internalLabel.style.display = "";
      hideDebtorList();
    }
  });

  // =========================================================
  // Job polling
  // =========================================================
  async function pollJob(jobId){
    for (let i=0;i<45;i++){
      const res = await fetch(REST + "/job/" + jobId, {
        method:"GET",
        headers:{ "X-WP-Nonce": NONCE },
        credentials:"same-origin",
        cache:"no-store"
      });

      const data = await res.json();

      if (!data || !data.ok){
        setStatus("Error reading job: " + (data && data.message ? data.message : "Unknown"));
        return;
      }

      const status = data.status || (data.job && data.job.status) || "";
      const err    = data.error  || (data.job && data.job.error_message) || "";

      if (status === "PENDING" || status === "PROCESSING"){
        setStatus("Job #" + jobId + " status: " + status + "\nWaiting for AutoCount worker...");
        await sleep(2000);
        continue;
      }

      if (status === "SUCCESS"){
        const result = data.result || (data.job && data.job.result) || null;
        setStatus("SUCCESS ✅\n\n" + JSON.stringify(result, null, 2));
        return;
      }

      if (status === "FAILED" || status === "FAILED_FINAL"){
        setStatus(status + " ❌\n\n" + (err || "Unknown error"));
        return;
      }

      setStatus("Unknown job status: " + status);
      return;
    }
    setStatus("Timed out waiting for AutoCount worker.");
  }

  // =========================================================
  // Submit Stock Receive
  // =========================================================
  async function submitStockReceive(){
    const location = (el(IDS.location).value || "").trim();
    if (!location){
      setStatus("Please choose Location.");
      el(IDS.location).focus();
      return;
    }

    // ItemCode from dropdown (AK-...)
    const itemCode = (el(IDS.stock_desc).value || "").trim();
    if (!itemCode){
      setStatus("Please choose Item (AK-...).");
      el(IDS.stock_desc).focus();
      return;
    }

    // Company: internal text OR external debtor code
    const mode = document.querySelector('input[name="'+IDS.company_mode+'"]:checked')?.value || 'internal';
    const company = companyValue();
    if (mode === "external" && !company){
      setStatus("Please choose Debtor (External Company).");
      // focus the search input instead of hidden field
      el(IDS.company_external_search).focus();
      return;
    }

    // Qty locked at 1
    const qty = 1;

    // Doc No from input -> AutoCount "Ref. Doc. No."
    const docNoRef = (el(IDS.doc).value || "").trim();

    const payload = {
      // ✅ for WP validation
      itemCode: itemCode,
      qty: qty,

      // common/header
      location: location,
      refDocNo: docNoRef,               // -> Ref. Doc. No.
      udfCustomer: company,             // -> UDF_CUSTOMER
      udfUser: (WP_USER || "").trim(),  // -> UDF_USER
      companyMode: mode,

      // ✅ for C# BridgeWorker ExecuteStockReceive()
      lines: [
        { itemCode: itemCode, qty: qty }
      ]
    };

    setStatus("Submitting STOCK_RECEIVE job...");

    const res = await fetch(REST + "/job", {
      method:"POST",
      headers:{ "Content-Type":"application/json", "X-WP-Nonce": NONCE },
      credentials:"same-origin",
      body: JSON.stringify({
        // new + old keys together
        type: "STOCK_RECEIVE",
        job_type: "STOCK_RECEIVE",

        clientReq: makeClientRequestId("SR"),
        client_request_id: makeClientRequestId("SR"),

        payload: payload
      })
    });

    const data = await res.json();
    if (!data || !data.ok){
      setStatus("Submit failed: " + (data && data.message ? data.message : "Unknown"));
      return;
    }

    setStatus("Submitted ✅\nJobId: " + data.jobId + "\nPolling status...");
    pollJob(data.jobId);
  }

  // =========================================================
  // Init
  // =========================================================
  const submitBtn = el(IDS.submit);
  if (submitBtn) submitBtn.addEventListener("click", submitStockReceive);

  const dt = el(IDS.dt);
  if (dt) dt.value = nowStr();

  // ensure qty stays 1 even if browser tries to change it
  if (el(IDS.qty)) el(IDS.qty).value = "1";

  loadLocations();
  loadDebtors();

  // Debtor search events
  el(IDS.company_external_search).addEventListener("input", function(){
    const q = (this.value || "").trim().toLowerCase();

    el(IDS.company_external).value = "";

    if (!q){
      hideDebtorList();
      return;
    }

    if (!DEBTORS.length){
      showDebtorList([]); // "No matches" (or still loading)
      return;
    }

    const hits = DEBTORS.filter(d =>
      d.code.toLowerCase().includes(q) || (d.name && d.name.toLowerCase().includes(q))
    ).slice(0, 30);

    showDebtorList(hits);
  });

  // click on a debtor
  el(IDS.company_external_list).addEventListener("click", function(e){
    const btn = e.target.closest("button[data-code]");
    if (!btn) return;
    setSelectedDebtor(btn.getAttribute("data-code"), btn.getAttribute("data-name"));
  });

  // close list when click outside
  document.addEventListener("click", function(e){
    const wrap = el(IDS.company_external_wrap);
    if (!wrap) return;
    if (!wrap.contains(e.target)) hideDebtorList();
  });

  loadAkItems();

})();
</script>Ics�`����������Id�
�!���<?php
/**
 * Template Name: Receiving Sticker (Direct Print 60x30) - Fixed Print + Center
 * URL: receiving-sticker/?srl=XXXX
 */

if (!defined('ABSPATH')) exit;

function ac_fmt_date($dt) {
  if (!$dt) return '';
  if ($dt instanceof DateTime) return $dt->format('Y-m-d');
  $ts = strtotime((string)$dt);
  return $ts ? date('Y-m-d', $ts) : (string)$dt;
}

get_header();

$srl = isset($_GET['srl']) ? trim((string)$_GET['srl']) : '';
$srl = preg_replace('/[\x00-\x1F\x7F]/u', '', $srl);

$docDate = '';
$itemCode = '';
$error = '';

if ($srl === '') {
  $error = 'Missing parameter: ?srl=XXXX';
} else {
  if (!function_exists('get_mssql')) {
    $error = 'get_mssql() not found.';
  } else {
    $conn = get_mssql();
    if (!$conn) {
      $error = 'MSSQL connection failed.';
    } else {
      $sql = "
        SELECT TOP 1 DocDate, ItemCode
        FROM [AED_TEST_API_09012026].[dbo].[SerialNoTrans]
        WHERE DocType = 'SR' AND FromSerialNo = ?
        ORDER BY DocDate DESC
      ";
      $stmt = sqlsrv_prepare($conn, $sql, [$srl]);
      if ($stmt && sqlsrv_execute($stmt)) {
        $row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC);
        if ($row) {
          $docDate  = ac_fmt_date($row['DocDate'] ?? '');
          $itemCode = trim((string)($row['ItemCode'] ?? ''));
        } else {
          $error = 'Serial not found: ' . esc_html($srl);
        }
        sqlsrv_free_stmt($stmt);
      } else {
        $error = 'SQL failed.';
      }
    }
  }
}
?>

<style>
  :root{
    --ac-border:#cfe3ff;
    --ac-muted:#64748b;
    --ac-text:#0f172a;
    --ac-shadow:0 14px 40px rgba(2, 40, 120, .10);
  }

  /* ===== Page center ===== */
  .ac-page-center{
    min-height: calc(100vh - 140px);
    display:flex;
    align-items:center;
    justify-content:center;
    padding: 18px 16px 40px;
    font-family: system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif;
  }
  .ac-card{
    width: min(980px, 100%);
    background:linear-gradient(180deg,#fff,#fbfdff);
    border:1px solid var(--ac-border);
    border-radius:18px;
    padding:18px;
    box-shadow:var(--ac-shadow);
  }

  .ac-center{
    display:flex;
    flex-direction:column;
    align-items:center;
    justify-content:center;
    gap:14px;
    padding: 10px 0 4px;
  }

  /* ===== Screen preview sticker (balanced) ===== */
  .preview{
    width: 760px;
    max-width: 94vw;
    aspect-ratio: 60 / 30;
    border:2px dashed #94a3b8;
    border-radius:16px;
    background:#fff;
    box-sizing:border-box;
    padding: 18px 22px;
    display:grid;
    grid-template-columns: 280px 1fr;  /* left fixed, right flexible */
    column-gap: 22px;
    align-items:center;
    margin: 0 auto;
  }

  .pv-left{
    display:flex;
    flex-direction:column;
    align-items:center;
    justify-content:center;
    gap:10px;
  }
  .pv-qr{
    width: 220px;
    height: 220px;
    display:flex;
    align-items:center;
    justify-content:center;
  }
  .pv-serial{
    max-width: 260px;
    font-size:18px;
    font-weight:950;
    line-height:1.05;
    text-align:center;
    color:var(--ac-text);
    word-break: break-word;
  }

  .pv-right{
    min-width:0;
    display:flex;
    flex-direction:column;
    justify-content:center;
    gap:18px;
  }
  .pv-block .pv-label{
    font-size:16px;
    color:var(--ac-muted);
    font-weight:900;
    margin-bottom:6px;
  }
  .pv-block .pv-value{
    font-size:24px;
    font-weight:950;
    color:var(--ac-text);
    letter-spacing:.2px;
    word-break:break-word;
  }

  .ac-btn{
    border:1px solid #d1d5db;
    background:#fff;
    color:#111827;
    padding:10px 14px;
    border-radius:12px;
    font-weight:900;
    cursor:pointer;
  }
  .ac-btn:hover{ background:#f9fafb; }

  .ac-err{
    padding:12px;border:1px solid #fecaca;background:#fff1f2;color:#991b1b;border-radius:12px;
  }

  /* ===== PRINT ROOT =====
     This is the only thing we print. It's outside theme stuff. */
  #ac_print_root{ display:none; }

  @media print {
    @page { size: 60mm 30mm; margin: 0; }

    html, body{
      width:60mm; height:30mm;
      margin:0 !important; padding:0 !important;
      background:#fff !important;
    }

    /* Hide EVERYTHING... */
    header, footer, .site-header, .site-footer,
    .ac-page-center { display:none !important; }

    /* ...except print root */
    #ac_print_root{
      display:block !important;
      position: fixed;
      left:0; top:0;
      width:60mm; height:30mm;
      margin:0; padding:0;
      background:#fff;
    }
  }

  /* Exact 60x30 sticker in print */
  .sticker-print{
    width:60mm;height:30mm;
    border:1px dashed #94a3b8;border-radius:6px;
    display:flex;gap:2mm;align-items:stretch;justify-content:space-between;
    padding:2mm;box-sizing:border-box;
    background:#fff;
    font-family: Arial, sans-serif;
  }
  .p-left{
    width:26mm;display:flex;flex-direction:column;align-items:center;justify-content:center;
    gap:1mm;
  }
  .p-qr{width:22mm;height:22mm;display:flex;align-items:center;justify-content:center;}
  .p-serial{
    font-size:8pt;font-weight:900;line-height:1.05;text-align:center;
    max-width:26mm;
    word-break:break-all;
  }
  .p-right{flex:1;display:flex;flex-direction:column;justify-content:center;padding-left:1mm;min-width:0;}
  .p-label{font-size:7pt;color:#64748b;font-weight:800;margin-bottom:1mm;}
  .p-value{font-size:8pt;font-weight:850;line-height:1.15;word-break:break-word;}
</style>

<?php if (!$error): ?>
  <!-- PRINT ROOT (must be OUTSIDE anything we hide) -->
  <div id="ac_print_root">
    <div class="sticker-print">
      <div class="p-left">
        <div class="p-qr" id="qr_print"></div>
        <div class="p-serial"><?php echo esc_html($srl); ?></div>
      </div>
      <div class="p-right">
        <div class="p-label">DocDate</div>
        <div class="p-value"><?php echo esc_html($docDate ?: '-'); ?></div>
        <div style="height:2mm;"></div>
        <div class="p-label">ItemCode</div>
        <div class="p-value"><?php echo esc_html($itemCode ?: '-'); ?></div>
      </div>
    </div>
  </div>
<?php endif; ?>

<div class="ac-page-center">
  <div class="ac-card">
    <?php if ($error): ?>
      <div class="ac-err"><?php echo esc_html($error); ?></div>
    <?php else: ?>
      <div class="ac-center">
        <div class="preview">
          <div class="pv-left">
            <div class="pv-qr" id="qr_preview"></div>
            <div class="pv-serial"><?php echo esc_html($srl); ?></div>
          </div>
          <div class="pv-right">
            <div class="pv-block">
              <div class="pv-label">DocDate</div>
              <div class="pv-value"><?php echo esc_html($docDate ?: '-'); ?></div>
            </div>
            <div class="pv-block">
              <div class="pv-label">ItemCode</div>
              <div class="pv-value"><?php echo esc_html($itemCode ?: '-'); ?></div>
            </div>
          </div>
        </div>

        <button class="ac-btn" id="ac_print_btn" type="button">Print / Save as PDF</button>
      </div>

      <script src="https://cdnjs.cloudflare.com/ajax/libs/qrcodejs/1.0.0/qrcode.min.js"></script>
<script>
(function(){
  const serial = <?php echo json_encode($srl); ?>;

  // ✅ Build the target link for QR
  // Result: (wp)/module/sample/receiving/receiving-transfer/?srl=(serial)
  const qrLink =
    <?php echo json_encode(home_url('/module/sample/receiving/receiving-transfer/')); ?>
    + '?srl=' + encodeURIComponent(serial);

  // Preview QR
  new QRCode(document.getElementById("qr_preview"), {
    text: qrLink,
    width: 220,
    height: 220,
    correctLevel: QRCode.CorrectLevel.M
  });

  // Print QR (render now so print isn't blank)
  new QRCode(document.getElementById("qr_print"), {
    text: qrLink,
    width: 220,
    height: 220,
    correctLevel: QRCode.CorrectLevel.M
  });

  // Print: wait for QR canvas/img exists
  document.getElementById("ac_print_btn").addEventListener("click", function(){
    const box = document.getElementById("qr_print");
    const t0 = Date.now();

    (function waitReady(){
      if (box.querySelector("canvas,img")) {
        window.print();
        return;
      }
      if (Date.now() - t0 > 2000) { // fallback
        window.print();
        return;
      }
      setTimeout(waitReady, 50);
    })();
  });
})();
</script>
    <?php endif; ?>
  </div>
</div>

<?php get_footer(); ?>Id�8DF���������Y�Y�
�?�
<?php
if (!defined('ABSPATH')) exit;

if (!is_user_logged_in()) {
  echo '<div style="padding:12px;border:1px solid #fca5a5;border-radius:12px;background:#fff1f2;color:#991b1b;">
    Please login to use this page.
  </div>';
  return;
}

$ajax_url   = admin_url('admin-ajax.php');
$ajax_nonce = wp_create_nonce('ac_qc_page_nonce');

/**
 * ===========================
 * Detect mode from page slug
 * ===========================
 * qc-test    => QC
 * complete  => HQ
 * dispose   => DMP
 */
$slug = '';
$post = get_post();
if ($post) $slug = (string) get_post_field('post_name', $post);

$to_location = 'QC'; // default
$mode_label  = 'QC';

switch ($slug) {
  case 'qc-test':
    $to_location = 'QC';
    $mode_label  = 'QC';
    break;
  case 'complete':
    $to_location = 'HQ';
    $mode_label  = 'HQ (Complete)';
    break;
  case 'dispose':
    $to_location = 'DMP';
    $mode_label  = 'DMP (Dispose)';
    break;
}

/**
 * ===========================
 * Bridge status endpoint
 * ===========================
 * Expected JSON (example):
 * {
 *   "state":"online|stale|offline",
 *   "age_seconds": 12,
 *   "last_seen_utc":"2026-02-27T08:10:00Z",
 *   "last_error":""
 * }
 */
$bridgeAgentId  = 'WEB-AUTOCOUNT';
$bridgeStatusUrl = home_url('/wp-json/ac/v1/bridge/status') . '?agent_id=' . rawurlencode($bridgeAgentId);
?>

<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
<script src="https://unpkg.com/html5-qrcode" type="text/javascript"></script>

<style>
  :root{
    --card:#ffffff;
    --border:#e6e9f0;
    --text:#0f172a;
    --muted:#64748b;

    --dark:#111827;
    --green:#16a34a;

    --radius:14px;
    --shadow:0 8px 22px rgba(2,6,23,.06);
    --shadow-soft:0 6px 16px rgba(2,6,23,.05);

    --okbg:#ecfdf5; --okfg:#065f46; --okbd:#a7f3d0;
    --warnbg:#fffbeb; --warnfg:#92400e; --warnbd:#fde68a;
    --badbg:#fef2f2; --badfg:#991b1b; --badbd:#fecaca;
    --unkbg:#f1f5f9; --unkfg:#334155; --unkbd:#e2e8f0;
  }

  .qc-wrap{max-width:1000px;margin:0 auto;padding:18px;}
  .qc-card{background:var(--card);border:1px solid var(--border);border-radius:var(--radius);box-shadow:var(--shadow);padding:14px;}

  .qc-headrow{display:flex;align-items:flex-start;justify-content:space-between;gap:10px;margin-bottom:10px;}
  .qc-title{margin:0;font-size:15px;font-weight:900;color:var(--text);letter-spacing:.2px;line-height:1.2;}
  .qc-sub{margin-top:3px;font-size:12px;color:var(--muted);line-height:1.25;}
  .qc-headleft{display:flex;flex-direction:column;gap:8px;}
  .qc-headright{display:flex;gap:8px;align-items:center;flex-wrap:wrap;}

  .qc-pill{
    display:inline-flex;align-items:center;gap:8px;
    font-size:12px;font-weight:900;padding:6px 10px;border-radius:999px;border:1px solid transparent;
    white-space:nowrap;
  }
  .qc-pill.ok{ background:var(--okbg); color:var(--okfg); border-color:var(--okbd); }
  .qc-pill.warn{ background:var(--warnbg); color:var(--warnfg); border-color:var(--warnbd); }
  .qc-pill.bad{ background:var(--badbg); color:var(--badfg); border-color:var(--badbd); }
  .qc-pill.unk{ background:var(--unkbg); color:var(--unkfg); border-color:var(--unkbd); }

  .qc-statusline{
    font-size:12px;color:var(--muted);line-height:1.25;
  }
  .qc-statusline b{ color:var(--text); }

  .qc-controls{display:flex;align-items:center;gap:10px;flex-wrap:wrap;}

  .qc-input{
    flex:1;min-width:240px;border:1px solid #d8dde8;border-radius:12px;
    padding:10px 12px;font-size:14px;outline:none;background:#fff;height:42px;
  }
  .qc-input:focus{border-color: rgba(11,92,255,.55);box-shadow: 0 0 0 4px rgba(11,92,255,.10);}

  .qc-btn{
    border:0;border-radius:12px;height:42px;padding:0 14px;font-weight:900;font-size:13px;
    cursor:pointer;user-select:none;transition: transform .08s ease, filter .15s ease, box-shadow .15s ease;white-space:nowrap;
  }
  .qc-btn:active{ transform: translateY(1px); }
  .qc-btn:hover{ filter: brightness(.985); }

  .qc-btn.dark{ background:var(--dark); color:#fff; box-shadow:var(--shadow-soft); }
  .qc-btn.green{ background:var(--green); color:#fff; box-shadow:0 10px 18px rgba(22,163,74,.14); }
  .qc-btn.gray{ background:#eef2f7; color:#0f172a; box-shadow:var(--shadow-soft); border:1px solid #dde3ee; }

  .qc-skip{
    display:flex;align-items:center;gap:10px;flex-wrap:wrap;margin-top:10px;
    padding-top:10px;border-top:1px dashed #edf0f6;
  }
  .qc-skip label{display:flex;align-items:center;gap:8px;cursor:pointer;font-weight:800;font-size:12px;color:var(--text);}
  .qc-skip small{font-size:12px;color:var(--muted);}

  /* scanner modal */
  .qc-scan-modal{
    display:none;position:fixed;inset:0;background:rgba(2,6,23,.58);
    z-index:999999;padding:18px;
  }
  .qc-scan-modal.is-open{ display:flex; align-items:center; justify-content:center; }
  .qc-scan-panel{
    width:min(520px, 96vw);background:#fff;border:1px solid var(--border);
    border-radius:16px;box-shadow:0 30px 90px rgba(2,6,23,.35);overflow:hidden;
  }
  .qc-scan-head{display:flex; align-items:center; justify-content:space-between;padding:10px 12px;border-bottom:1px solid var(--border);}
  .qc-scan-head strong{ font-weight:900; color:var(--text); font-size:13px; }
  .qc-scan-close{
    border:0;background:#fff;border:1px solid var(--border);
    width:34px; height:34px;border-radius:10px;cursor:pointer;font-weight:900;color:var(--muted);
  }
  .qc-scan-close:hover{ background:#f8fafc; color:var(--text); }
  #qcReader{ width:100%; }
  .qc-scan-foot{padding:10px 12px;border-top:1px solid var(--border);font-size:12px;color:var(--muted);background:#fbfcff;}

  @media (max-width: 820px){
    .qc-wrap{ padding:14px; }
    .qc-card{ padding:12px; }
    .qc-controls .qc-input{ min-width: 100%; }
    .qc-controls .qc-btn{ flex:1; }
    .qc-headrow{ gap:8px; }
    .qc-headright{ width:100%; }
  }
</style>

<div class="qc-wrap"
     data-ajax-url="<?php echo esc_attr($ajax_url); ?>"
     data-ajax-nonce="<?php echo esc_attr($ajax_nonce); ?>"
     data-to-location="<?php echo esc_attr($to_location); ?>"
     data-mode-label="<?php echo esc_attr($mode_label); ?>"
     data-bridge-status-url="<?php echo esc_attr($bridgeStatusUrl); ?>"
     data-bridge-agent-id="<?php echo esc_attr($bridgeAgentId); ?>">

  <div class="qc-card">
    <div class="qc-headrow">
      <div class="qc-headleft">
        <div>
          <h2 class="qc-title">Scan / Enter Serial</h2>
          <div class="qc-sub">
            Scan QR/Barcode or paste serial, then transfer to <b><?php echo esc_html($mode_label); ?></b>.
          </div>
        </div>

        <!-- Bridge status -->
        <div>
          <span class="qc-pill unk" id="qcBridgePill">Bridge: UNKNOWN</span>
          <div class="qc-statusline" id="qcBridgeText">Checking bridge status…</div>
          <div class="qc-statusline" id="qcLastMsg" style="margin-top:2px;display:none;"></div>
        </div>
      </div>

      <div class="qc-headright">
        <button id="qcScanBtn" class="qc-btn dark" type="button">Scan</button>
        <button id="qcClearBtn" class="qc-btn gray" type="button">Clear</button>
      </div>
    </div>

    <div class="qc-controls">
      <input id="qcSerialInput" class="qc-input" placeholder="e.g. AK-COAL-000123" autocomplete="off" inputmode="text" />
      <button id="qcTransferBtnTop" class="qc-btn green" type="button">
        Transfer to <?php echo esc_html($to_location); ?>
      </button>
    </div>

    <div class="qc-skip">
      <label>
        <input type="checkbox" id="qcSkipConfirm" />
        Skip confirmation
      </label>
      <small>(Saved on this device)</small>
    </div>
  </div>
</div>

<!-- Scanner Modal -->
<div class="qc-scan-modal" id="qcScanModal" aria-hidden="true">
  <div class="qc-scan-panel" role="dialog" aria-modal="true" aria-label="QR Scanner">
    <div class="qc-scan-head">
      <strong>Scan QR / Barcode</strong>
      <button class="qc-scan-close" type="button" id="qcScanClose" aria-label="Close">✕</button>
    </div>
    <div id="qcReader"></div>
    <div class="qc-scan-foot">Point your camera at the QR/barcode — it will fill the serial automatically.</div>
  </div>
</div>

<script>
(function(){
  const wrap      = document.querySelector('.qc-wrap');
  const AJAX_URL  = wrap.dataset.ajaxUrl;
  const NONCE     = wrap.dataset.ajaxNonce;

  const TO_LOC    = wrap.dataset.toLocation || 'QC';
  const MODE_LBL  = wrap.dataset.modeLabel || TO_LOC;

  const BRIDGE_URL = wrap.dataset.bridgeStatusUrl || '';
  const BRIDGE_AGENT = wrap.dataset.bridgeAgentId || 'WEB-AUTOCOUNT';

  const elSerial   = document.getElementById('qcSerialInput');
  const elTransfer = document.getElementById('qcTransferBtnTop');
  const elClear    = document.getElementById('qcClearBtn');

  // bridge UI
  const elBridgePill = document.getElementById('qcBridgePill');
  const elBridgeText = document.getElementById('qcBridgeText');
  const elLastMsg    = document.getElementById('qcLastMsg');

  // skip confirm (per device)
  const elSkip = document.getElementById('qcSkipConfirm');
  const SKIP_KEY = 'ac_skip_confirm_transfer_' + TO_LOC;

  // scanner
  const elScanBtn   = document.getElementById('qcScanBtn');
  const elScanModal = document.getElementById('qcScanModal');
  const elScanClose = document.getElementById('qcScanClose');

  // === Settings ===
  const SUCCESS_AUTO_CLOSE_MS = 2000;   // make it longer & readable (set 0 to never auto-close)
  const DUPLICATE_GUARD_MS    = 900;    // prevent double scan / double enter
  const BRIDGE_WARN_ONCE_KEY  = 'ac_bridge_warn_once_' + TO_LOC; // per session

  // Init skip toggle
  if (elSkip){
    elSkip.checked = localStorage.getItem(SKIP_KEY) === '1';
    elSkip.addEventListener('change', () => {
      localStorage.setItem(SKIP_KEY, elSkip.checked ? '1' : '0');
    });
  }
  function shouldSkipConfirm(){
    return localStorage.getItem(SKIP_KEY) === '1';
  }

  // Focus for fast scanner usage
  setTimeout(() => { try{ elSerial.focus(); }catch(e){} }, 150);

  // Clear button => latest only behavior
  function clearSerial(){
    elSerial.value = '';
    try{ elSerial.focus(); }catch(e){}
  }
  if (elClear) elClear.addEventListener('click', clearSerial);

  // Enter triggers transfer
  elSerial.addEventListener('keydown', (e) => {
    if (e.key === 'Enter'){
      e.preventDefault();
      sendFromInput();
    }
  });
  elTransfer.addEventListener('click', sendFromInput);

  /* ========== Scanner ========== */
  let qr = null;
  let lastScanAt = 0;

  elScanBtn.addEventListener('click', openScanner);
  elScanClose.addEventListener('click', () => closeScanner(false));
  elScanModal.addEventListener('click', (e) => {
    if (e.target === elScanModal) closeScanner(false);
  });

  async function openScanner(){
    elScanModal.classList.add('is-open');
    elScanModal.setAttribute('aria-hidden', 'false');

    try{
      if (!qr) qr = new Html5Qrcode("qcReader");

      await qr.start(
        { facingMode: "environment" },
        { fps: 10, qrbox: { width: 240, height: 240 } },
        (decodedText) => {
          const now = Date.now();
          if (now - lastScanAt < DUPLICATE_GUARD_MS) return;
          lastScanAt = now;

          const val = (decodedText || '').trim();
          if (!val) return;

          // Latest only: replace previous serial immediately
          elSerial.value = val;

          closeScanner(true);

          // Auto-send immediately if skip confirm enabled
          if (shouldSkipConfirm()){
            sendTransfer(val, {source:'camera'});
          } else {
            // keep focus for next action
            try{ elSerial.focus(); }catch(e){}
          }
        },
        () => {}
      );
    }catch(err){
      closeScanner(true);
      Swal.fire({
        icon:'error',
        title:'Scanner error',
        text: (err && err.message) ? err.message : String(err || 'Unable to start camera')
      });
    }
  }

  async function closeScanner(silent){
    elScanModal.classList.remove('is-open');
    elScanModal.setAttribute('aria-hidden', 'true');

    try{ if (qr) await qr.stop(); }catch(e){}
    try{ if (qr) qr.clear(); }catch(e){}

    if (!silent){
      try{ elSerial.focus(); }catch(e){}
    }
  }

  /* ========== Bridge Status ========== */
  let bridgeState = 'unknown';
  let bridgeAgeSec = null;
  let bridgeLastError = '';

  function setBridgeUI(state, msg){
    bridgeState = (state || 'unknown').toLowerCase();

    if (!elBridgePill || !elBridgeText) return;

    elBridgePill.className = 'qc-pill';
    if (bridgeState === 'online') elBridgePill.classList.add('ok');
    else if (bridgeState === 'stale') elBridgePill.classList.add('warn');
    else if (bridgeState === 'offline') elBridgePill.classList.add('bad');
    else elBridgePill.classList.add('unk');

    elBridgePill.textContent = 'Bridge: ' + bridgeState.toUpperCase();
    elBridgeText.textContent = msg || '';
  }

  async function pollBridge(){
    if (!BRIDGE_URL){
      setBridgeUI('unknown', 'No bridge status endpoint configured.');
      return;
    }
    try{
      const res = await fetch(BRIDGE_URL, { cache:'no-store' });
      if (!res.ok) throw new Error('HTTP ' + res.status);
      const j = await res.json();

      const state = j && j.state ? String(j.state).toLowerCase() : 'unknown';
      const age = (j && typeof j.age_seconds !== 'undefined') ? Number(j.age_seconds) : null;
      const err = (j && j.last_error) ? String(j.last_error) : '';

      bridgeAgeSec = (age !== null && !isNaN(age)) ? age : null;
      bridgeLastError = err || '';

      let msg = '';
      if (bridgeAgeSec !== null) msg = 'Last seen: ' + bridgeAgeSec + 's ago';
      else msg = 'Status loaded.';

      if (bridgeLastError) msg += ' • Error: ' + bridgeLastError;

      setBridgeUI(state, msg);
    }catch(e){
      bridgeAgeSec = null;
      bridgeLastError = '';
      setBridgeUI('unknown', 'Unable to check bridge (endpoint missing / error).');
    }
  }

  pollBridge();
  setInterval(pollBridge, 30000);

  // Warn if bridge is stale/offline before sending
  async function ensureBridgeOkForTransfer(){
    // If unknown: allow but warn once (so staff knows checker not ready)
    const st = bridgeState;

    // Show warning ONCE per session for stale/offline/unknown when skip confirmation is on
    const warned = sessionStorage.getItem(BRIDGE_WARN_ONCE_KEY) === '1';

    if (st === 'online') return true;

    let title = 'Bridge status: ' + st.toUpperCase();
    let msg = '';

    if (st === 'offline'){
      msg = 'Bridge appears OFFLINE. Scans may not update AutoCount. Continue anyway?';
    } else if (st === 'stale'){
      msg = 'Bridge seems STALE (not updated recently). Continue anyway?';
    } else {
      msg = 'Cannot confirm bridge is running. Continue anyway?';
    }

    // If skip confirm enabled, still force a warning once per session.
    if (shouldSkipConfirm() && warned) return true;

    const res = await Swal.fire({
      icon: (st === 'offline') ? 'error' : (st === 'stale') ? 'warning' : 'info',
      title: title,
      text: msg,
      showCancelButton: true,
      confirmButtonText: 'Continue',
      cancelButtonText: 'Cancel',
      reverseButtons: true
    });

    if (res.isConfirmed){
      sessionStorage.setItem(BRIDGE_WARN_ONCE_KEY, '1');
      return true;
    }
    return false;
  }

  /* ========== Actions ========== */
  async function sendFromInput(){
    const srl = (elSerial.value || '').trim();
    if (!srl) {
      Swal.fire({icon:'warning', title:'Serial required'});
      return;
    }
    await sendTransfer(srl, {source:'input'});
  }

  // ---- anti-duplicate guard (per device) ----
  const SEND_GUARD_KEY = 'ac_last_send_' + TO_LOC;
  function canSendNow(serial){
    const now = Date.now();
    const last = JSON.parse(localStorage.getItem(SEND_GUARD_KEY) || 'null');
    if (last && last.serial === serial && (now - last.at) < DUPLICATE_GUY�Y�/^�	��������Y�6
�6�����<?php
/**
 * FULL REPLACEMENT — Receiving List
 * - Desktop: clean table
 * - Mobile: clean cards (label top, value full width)
 * - QR expands as a full-width row under each record
 * - Fix: serial never breaks vertically; uses ellipsis on mobile
 * - Fix: Show QR button text stays blue on hover
 * - CHANGE: QR code stores ONLY Serial Number (NOT sticker URL)
 */
if (!defined('ABSPATH')) exit;

$conn = get_mssql();
if (!$conn) {
  echo '<div style="color:red;">MSSQL connection failed.</div>';
  return;
}

/**
 * ===============================
 * Query
 * ===============================
 */
$sql = "
;WITH FirstSR AS (
  SELECT
    s.FromSerialNo,
    s.ItemCode,
    s.DocKey,
    s.DocDate,
    s.TransKey,
    ROW_NUMBER() OVER (
      PARTITION BY s.FromSerialNo, s.ItemCode
      ORDER BY s.TransKey ASC
    ) AS rn
  FROM dbo.SerialNoTrans s WITH (NOLOCK)
  WHERE s.DocType = 'SR'
    AND (s.Cancelled IS NULL OR s.Cancelled = 'F')
)
SELECT TOP 200
  d.SerialNumber,
  d.ItemCode,
  d.Location,
  r.RefDocNo,
  r.UDF_USER,
  fs.DocDate,
  fs.TransKey
FROM [AED_TEST_API_09012026].[dbo].[ItemSerialNoDtl] d WITH (NOLOCK)
INNER JOIN FirstSR fs
  ON fs.FromSerialNo = d.SerialNumber
 AND fs.ItemCode     = d.ItemCode
 AND fs.rn = 1
LEFT JOIN dbo.RCV r WITH (NOLOCK)
  ON r.DocKey = fs.DocKey
WHERE d.Qty = 1
ORDER BY fs.TransKey DESC
";

$stmt = sqlsrv_query($conn, $sql);
if ($stmt === false) {
  echo '<div style="color:red;">Query failed.</div>';
  return;
}

$nowTs = current_time('timestamp');
$stickerBase = 'https://website.ipohserver.com/StockAdjust/index.php/home/sample/receiving/receiving-sticker/';

function ac_to_ts($dt) {
  if ($dt instanceof DateTime) return $dt->getTimestamp();
  if (!empty($dt)) {
    $ts = strtotime((string)$dt);
    return $ts ?: null;
  }
  return null;
}
function ac_h($s){ return esc_html((string)$s); }
function ac_attr($s){ return esc_attr((string)$s); }
function ac_url($s){ return esc_url($s); }

?>
<script src="https://cdnjs.cloudflare.com/ajax/libs/qrcodejs/1.0.0/qrcode.min.js" referrerpolicy="no-referrer"></script>

<style>
/* ===== Layout ===== */
.ac-wrap{
  max-width:1100px;
  margin:0 auto;
  padding:16px;
  color:#0f172a;
  font-family: inherit;
}

.ac-topbar{
  display:flex;
  gap:12px;
  align-items:center;
  justify-content:space-between;
  margin-bottom:12px;
}
.ac-title{ font-size:16px; font-weight:900; }

.ac-search{ width:min(420px,100%); }
.ac-search input{
  width:100%;
  height:40px;
  border:1px solid #e5e7eb;
  border-radius:12px;
  padding:0 12px;
  outline:none;
  background:#fff;
}
.ac-search input:focus{
  border-color:rgba(2,132,199,.55);
  box-shadow:0 0 0 4px rgba(2,132,199,.14);
}

/* ===== Table (desktop) ===== */
.ac-table{
  width:100%;
  border-collapse:separate;
  border-spacing:0;
  background:#fff;
  border:1px solid #e5e7eb;
  border-radius:14px;
  overflow:hidden;
}
.ac-table thead th{
  text-align:left;
  font-size:12px;
  letter-spacing:.06em;
  text-transform:uppercase;
  color:#475569;
  background:#f8fafc;
  border-bottom:1px solid #e5e7eb;
  padding:12px 12px;
}
.ac-table tbody td{
  padding:12px 12px;
  border-bottom:1px solid #f1f5f9;
  vertical-align:top;
  font-size:14px;
}
.ac-table tbody tr:last-child td{ border-bottom:none; }

/* ===== Pills ===== */
.ac-code{
  font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono","Courier New", monospace;
  font-size:12px;
  font-weight:900;
  background:#f1f5f9;
  border:1px solid #e2e8f0;
  padding:4px 10px;
  border-radius:999px;
  display:inline-block;
  max-width: 100%;
}

.ac-pill{
  display:inline-block;
  font-size:12px;
  font-weight:900;
  padding:4px 10px;
  border-radius:999px;
  border:1px solid transparent;
  white-space:nowrap;
}
.ac-pill--qc{ background:#fff7ed; color:#9a3412; border-color:#fed7aa; }
.ac-pill--ok{ background:#ecfeff; color:#155e75; border-color:#a5f3fc; }
.ac-pill--muted{ background:#f1f5f9; color:#475569; border-color:#e2e8f0; }
.ac-pill--warn{ background:#fef9c3; color:#854d0e; border-color:#fde68a; }
.ac-pill--danger{ background:#fee2e2; color:#991b1b; border-color:#fecaca; }

/* ===== Show QR button (no white text on hover) ===== */
.ac-qr-toggle{
  border:1px solid #dbe3ee;
  background:#f8fbff;
  border-radius:12px;
  padding:8px 12px;
  cursor:pointer;
  font-weight:900;
  color:#0a66c2;
  transition: background .12s ease, border-color .12s ease, transform .08s ease;
}
.ac-qr-toggle:hover,
.ac-qr-toggle:focus{
  background:#eef6ff;
  border-color:#cfe3ff;
  color:#0a66c2;
  outline:none;
}
.ac-qr-toggle:active{
  transform: translateY(1px);
  background:#e6f1ff;
  color:#0a66c2;
}

/* ===== QR expanded row ===== */
.ac-qr-row td{ background:#fbfdff; }
.ac-qrbox{
  display:flex;
  gap:14px;
  align-items:flex-start;
  flex-wrap:wrap;
  padding:12px;
  border:1px dashed #e5e7eb;
  border-radius:12px;
  background:#fff;
}
.ac-qrhint{
  font-size:12px;
  color:#64748b;
  max-width:520px;
  word-break:break-all;
  margin-top:2px;
}
.ac-qrmeta{
  display:flex;
  flex-direction:column;
  gap:6px;
  min-width:240px;
}
.ac-qrmeta a{
  font-weight:900;
  color:#075985;
  text-decoration:none;
}
.ac-qrmeta a:hover{ text-decoration:underline; }

/* ===== Mobile: real cards (label on top, value below) ===== */
@media (max-width: 900px){
  .ac-topbar{
    flex-direction: column;
    align-items: stretch;
  }
  .ac-search{ width:100%; }

  .ac-table thead{ display:none; }
  .ac-table, .ac-table tbody{ display:block; width:100%; }

  /* Each record becomes a card */
  tr.ac-main-row{
    display:block;
    background:#fff;
    border:1px solid #e5e7eb;
    border-radius:14px;
    margin-bottom:12px;
    overflow:hidden;
  }

  /* Cells become stacked blocks */
  tr.ac-main-row td{
    display:block;
    width:100%;
    padding:10px 12px;
    border-bottom:1px solid #eef2f7;
  }
  tr.ac-main-row td:last-child{ border-bottom:none; }

  /* Label */
  tr.ac-main-row td[data-label]::before{
    content: attr(data-label);
    display:block;
    font-size:11px;
    letter-spacing:.06em;
    text-transform:uppercase;
    color:#64748b;
    font-weight:900;
    margin-bottom:6px;
  }

  /* Value */
  .ac-val{
    display:block;
    text-align:left;
  }

  /* Serial MUST NOT wrap vertically */
  .ac-code{
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    max-width: 100%;
  }

  /* QR row attaches to card (no separate giant block) */
  tr.ac-qr-row{
    display:block;
    margin-top:-10px; /* visually attach under card */
    margin-bottom:12px;
  }
  tr.ac-qr-row td{
    display:block;
    width:100%;
    padding: 0 0 0;
    border:none;
    background: transparent;
  }
  tr.ac-qr-row .ac-qrbox{
    border-radius:14px;
    border:1px dashed #e5e7eb;
  }
}
</style>

<div class="ac-wrap">
  <div class="ac-topbar">
    <div class="ac-title">Receiving Records</div>
    <div class="ac-search">
      <input id="acSearch" type="search" placeholder="Search serial / item / location / doc ref..." />
    </div>
  </div>

  <table class="ac-table" id="acTable">
    <thead>
      <tr>
        <th>Serial</th>
        <th>Location</th>
        <th>Item Code</th>
        <th>Registered</th>
        <th>Doc Ref</th>
        <th>Created By</th>
        <th>QR</th>
      </tr>
    </thead>

    <tbody>
      <?php while ($row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC)) :

        $serial  = (string)($row['SerialNumber'] ?? '');
        $item    = (string)($row['ItemCode'] ?? '');
        $loc     = (string)($row['Location'] ?? '');
        $ref     = (string)($row['RefDocNo'] ?? '');
        $user    = (string)($row['UDF_USER'] ?? '');

        $locUpper = strtoupper(trim($loc));
        $docTs = ac_to_ts($row['DocDate'] ?? null);

        $registeredText = '-';
        $ageClass = 'ac-pill--muted';
        if ($docTs) {
          $days = (int) floor(($nowTs - $docTs) / 86400);
          if ($days < 0) $days = 0;

          $registeredText = $days . ' day' . ($days === 1 ? '' : 's') . ' ago • ' . date_i18n('Y-m-d', $docTs);

          if ($days > 7) $ageClass = 'ac-pill--danger';
          elseif ($days === 7) $ageClass = 'ac-pill--warn';
        }

        $locClass = ($locUpper === 'QC') ? 'ac-pill--qc' : 'ac-pill--ok';

        // Sticker link still uses ?srl=serial
        $stickerUrl = add_query_arg(['srl' => $serial], $stickerBase);

        $searchText = strtolower(trim($serial.' '.$item.' '.$loc.' '.$ref.' '.$user.' '.$registeredText));

        $baseId  = md5($serial . '|' . $item . '|' . $ref);
        $qrRowId = 'qrrow-' . $baseId;
        $qrBoxId = 'qrbox-' . $baseId;
      ?>
        <tr class="ac-main-row" data-search="<?php echo ac_attr($searchText); ?>">
          <td data-label="Serial">
            <span class="ac-val">
              <span class="ac-code" title="<?php echo ac_attr($serial); ?>"><?php echo ac_h($serial ?: '-'); ?></span>
            </span>
          </td>

          <td data-label="Location">
            <span class="ac-val">
              <span class="ac-pill <?php echo ac_attr($locClass); ?>"><?php echo ac_h($loc ?: '-'); ?></span>
            </span>
          </td>

          <td data-label="Item Code">
            <span class="ac-val"><?php echo ac_h($item ?: '-'); ?></span>
          </td>

          <td data-label="Registered">
            <span class="ac-val">
              <span class="ac-pill <?php echo ac_attr($ageClass); ?>"><?php echo ac_h($registeredText); ?></span>
            </span>
          </td>

          <td data-label="Doc Ref">
            <span class="ac-val"><?php echo ac_h($ref ?: '-'); ?></span>
          </td>

          <td data-label="Created By">
            <span class="ac-val"><?php echo ac_h($user ?: '-'); ?></span>
          </td>

          <td data-label="QR">
            <span class="ac-val">
              <?php if ($serial !== ''): ?>
                <button type="button"
                  class="ac-qr-toggle"
                  data-qr-row="<?php echo ac_attr($qrRowId); ?>"
                  data-qr-box="<?php echo ac_attr($qrBoxId); ?>"
                  data-qr-text="<?php echo ac_attr($serial); ?>">
                  Show QR
                </button>
              <?php else: ?>
                <span class="ac-pill ac-pill--muted">-</span>
              <?php endif; ?>
            </span>
          </td>
        </tr>

        <tr class="ac-qr-row" id="<?php echo ac_attr($qrRowId); ?>" style="display:none;">
          <td colspan="7">
            <div class="ac-qrbox">
              <div id="<?php echo ac_attr($qrBoxId); ?>"></div>

              <div class="ac-qrmeta">
                <div style="font-weight:900;">
                  Serial: <span class="ac-code" title="<?php echo ac_attr($serial); ?>"><?php echo ac_h($serial ?: '-'); ?></span>
                </div>
                <a href="<?php echo ac_url($stickerUrl); ?>" target="_blank" rel="noopener">Open sticker link</a>

                <!-- CHANGE: show serial only -->
                <div class="ac-qrhint"><?php echo ac_h($serial); ?></div>
              </div>
            </div>
          </td>
        </tr>

      <?php endwhile; ?>
    </tbody>
  </table>
</div>

<script>
(function(){
  var inp = document.getElementById('acSearch');
  var tbl = document.getElementById('acTable');

  function closeAllQrRows(){
    document.querySelectorAll('tr.ac-qr-row').forEach(function(r){
      r.style.display = 'none';
    });
    document.querySelectorAll('.ac-qr-toggle').forEach(function(b){
      b.textContent = 'Show QR';
      b.setAttribute('aria-expanded', 'false');
    });
  }

  // Search filter
  if(inp && tbl){
    inp.addEventListener('input', function(){
      closeAllQrRows();
      var q = (inp.value || '').toLowerCase().trim();

      var mainRows = tbl.querySelectorAll('tbody tr.ac-main-row');
      mainRows.forEach(function(tr){
        var t = (tr.getAttribute('data-search') || '');
        var show = (q === '' || t.indexOf(q) !== -1);
        tr.style.display = show ? '' : 'none';

        var next = tr.nextElementSibling;
        if(next && next.classList.contains('ac-qr-row')){
          next.style.display = 'none';
        }
      });
    });
  }

  // QR toggle
  document.querySelectorAll('.ac-qr-toggle').forEach(function(btn){
    btn.setAttribute('aria-expanded', 'false');

    btn.addEventListener('click', function(){
      var rowId = btn.getAttribute('data-qr-row');
      var boxId = btn.getAttribute('data-qr-box');
      var text  = btn.getAttribute('data-qr-text') || ''; // serial only

      var qrRow = document.getElementById(rowId);
      var box   = document.getElementById(boxId);
      if(!qrRow || !box) return;

      var isOpen = (qrRow.style.display !== 'none');

      if(isOpen){
        qrRow.style.display = 'none';
        btn.textContent = 'Show QR';
        btn.setAttribute('aria-expanded', 'false');
        return;
      }

      // keep one open
      closeAllQrRows();

      qrRow.style.display = '';
      btn.textContent = 'Hide QR';
      btn.setAttribute('aria-expanded', 'true');

      if(box.getAttribute('data-made') === '1') return;

      try{
        new QRCode(box, { text: text, width: 140, height: 140 });
        box.setAttribute('data-made','1');
      }catch(e){
        box.innerHTML = '<div style="color:#991b1b;font-size:12px;">QR failed to render</div>';
      }
    });
  });
})();
</script>

<?php
sqlsrv_free_stmt($stmt);
?>Y�6�\L�
��������Y�Y�
�����ARD_MS){
      return false;
    }
    localStorage.setItem(SEND_GUARD_KEY, JSON.stringify({serial, at: now}));
    return true;
  }

  function setLastMsg(html){
    if (!elLastMsg) return;
    elLastMsg.style.display = 'block';
    elLastMsg.innerHTML = html;
  }

  async function sendTransfer(serial, meta){
    serial = (serial || '').trim();
    if (!serial) return;

    // Latest only: replace whatever was previously in input
    elSerial.value = serial;

    if (!canSendNow(serial)) return;

    // Bridge warning before send
    const okBridge = await ensureBridgeOkForTransfer();
    if (!okBridge) return;

    if (!shouldSkipConfirm()){
      const res = await Swal.fire({
        icon: 'question',
        title: `Transfer to ${MODE_LBL}?`,
        html: `<div style="text-align:left"><b>Serial:</b> ${escapeHtml(serial)}<br><b>To:</b> ${escapeHtml(TO_LOC)}</div>`,
        showCancelButton: true,
        confirmButtonText: 'Yes, Transfer',
        cancelButtonText: 'Cancel',
        reverseButtons: true
      });
      if (!res.isConfirmed) return;
    }

    const fd = new FormData();
    fd.append('action', 'ac_qc_send_to_qc'); // keep your existing action
    fd.append('nonce', NONCE);
    fd.append('srl', serial);
    fd.append('to_location', TO_LOC);

    // Optional context
    fd.append('source', (meta && meta.source) ? meta.source : 'unknown');

    // Show non-blocking loader (won't disappear unless request completes)
    Swal.fire({
      title: 'Sending…',
      html: 'Queuing transfer to bridge…',
      allowOutsideClick: false,
      allowEscapeKey: false,
      didOpen: () => Swal.showLoading()
    });

    try {
      const r = await fetch(AJAX_URL, { method:'POST', body: fd });

      let j = null;
      try{ j = await r.json(); }catch(e){}

      if (!r.ok){
        const msg = j && j.data && j.data.message ? j.data.message : ('HTTP ' + r.status);
        throw new Error(msg);
      }

      if (!j || !j.success){
        const msg = (j && j.data && j.data.message) ? j.data.message : 'Queue failed';
        throw new Error(msg);
      }

      // Success popup: readable + then clear input for next scan
      if (SUCCESS_AUTO_CLOSE_MS > 0){
        await Swal.fire({
          icon:'success',
          title:'Queued',
          html:`<div style="text-align:left">✅ Queued<br><b>Serial:</b> ${escapeHtml(serial)}<br><b>To:</b> ${escapeHtml(TO_LOC)}</div>`,
          timer: SUCCESS_AUTO_CLOSE_MS,
          timerProgressBar: true,
          showConfirmButton: false
        });
      } else {
        await Swal.fire({
          icon:'success',
          title:'Queued',
          html:`<div style="text-align:left">✅ Queued<br><b>Serial:</b> ${escapeHtml(serial)}<br><b>To:</b> ${escapeHtml(TO_LOC)}</div>`,
          showConfirmButton: true,
          confirmButtonText: 'OK'
        });
      }

      // Latest only: clear previous and ready for next scan
      clearSerial();

      setLastMsg(`<b>Last:</b> Queued <span class="qc-pill ok" style="padding:2px 8px;border-radius:999px;border:1px solid var(--okbd);background:var(--okbg);color:var(--okfg);">${escapeHtml(serial)}</span> → <b>${escapeHtml(TO_LOC)}</b>`);

    } catch(e) {
      Swal.fire({icon:'error', title:'Failed', text: e.message || 'Unknown error'});
      setLastMsg(`<b>Last error:</b> <span style="color:#991b1b;font-weight:900;">${escapeHtml(e.message || 'Unknown error')}</span>`);
    } finally {
      try{ elSerial.focus(); }catch(e){}
    }
  }

  function escapeHtml(str){
    return String(str).replace(/[&<>"']/g, m => ({
      '&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#039;'
    }[m]));
  }
})();
</script>Y�Y��J}���������W��a
����: 'Cancel',
      reverseButtons: true
    });

    if (!res.isConfirmed) return;

    const fd = new FormData();
    fd.append('action', 'ac_qc_send_to_qc');
    fd.append('nonce', NONCE);
    fd.append('srl', serial);

    try {
      const r = await fetch(AJAX_URL, { method:'POST', body: fd });
      const j = await r.json();

      if (!j || !j.success) throw new Error((j && j.data && j.data.message) ? j.data.message : 'Queue failed');

      await Swal.fire({
        icon:'success',
        title:'Queued',
        html:`<div style="text-align:left">✅ Queued to QC<br><b>Serial:</b> ${escapeHtml(serial)}</div>`
      });

      if (!elListCard.classList.contains('qc-collapsed')) {
        await loadList();
        listLoadedOnce = true;
      }

    } catch(e) {
      Swal.fire({icon:'error', title:'Failed', text: e.message || 'Unknown error'});
    }
  }

  function escapeHtml(str){
    return String(str).replace(/[&<>"']/g, m => ({
      '&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#039;'
    }[m]));
  }
  function escapeHtmlAttr(str){
    return escapeHtml(str).replace(/`/g, '&#096;');
  }
  function cssEscape(str){
    return String(str).replace(/"/g, '\\"');
  }
})();
</script>W��aeAAM

Youez - 2016 - github.com/yon3zu
LinuXploit