<?php
/** @var array       $orders */
/** @var string|null $tanggal */
/** @var string      $title */

$labelTanggal = $tanggal
    ? strtoupper(date('d F Y', strtotime($tanggal)))
    : 'MONITOR PRODUKSI PEKERJAAN';
$jobStatuses = job_statuses(true);
?>
<!doctype html>
<html lang="id">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title><?= htmlentities($title, ENT_QUOTES, 'UTF-8') ?></title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700;800&display=swap" rel="stylesheet">
<style>
  :root{
    --bg-top: #0a1638;
    --bg-bottom: #09142f;
    --text: #ff7f97;
    --white: #fff;
    --r: 12px;
    --card-min: 138px;
    --card-gap: 10px;
    --card-border: rgba(255,255,255,.16);
    --panel-bg: rgba(30, 45, 79, .4);
  }
  *{ box-sizing:border-box; }

  body{
    margin:0;
    min-height:100vh;
    background:
      radial-gradient(circle at 20% -10%, #15306e 0%, transparent 38%),
      linear-gradient(180deg, var(--bg-top) 0%, var(--bg-bottom) 100%);
    font-family:"Inter",system-ui,-apple-system,Segoe UI,Arial,sans-serif;
  }

  .wrap{
    width:100%;
    max-width:1920px;
    padding:24px 14px 32px;
    margin:0 auto;
  }

  h1{
    color:var(--text);
    text-align:center;
    font-weight:800;
    letter-spacing:.35px;
    margin:6px 0 10px;
    line-height:1.12;
    font-size:clamp(28px,3.4vw,64px);
  }
  .subnote{
    color:#cbd5f5;
    text-align:center;
    font-weight:600;
    font-size:clamp(13px,1.05vw,20px);
    max-width:980px;
    margin:0 auto 16px;
  }
  .datetime-info{
    color:#dbe4ff;
    text-align:center;
    font-weight:700;
    font-size:clamp(12px,1vw,18px);
    margin:8px auto 12px;
    letter-spacing:.2px;
  }

  /* GRID FLEXIBLE FULL WIDTH */
  .grid{
    width:100%;
    display:grid;
    grid-template-columns:repeat(auto-fit, minmax(var(--card-min), 1fr));
    gap:var(--card-gap);
    align-items:stretch;
  }

  .card{
    container-type:inline-size;
    border-radius:var(--r);
    color:var(--white);
    text-align:center;
    padding:3px 4px 4px;
    min-height:46px;
    font-weight:800;
    box-shadow:0 4px 10px rgba(0,0,0,.18);
    border:1px solid var(--card-border);
    display:flex;
    align-items:center;
    justify-content:center;
    flex-direction:column;
    gap:0;
    cursor:pointer;
    overflow:hidden;
    transition:transform .15s ease, box-shadow .15s ease, border-color .15s ease;
  }
  @supports (font-size: 1cqi){
    .card{
      padding:clamp(2px, 1.8cqi, 5px) clamp(3px, 2.2cqi, 6px) clamp(3px, 2.4cqi, 6px);
      min-height:clamp(44px, 29cqi, 58px);
      gap:clamp(0px, .3cqi, 1px);
    }
  }
  .card:hover{
    transform:translateY(-1px);
    box-shadow:0 8px 16px rgba(0,0,0,.22);
    border-color:rgba(255,255,255,.28);
  }
  .card-code{
    font-size:clamp(12px, 1.15vw, 24px);
    line-height:1.05;
    font-weight:900;
    letter-spacing:.08px;
    max-width:100%;
    white-space:nowrap;
    text-shadow:0 1px 0 rgba(0,0,0,.18);
    font-variant-numeric: tabular-nums;
  }
  @supports (font-size: 1cqi){
    .card-code{
      font-size:clamp(12px, 15cqi, 30px);
    }
  }
  .card-customer{
    font-size:clamp(10px, .8vw, 14px);
    font-size:clamp(10px, 8.2cqi, 16px);
    line-height:1.2;
    font-weight:600;
    opacity:.96;
    max-width:100%;
    padding:0 1px;
    overflow:hidden;
    text-overflow:ellipsis;
    white-space:nowrap;
  }

  /* WARNA STATUS (ikut legend) */
  .baru     { background:#667085; }
  .desain   { background:linear-gradient(145deg, #b100ff 0%, #da27ff 100%); }
  .cetak    { background:linear-gradient(145deg, #2869f2 0%, #2d8dff 100%); }
  .selesai  { background:linear-gradient(145deg, #14a84c 0%, #1ccf60 100%); }
  .diambil  { background:linear-gradient(145deg, #facc15 0%, #fde047 100%); color:#111827; }
  .dikirim  { background:linear-gradient(145deg, #ec9b13 0%, #ffb22c 100%); color:#111827; }

  .legend{
    margin-top:12px;
    margin-left:auto;
    margin-right:auto;
    padding:10px 12px;
    border:1px solid rgba(255,255,255,.12);
    border-radius:10px;
    background:var(--panel-bg);
    display:flex;
    width:fit-content;
    max-width:100%;
    flex-wrap:wrap;
    gap:14px;
    justify-content:center;
    align-items:center;
    color:#e5e7eb;
    font-size:15px;
    font-weight:700;
  }
  .sb-modal{
    position: fixed;
    inset: 0;
    background: rgba(0,0,0,.55);
    display:none;
    align-items:center;
    justify-content:center;
    z-index: 9999;
    padding: 16px;
  }
  .sb-modal.is-open{ display:flex; }
  .sb-modal-content{
    background:#fff;
    border-radius:10px;
    width:min(1000px, 96vw);
    max-height: 92vh;
    overflow:auto;
    box-shadow:0 8px 24px rgba(0,0,0,.25);
  }
  .sb-modal-header{
    display:flex;
    align-items:center;
    justify-content:space-between;
    padding:10px 12px;
    border-bottom:1px solid #e5e7eb;
    font-weight:700;
    color:#111827;
  }
  .sb-modal-close{
    border:1px solid #e5e7eb;
    background:#fff;
    border-radius:8px;
    padding:6px 10px;
    cursor:pointer;
  }
  .sb-modal-body{
    padding:12px;
  }
  .sb-modal-catatan{
    z-index:10020;
  }
  .sb-modal-content-catatan{
    width:min(560px, 96vw);
    max-height:88vh;
    display:flex;
    flex-direction:column;
    overflow:hidden;
    border-radius:6px;
  }
  .sb-modal-body-catatan{
    padding:0;
    width:100%;
    height:min(78vh, 560px);
  }
  #sb-catatan-frame{
    width:100%;
    height:100%;
    border:0;
    background:#fff;
  }
  .sb-gallery-modal{
    z-index:10030;
  }
  .sb-gallery-content{
    width:min(920px, 96vw);
    max-height:90vh;
    border-radius:10px;
    background:#0f172a;
    color:#e5e7eb;
    box-shadow:0 12px 36px rgba(0,0,0,.45);
    overflow:hidden;
  }
  .sb-gallery-header{
    display:flex;
    align-items:center;
    justify-content:space-between;
    gap:10px;
    padding:10px 12px;
    border-bottom:1px solid rgba(148,163,184,.25);
    background:rgba(15,23,42,.92);
  }
  .sb-gallery-title{
    font-weight:700;
    font-size:14px;
    color:#e2e8f0;
    white-space:nowrap;
    overflow:hidden;
    text-overflow:ellipsis;
  }
  .sb-gallery-close{
    border:1px solid rgba(148,163,184,.45);
    background:#111827;
    color:#fff;
    border-radius:8px;
    padding:6px 10px;
    cursor:pointer;
    font-weight:700;
  }
  .sb-gallery-stage{
    position:relative;
    width:100%;
    background:#020617;
  }
  .sb-gallery-viewport{
    overflow:hidden;
    width:100%;
    touch-action:pan-y;
    cursor:grab;
  }
  .sb-gallery-viewport.is-dragging{
    cursor:grabbing;
  }
  .sb-gallery-track{
    display:flex;
    transition:transform .28s ease;
    will-change:transform;
  }
  .sb-gallery-slide{
    min-width:100%;
    height:min(72vh, 620px);
    display:flex;
    align-items:center;
    justify-content:center;
    padding:12px;
  }
  .sb-gallery-slide img{
    max-width:100%;
    max-height:100%;
    object-fit:contain;
    border-radius:8px;
    background:#fff;
  }
  .sb-gallery-nav{
    position:absolute;
    top:50%;
    transform:translateY(-50%);
    border:1px solid rgba(148,163,184,.5);
    background:rgba(15,23,42,.7);
    color:#f8fafc;
    width:36px;
    height:36px;
    border-radius:999px;
    cursor:pointer;
    font-size:18px;
    line-height:1;
    z-index:2;
    display:flex;
    align-items:center;
    justify-content:center;
  }
  .sb-gallery-nav[disabled]{
    opacity:.4;
    cursor:not-allowed;
  }
  .sb-gallery-nav-prev{ left:10px; }
  .sb-gallery-nav-next{ right:10px; }
  .sb-gallery-note{
    border-top:1px solid rgba(148,163,184,.2);
    padding:8px 12px;
    color:#e2e8f0;
    font-size:13px;
    line-height:1.35;
    min-height:40px;
    word-break:break-word;
    background:rgba(15,23,42,.88);
  }
  .sb-gallery-footer{
    padding:8px 12px 10px;
    display:flex;
    justify-content:flex-end;
    color:#cbd5e1;
    font-weight:700;
    font-size:13px;
  }
  .sb-loading{
    color:#6b7280;
    font-weight:600;
  }

  .lg{
    display:inline-flex;
    align-items:center;
    gap:6px;
  }

  .dot{
    width:16px;
    height:16px;
    border-radius:6px;
    display:inline-block;
  }

  @media (max-width:1440px){
    :root{
      --card-min: 124px;
      --card-gap: 9px;
    }
    .wrap{ padding:20px 10px 26px; }
    h1{ font-size:clamp(26px,3.1vw,52px); }
    .subnote{ margin:0 auto 14px; }
  }

  @media (max-width:1024px){
    :root{
      --card-min: 116px;
      --card-gap: 8px;
    }
    .wrap{ padding:16px 8px 22px; }
    h1{ font-size:clamp(24px,4.6vw,42px); }
    .legend{
      font-size:13px;
      gap:10px;
    }
  }

  @media (max-width:768px){
    :root{
      --card-min: 106px;
      --card-gap: 7px;
    }
    .wrap{ padding:14px 8px 20px; }
    h1{
      margin:4px 0 10px;
      letter-spacing:.2px;
      font-size:clamp(22px,7.2vw,34px);
    }
    .subnote{
      margin:0 auto 12px;
      font-size:12px;
    }
    .grid{ grid-auto-rows:auto; }
    .card{ border-radius:10px; }
    .legend{
      margin-top:10px;
      padding:8px 8px;
      font-size:12px;
      gap:8px;
    }
    .dot{
      width:14px;
      height:14px;
      border-radius:5px;
    }
    .sb-gallery-content{
      width:98vw;
      border-radius:8px;
    }
    .sb-gallery-header{
      padding:8px 10px;
    }
    .sb-gallery-title{
      font-size:13px;
    }
    .sb-gallery-close{
      padding:5px 9px;
      font-size:12px;
    }
    .sb-gallery-slide{
      height:min(60vh, 520px);
      padding:8px;
    }
    .sb-gallery-nav{
      width:32px;
      height:32px;
      font-size:16px;
    }
    .sb-gallery-nav-prev{ left:6px; }
    .sb-gallery-nav-next{ right:6px; }
    .sb-gallery-note{
      padding:7px 10px;
      font-size:12px;
      min-height:36px;
    }
    .sb-gallery-footer{
      padding:6px 10px 8px;
      font-size:12px;
    }
  }

  @media (max-width:480px){
    :root{ --card-min: 98px; }
    .wrap{ padding:12px 6px 18px; }
    .grid{ grid-auto-rows:auto; }
    .legend{
      justify-content:flex-start;
      font-size:11px;
    }
    .sb-gallery-slide{
      height:min(54vh, 460px);
      padding:6px;
    }
  }

  @media (max-width:360px){
    :root{ --card-min: 88px; }
  }
</style>
</head>
<body>
<div class="wrap">
  <h1> <?= htmlentities($labelTanggal, ENT_QUOTES, 'UTF-8') ?></h1>

  <div class="legend">
    <span>KETERANGAN STATUS :</span>
    <?php foreach ($jobStatuses as $jobStatus): ?><span class="lg"><span class="dot" style="background:<?=htmlentities($jobStatus['color'], ENT_QUOTES, 'UTF-8');?>"></span> <?=htmlentities($jobStatus['label'], ENT_QUOTES, 'UTF-8');?></span><?php endforeach; ?>
  </div>
  <div id="datetime-info" class="datetime-info" aria-live="polite"></div>


  <div class="subnote"></div>

  <div id="grid" class="grid">
    <?php foreach ($orders as $o):
      $statusMeta = job_status_meta((int)$o['status_angka']);
    ?>
      <div class="card" style="background:<?=htmlentities($statusMeta['color'], ENT_QUOTES, 'UTF-8');?>" data-status="<?=(int)$o['status_angka'];?>" data-trx="<?= htmlentities($o['id_transaksi'], ENT_QUOTES, 'UTF-8') ?>">
        <div class="card-code"><?= htmlentities($o['id_transaksi'], ENT_QUOTES, 'UTF-8') ?></div>
        <div class="card-customer"><?= htmlentities(isset($o['nama_pelanggan']) ? $o['nama_pelanggan'] : 'Umum', ENT_QUOTES, 'UTF-8') ?></div>
      </div>
    <?php endforeach; ?>
  </div>

</div>

<div id="sb-modal" class="sb-modal" aria-hidden="true">
  <div class="sb-modal-content" role="dialog" aria-modal="true" aria-label="Preview Transaksi">
    <div class="sb-modal-header">
      <span>Preview Transaksi</span>
      <button type="button" id="sb-modal-close" class="sb-modal-close">Tutup</button>
    </div>
    <div class="sb-modal-body" id="sb-modal-body">
      <div class="sb-loading">Memuat...</div>
    </div>
  </div>
</div>
<div id="sb-catatan-modal" class="sb-modal sb-modal-catatan" aria-hidden="true">
  <div class="sb-modal-content sb-modal-content-catatan" role="dialog" aria-modal="true" aria-label="Isi Catatan">
    <div class="sb-modal-body sb-modal-body-catatan">
      <iframe id="sb-catatan-frame" src="about:blank" loading="lazy" referrerpolicy="no-referrer"></iframe>
    </div>
  </div>
</div>
<div id="sb-gallery-modal" class="sb-modal sb-gallery-modal" aria-hidden="true">
  <div class="sb-gallery-content" role="dialog" aria-modal="true" aria-label="Galeri Gambar Catatan">
    <div class="sb-gallery-header">
      <div id="sb-gallery-title" class="sb-gallery-title">Galeri gambar</div>
      <button type="button" id="sb-gallery-close" class="sb-gallery-close">Tutup</button>
    </div>
    <div class="sb-gallery-stage">
      <button type="button" id="sb-gallery-prev" class="sb-gallery-nav sb-gallery-nav-prev" aria-label="Sebelumnya">&#10094;</button>
      <div id="sb-gallery-viewport" class="sb-gallery-viewport">
        <div id="sb-gallery-track" class="sb-gallery-track"></div>
      </div>
      <button type="button" id="sb-gallery-next" class="sb-gallery-nav sb-gallery-nav-next" aria-label="Berikutnya">&#10095;</button>
    </div>
    <div id="sb-gallery-note" class="sb-gallery-note">-</div>
    <div class="sb-gallery-footer">
      <span id="sb-gallery-counter">0 / 0</span>
    </div>
  </div>
</div>

<script>
  const dateStr = '<?= $tanggal ? $tanggal : '' ?>';
  const cardUrl = '<?= site_url('status-board/card') ?>';
  const getLaporanUrl = '<?= site_url('laporan/get_laporan') ?>';
  const simpanLaporanUrl = '<?= site_url('laporan/simpan_laporan') ?>';
  const uploadBuktiUrl = '<?= site_url('laporan/upload_bukti_progress') ?>';
  const applicationTimeZone = <?= json_encode(date_default_timezone_get()) ?>;
  const jobStatuses = <?=json_encode($jobStatuses, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);?>;
  const allJobStatuses = <?=json_encode(job_statuses(false), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);?>;
  const applicationTimeZoneLabel = <?= json_encode([
    'Asia/Jakarta' => 'WIB',
    'Asia/Makassar' => 'WITA',
    'Asia/Jayapura' => 'WIT',
    'UTC' => 'UTC'
  ][date_default_timezone_get()] ?? 'WIB') ?>;
  const tanggalFmt = new Intl.DateTimeFormat('id-ID', {
    weekday: 'long',
    day: '2-digit',
    month: 'long',
    year: 'numeric',
    timeZone: applicationTimeZone
  });
  const jamFmt = new Intl.DateTimeFormat('id-ID', {
    hour: '2-digit',
    minute: '2-digit',
    second: '2-digit',
    hour12: false,
    timeZone: applicationTimeZone
  });
  let activeTrx = '';
  let gridRefreshInFlight = false;
  let lastGridSignature = '';
  let cardRequestSeq = 0;
  const cardCache = new Map();
  const cardCacheTtl = 15000;
  const sbCameraState = {
    target: null,
    stream: null,
    uploading: false
  };
  const sbGalleryState = {
    items: [],
    index: 0,
    touchStartX: 0,
    touchStartY: 0,
    pointerActive: false
  };

  function refreshDateTimeInfo(){
    const el = document.getElementById('datetime-info');
    if (!el) return;
    const now = new Date();
    el.textContent = `${tanggalFmt.format(now)} | ${jamFmt.format(now)} ${applicationTimeZoneLabel}`;
  }

  function jsonURL(){
    let url = '<?= site_url('status-board/json') ?>';
    const params = [];
    if (dateStr) params.push('date=' + encodeURIComponent(dateStr));
    params.push('t=' + Date.now()); // cache-buster
    if (params.length) url += '?' + params.join('&');
    return url;
  }

  function configuredStatus(n){
    return allJobStatuses.find(item => +item.code === +n) || {code:+n,label:'Status '+n,color:'#64748b',icon:'circle'};
  }

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

  function gridSignatureFromData(data){
    if (!Array.isArray(data)) return '';
    return data.map(o => {
      const id = o && o.id_transaksi ? String(o.id_transaksi) : '';
      const status = o && o.status_angka !== undefined ? String(o.status_angka) : '0';
      const nama = o && o.nama_pelanggan ? String(o.nama_pelanggan) : 'Umum';
      return `${id}|${status}|${nama}`;
    }).join('~');
  }

  function gridSignatureFromDom(){
    const cards = document.querySelectorAll('#grid .card[data-trx]');
    return Array.from(cards).map(card => {
      const id = card.getAttribute('data-trx') || '';
      const status = classToStatus(card);
      const namaEl = card.querySelector('.card-customer');
      const nama = namaEl ? namaEl.textContent : 'Umum';
      return `${id}|${status}|${nama}`;
    }).join('~');
  }

  function classToStatus(card){
    return card ? parseInt(card.getAttribute('data-status') || '0', 10) : 0;
  }

  function renderGrid(data){
    const grid = document.getElementById('grid');
    if (!grid) return;
    const html = data.map(o => {
      const meta = configuredStatus(o.status_angka);
      const nama = o.nama_pelanggan ? o.nama_pelanggan : 'Umum';
      return `<div class="card" style="background:${escapeHtml(meta.color)}" data-status="${parseInt(o.status_angka || 0,10)}" data-trx="${escapeHtml(o.id_transaksi)}">
        <div class="card-code">${escapeHtml(o.id_transaksi)}</div>
        <div class="card-customer">${escapeHtml(nama)}</div>
      </div>`;
    }).join('');
    grid.innerHTML = html;
  }

  async function refreshGrid(){
    if (gridRefreshInFlight) return;
    gridRefreshInFlight = true;
    try{
      const r = await fetch(jsonURL(), {cache:'no-store'});
      if(!r.ok) return;
      const data = await r.json();
      const nextSignature = gridSignatureFromData(data);
      if (nextSignature !== lastGridSignature) {
        renderGrid(data);
        lastGridSignature = nextSignature;
      }
    }catch(e){
    }finally{
      gridRefreshInFlight = false;
    }
  }

  function openModal(){
    document.getElementById('sb-modal').classList.add('is-open');
  }
  function stopBuktiCameraInCardStream(){
    if (sbCameraState.stream && sbCameraState.stream.getTracks) {
      sbCameraState.stream.getTracks().forEach((track) => {
        try { track.stop(); } catch (_) {}
      });
    }
    sbCameraState.stream = null;
    const video = document.querySelector('#sb-modal-body #bukti-camera-video');
    if (video) video.srcObject = null;
  }

  function closeBuktiCameraInCard(){
    const modal = document.querySelector('#sb-modal-body #bukti-camera-modal');
    if (modal) modal.classList.remove('is-open');
    stopBuktiCameraInCardStream();
  }
  function openCatatanInCard(url){
    if (!url) return;
    const modal = document.getElementById('sb-catatan-modal');
    const frame = document.getElementById('sb-catatan-frame');
    if (frame && frame.getAttribute('src') !== url) {
      frame.setAttribute('src', url);
    }
    if (modal) modal.classList.add('is-open');
  }
  function closeCatatanInCard(){
    const modal = document.getElementById('sb-catatan-modal');
    const frame = document.getElementById('sb-catatan-frame');
    if (modal) modal.classList.remove('is-open');
    if (frame) frame.setAttribute('src', 'about:blank');
  }
  function normalizeGalleryItems(items){
    if (!Array.isArray(items)) return [];
    const normalized = [];
    items.forEach((item) => {
      if (typeof item === 'string') {
        const url = item.trim();
        if (url !== '') normalized.push({ url, text: '-' });
        return;
      }
      if (!item || typeof item !== 'object') return;
      const url = typeof item.url === 'string' ? item.url.trim() : '';
      if (url === '') return;
      const text = typeof item.text === 'string' && item.text.trim() !== '' ? item.text.trim() : '-';
      normalized.push({ url, text });
    });
    return normalized;
  }
  function updateGalleryInCard(){
    const track = document.getElementById('sb-gallery-track');
    const counter = document.getElementById('sb-gallery-counter');
    const prev = document.getElementById('sb-gallery-prev');
    const next = document.getElementById('sb-gallery-next');
    const note = document.getElementById('sb-gallery-note');
    if (track) track.style.transform = `translateX(-${sbGalleryState.index * 100}%)`;
    if (counter) counter.textContent = `${sbGalleryState.index + 1} / ${sbGalleryState.items.length}`;
    const disabled = sbGalleryState.items.length <= 1;
    if (prev) prev.disabled = disabled;
    if (next) next.disabled = disabled;
    const current = sbGalleryState.items[sbGalleryState.index] || null;
    if (note) note.textContent = current && current.text ? current.text : '-';
  }
  function renderGalleryInCard(){
    const track = document.getElementById('sb-gallery-track');
    if (!track) return;
    track.innerHTML = sbGalleryState.items.map((item, idx) => `
      <div class="sb-gallery-slide">
        <img src="${escapeHtml(item.url)}" alt="Gambar catatan ${idx + 1}" loading="lazy" draggable="false">
      </div>
    `).join('');
    updateGalleryInCard();
  }
  function openGalleryInCard(items, title){
    const cleaned = normalizeGalleryItems(items);
    if (!cleaned.length) {
      alert('Gambar catatan belum tersedia.');
      return;
    }
    sbGalleryState.items = cleaned;
    sbGalleryState.index = 0;
    renderGalleryInCard();
    const titleEl = document.getElementById('sb-gallery-title');
    if (titleEl) titleEl.textContent = title ? `Galeri - ${title}` : 'Galeri gambar';
    const modal = document.getElementById('sb-gallery-modal');
    if (modal) modal.classList.add('is-open');
  }
  function closeGalleryInCard(){
    const modal = document.getElementById('sb-gallery-modal');
    if (modal) modal.classList.remove('is-open');
    const track = document.getElementById('sb-gallery-track');
    if (track) track.innerHTML = '';
    const titleEl = document.getElementById('sb-gallery-title');
    if (titleEl) titleEl.textContent = 'Galeri gambar';
    const counter = document.getElementById('sb-gallery-counter');
    if (counter) counter.textContent = '0 / 0';
    const note = document.getElementById('sb-gallery-note');
    if (note) note.textContent = '-';
    sbGalleryState.items = [];
    sbGalleryState.index = 0;
  }
  function nextGalleryInCard(){
    if (sbGalleryState.items.length <= 1) return;
    sbGalleryState.index = (sbGalleryState.index + 1) % sbGalleryState.items.length;
    updateGalleryInCard();
  }
  function prevGalleryInCard(){
    if (sbGalleryState.items.length <= 1) return;
    sbGalleryState.index = (sbGalleryState.index - 1 + sbGalleryState.items.length) % sbGalleryState.items.length;
    updateGalleryInCard();
  }
  function initGallerySwipe(){
    const viewport = document.getElementById('sb-gallery-viewport');
    if (!viewport) return;
    viewport.addEventListener('touchstart', (e) => {
      const touch = e.changedTouches && e.changedTouches[0];
      if (!touch) return;
      sbGalleryState.touchStartX = touch.clientX;
      sbGalleryState.touchStartY = touch.clientY;
    }, { passive: true });
    viewport.addEventListener('touchend', (e) => {
      const touch = e.changedTouches && e.changedTouches[0];
      if (!touch) return;
      const dx = touch.clientX - sbGalleryState.touchStartX;
      const dy = touch.clientY - sbGalleryState.touchStartY;
      if (Math.abs(dx) < 40 || Math.abs(dx) <= Math.abs(dy)) return;
      if (dx < 0) nextGalleryInCard();
      else prevGalleryInCard();
    }, { passive: true });
    viewport.addEventListener('pointerdown', (e) => {
      if (e.pointerType !== 'mouse') return;
      if (e.button !== 0) return;
      sbGalleryState.pointerActive = true;
      sbGalleryState.touchStartX = e.clientX;
      sbGalleryState.touchStartY = e.clientY;
      viewport.classList.add('is-dragging');
    });
    viewport.addEventListener('pointerup', (e) => {
      if (!sbGalleryState.pointerActive) return;
      sbGalleryState.pointerActive = false;
      viewport.classList.remove('is-dragging');
      const dx = e.clientX - sbGalleryState.touchStartX;
      const dy = e.clientY - sbGalleryState.touchStartY;
      if (Math.abs(dx) < 50 || Math.abs(dx) <= Math.abs(dy)) return;
      if (dx < 0) nextGalleryInCard();
      else prevGalleryInCard();
    });
    viewport.addEventListener('pointercancel', () => {
      sbGalleryState.pointerActive = false;
      viewport.classList.remove('is-dragging');
    });
    viewport.addEventListener('pointerleave', () => {
      if (!sbGalleryState.pointerActive) return;
      sbGalleryState.pointerActive = false;
      viewport.classList.remove('is-dragging');
    });
  }
  window.closeStatusBoardCatatanPopup = closeCatatanInCard;

  function closeModal(){
    closeCatatanInCard();
    closeGalleryInCard();
    closeBuktiInCard();
    closeBuktiCameraInCard();
    sbCameraState.target = null;
    document.getElementById('sb-modal').classList.remove('is-open');
  }
  function getCardBody(){
    return document.getElementById('sb-modal-body');
  }

  function isMobileCameraDevice(){
    const ua = (navigator.userAgent || navigator.vendor || window.opera || '').toLowerCase();
    if (/android|iphone|ipad|ipod|iemobile|opera mini|mobile/i.test(ua)) return true;
    if (window.matchMedia) {
      try { return window.matchMedia('(pointer:coarse)').matches; } catch (_) {}
    }
    return false;
  }

  async function loadCard(trx){
    const body = getCardBody();
    if (!body) return;
    closeBuktiCameraInCard();
    sbCameraState.target = null;
    activeTrx = trx || '';
    openModal();

    const cached = cardCache.get(activeTrx);
    if (cached && (Date.now() - cached.time) < cardCacheTtl) {
      body.innerHTML = cached.html;
      return;
    }

    const requestId = ++cardRequestSeq;
    body.innerHTML = '<div class="sb-loading">Memuat...</div>';
    try{
      const r = await fetch(`${cardUrl}?trx=${encodeURIComponent(activeTrx)}`, {cache:'no-store'});
      if (requestId !== cardRequestSeq) return;
      if(!r.ok){
        body.innerHTML = '<div class="sb-loading">Data tidak ditemukan.</div>';
        return;
      }
      const html = await r.text();
      body.innerHTML = html;
      cardCache.set(activeTrx, {html, time: Date.now()});
    }catch(err){
      if (requestId !== cardRequestSeq) return;
      body.innerHTML = '<div class="sb-loading">Gagal memuat.</div>';
    }
  }

  async function postAjax(url, payload, useFormData = false){
    const init = {
      method: 'POST',
      cache: 'no-store',
      headers: {'X-Requested-With': 'XMLHttpRequest'}
    };
    if (useFormData) {
      init.body = payload;
    } else {
      init.headers['Content-Type'] = 'application/x-www-form-urlencoded; charset=UTF-8';
      init.body = new URLSearchParams(payload).toString();
    }
    const res = await fetch(url, init);
    const txt = await res.text();
    let json = null;
    try { json = JSON.parse(txt); } catch(_) {}
    return { ok: res.ok, status: res.status, json, text: txt };
  }

  function nextStatusOptions(currentStatus, cekBahan){
    const st = parseInt(currentStatus || 0, 10);
    if (st === 4 || st === 5) return [];
	const numberedStatuses = jobStatuses.filter(item => parseInt(item.code, 10) !== 0)
	  .map((item, index) => Object.assign({}, item, {number:index + 1}));
    return numberedStatuses.filter(item => {
      const code = parseInt(item.code, 10);
	  if (code === st) return false;
      return st === 3 ? (code === 4 || code === 5) : (code !== 4 && code !== 5);
	}).map(item => ({v:item.code,n:item.number,t:item.label}));
  }

  function statusMeta(n){
    return configuredStatus(n);
  }

  function applyStatusToButton(btn, status){
    const meta = statusMeta(status);
    btn.setAttribute('data-status', String(status));
    ['btn-sb-baru','btn-sb-desain','btn-sb-cetak','btn-sb-selesai','btn-sb-diambil','btn-sb-dikirim'].forEach(cls => {
      btn.classList.remove(cls);
    });
    btn.style.backgroundColor = meta.color;
    btn.style.borderColor = meta.color;
    btn.style.color = '#fff';

    const text = btn.querySelector('.text');
    if (text) text.textContent = meta.label;
    const icon = btn.querySelector('i');
    if (icon) icon.className = `fa fa-${meta.icon}`;

    const row = btn.closest('tr[data-st]');
    if (row) row.setAttribute('data-st', String(status));
  }

  function aggregateStatusFromModal(){
    const rows = document.querySelectorAll('#sb-modal-body tr[data-st]');
    if (!rows.length) return null;

	let selectedStatus = null;
	let selectedRank = Number.MAX_SAFE_INTEGER;
    rows.forEach(row => {
      const st = parseInt(row.getAttribute('data-st') || '0', 10);
	  const meta = configuredStatus(st);
	  const rank = parseInt(meta.order === undefined ? 999 : meta.order, 10);
	  if (rank < selectedRank) { selectedStatus = st; selectedRank = rank; }
    });
	return selectedStatus === null ? 0 : selectedStatus;
  }

  function updateGridCardStatus(trx, status){
    if (!trx) return;
    const card = Array.from(document.querySelectorAll('#grid .card[data-trx]'))
      .find(el => (el.getAttribute('data-trx') || '') === trx);
    if (!card) return;

	const meta = configuredStatus(status);
	card.setAttribute('data-status', String(status));
	card.style.background = meta.color;
    lastGridSignature = gridSignatureFromDom();
  }

  function rememberActiveCardHtml(){
    const body = getCardBody();
    if (!body || !activeTrx) return;
    cardCache.set(activeTrx, {html: body.innerHTML, time: Date.now()});
  }

  async function handleEditStatus(btn){
    const cardBody = getCardBody();
    if (!cardBody || !cardBody.contains(btn)) return;
    const id = btn.getAttribute('data-id') || '';
    const status = parseInt(btn.getAttribute('data-status') || '0', 10);
    if (!id || status === 4 || status === 5) return;

    let infoJson = null;
    const inlineCekBahan = btn.getAttribute('data-cek-bahan');
    if (inlineCekBahan !== null && inlineCekBahan !== '') {
      infoJson = {
        idorder: id,
        status: status,
        cek_bahan: parseInt(inlineCekBahan || '0', 10)
      };
    } else {
      const info = await postAjax(getLaporanUrl, {id});
      if (!info.json || typeof info.json !== 'object') {
        alert('Gagal mengambil data status.');
        return;
      }
      infoJson = info.json;
    }

    const opts = nextStatusOptions(infoJson.status, infoJson.cek_bahan);
    if (!opts.length) return;

	const defaultVal = String(opts[0].n);
	const guide = opts.map(o => `${o.n} = ${o.t}`).join('\n');
    const answer = window.prompt(`Pilih status baru:\n${guide}`, defaultVal);
    if (answer === null) return;
    const pick = String(parseInt(answer, 10));
	const chosen = opts.find(o => String(o.n) === pick);
    if (!chosen) {
      alert('Pilihan status tidak valid.');
      return;
    }

    const oldDisabled = btn.disabled;
    btn.disabled = true;
    let save = null;
    try {
      save = await postAjax(simpanLaporanUrl, {
        type: 'proses',
        id: infoJson.idorder,
        status: chosen.v
      });
    } catch (_) {
      btn.disabled = oldDisabled;
      alert('Gagal menyimpan status.');
      return;
    }
    btn.disabled = oldDisabled;

    if (!save.json || save.json.status !== 200) {
      const msg = save.json && save.json.msg ? save.json.msg : 'Gagal menyimpan status.';
      alert(msg);
      return;
    }

    applyStatusToButton(btn, chosen.v);
    const aggregateStatus = aggregateStatusFromModal();
    if (aggregateStatus !== null) updateGridCardStatus(activeTrx, aggregateStatus);
    rememberActiveCardHtml();
  }

  async function uploadBuktiFromFile(file, encId, previewId, actionEl){
    if (!file || !encId) return false;
    if (sbCameraState.uploading) {
      alert('Masih ada proses upload berjalan.');
      return false;
    }

    sbCameraState.uploading = true;
    try {
      const fd = new FormData();
      fd.append('id', encId);
      fd.append('lampiran', file);

      const res = await postAjax(uploadBuktiUrl, fd, true);
      if (!res.json || !res.json.success) {
        const msg = res.json && res.json.message ? res.json.message : 'Upload bukti gagal.';
        alert(msg);
        return false;
      }

      const wrap = previewId ? document.getElementById(previewId) : null;
      if (wrap && res.json.url) {
        wrap.innerHTML = `<a class="bukti-thumb bukti-open" href="${escapeHtml(res.json.url)}">
          <img src="${escapeHtml(res.json.url)}" alt="Bukti progres">
          <span>Lihat bukti</span>
        </a>`;
      }

      let action = actionEl || null;
      if (!action && wrap) {
        action = wrap.closest('.progres-wrap')?.querySelector('.status-item-actions') || null;
      }
      if (action) action.remove();
      return true;
    } finally {
      sbCameraState.uploading = false;
    }
  }

  async function handleUploadInput(input){
    const cardBody = getCardBody();
    if (!cardBody || !cardBody.contains(input)) return;
    const file = input.files && input.files[0] ? input.files[0] : null;
    const encId = input.getAttribute('data-id') || '';
    const previewId = input.getAttribute('data-preview') || '';
    if (!file || !encId) {
      input.value = '';
      return;
    }

    await uploadBuktiFromFile(file, encId, previewId, input.closest('.status-item-actions'));
    input.value = '';
  }

  async function handleCameraInput(input){
    const cardBody = getCardBody();
    if (!cardBody || !cardBody.contains(input)) return;
    const file = input.files && input.files[0] ? input.files[0] : null;
    const encId = input.getAttribute('data-id') || '';
    const previewId = input.getAttribute('data-preview') || '';
    if (!file || !encId) {
      input.value = '';
      sbCameraState.target = null;
      return;
    }

    await uploadBuktiFromFile(file, encId, previewId, input.closest('.status-item-actions'));
    input.value = '';
    sbCameraState.target = null;
  }

  document.getElementById('sb-modal-close').addEventListener('click', closeModal);
  document.getElementById('sb-modal').addEventListener('click', (e) => {
    if (e.target.id === 'sb-modal') closeModal();
  });
  document.getElementById('sb-catatan-modal').addEventListener('click', (e) => {
    if (e.target.id === 'sb-catatan-modal') closeCatatanInCard();
  });
  initGallerySwipe();

  document.addEventListener('click', async (e) => {
    const card = e.target.closest('.card[data-trx]');
    if (!card) return;
    const trx = card.getAttribute('data-trx');
    if (!trx) return;
    await loadCard(trx);
  });

  function openBuktiInCard(url){
    if (!url) return;
    const modal = document.querySelector('#sb-modal-body #bukti-modal');
    const img = document.querySelector('#sb-modal-body #bukti-modal-img');
    if (img) img.src = url;
    if (modal) modal.classList.add('is-open');
  }

  function closeBuktiInCard(){
    const modal = document.querySelector('#sb-modal-body #bukti-modal');
    const img = document.querySelector('#sb-modal-body #bukti-modal-img');
    if (img) img.src = '';
    if (modal) modal.classList.remove('is-open');
  }

  function openBuktiCameraInCard(){
    const modal = document.querySelector('#sb-modal-body #bukti-camera-modal');
    if (modal) modal.classList.add('is-open');
  }

  document.addEventListener('click', (e) => {
    const catatanBtn = e.target.closest('.btn-catatan-popup');
    if (catatanBtn && document.getElementById('sb-modal-body')?.contains(catatanBtn)) {
      e.preventDefault();
      const url = catatanBtn.getAttribute('data-url') || '';
      openCatatanInCard(url);
      return;
    }

    const link = e.target.closest('.bukti-open');
    if (link && document.getElementById('sb-modal-body')?.contains(link)) {
      e.preventDefault();
      openBuktiInCard(link.getAttribute('href'));
      return;
    }
    const galleryBtn = e.target.closest('.btn-catatan-gallery');
    if (galleryBtn && document.getElementById('sb-modal-body')?.contains(galleryBtn)) {
      e.preventDefault();
      let galleryItems = [];
      try {
        const rawGallery = galleryBtn.getAttribute('data-gallery');
        if (rawGallery) {
          galleryItems = JSON.parse(rawGallery || '[]');
        } else {
          galleryItems = JSON.parse(galleryBtn.getAttribute('data-images') || '[]');
        }
      } catch (_) {
        galleryItems = [];
      }
      openGalleryInCard(galleryItems, galleryBtn.getAttribute('data-product') || '');
      return;
    }

    const cameraBtn = e.target.closest('.btn-bukti-camera');
    if (cameraBtn && document.getElementById('sb-modal-body')?.contains(cameraBtn)) {
      e.preventDefault();
      if (sbCameraState.uploading) {
        alert('Masih ada proses upload berjalan.');
        return;
      }

      const encId = cameraBtn.getAttribute('data-id') || '';
      const previewId = cameraBtn.getAttribute('data-preview') || '';
      const mobileInputId = cameraBtn.getAttribute('data-mobile-input') || '';
      if (!encId) return;

      sbCameraState.target = {
        encId,
        previewId,
        actionEl: cameraBtn.closest('.status-item-actions')
      };

      if (isMobileCameraDevice()) {
        const mobileInput = mobileInputId ? document.getElementById(mobileInputId) : null;
        if (mobileInput) {
          mobileInput.click();
        } else {
          sbCameraState.target = null;
        }
        return;
      }

      if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) {
        alert('Browser tidak mendukung webcam.');
        sbCameraState.target = null;
        return;
      }

      const video = document.querySelector('#sb-modal-body #bukti-camera-video');
      if (!video) {
        alert('Komponen camera tidak ditemukan.');
        sbCameraState.target = null;
        return;
      }

      openBuktiCameraInCard();
      navigator.mediaDevices.getUserMedia({
        video: { facingMode: 'environment' },
        audio: false
      }).then((stream) => {
        sbCameraState.stream = stream;
        video.srcObject = stream;
        if (video.play) video.play();
      }).catch(() => {
        closeBuktiCameraInCard();
        sbCameraState.target = null;
        alert('Tidak bisa mengakses camera.');
      });
      return;
    }

    const uploadBtn = e.target.closest('.btn-bukti-upload');
    if (uploadBtn && document.getElementById('sb-modal-body')?.contains(uploadBtn)) {
      const inputId = uploadBtn.getAttribute('data-input');
      const input = inputId ? document.getElementById(inputId) : null;
      if (input) input.click();
      return;
    }

    const captureBtn = e.target.closest('#bukti-camera-capture');
    if (captureBtn && document.getElementById('sb-modal-body')?.contains(captureBtn)) {
      e.preventDefault();
      if (sbCameraState.uploading) {
        alert('Masih ada proses upload berjalan.');
        return;
      }
      if (!sbCameraState.target || !sbCameraState.target.encId) return;

      const video = document.querySelector('#sb-modal-body #bukti-camera-video');
      const canvas = document.querySelector('#sb-modal-body #bukti-camera-canvas');
      if (!video || !canvas || !video.videoWidth || !video.videoHeight) {
        alert('Camera belum siap.');
        return;
      }

      canvas.width = video.videoWidth;
      canvas.height = video.videoHeight;
      const ctx = canvas.getContext('2d');
      ctx.drawImage(video, 0, 0, canvas.width, canvas.height);

      const target = sbCameraState.target;
      canvas.toBlob(async (blob) => {
        if (!blob) {
          alert('Gagal mengambil foto.');
          return;
        }

        let fileObj = blob;
        if (typeof File !== 'undefined') {
          try {
            fileObj = new File([blob], 'bukti-camera.jpg', {
              type: 'image/jpeg',
              lastModified: Date.now()
            });
          } catch (_) {
            fileObj = blob;
          }
        }

        closeBuktiCameraInCard();
        await uploadBuktiFromFile(fileObj, target.encId, target.previewId, target.actionEl || null);
        sbCameraState.target = null;
      }, 'image/jpeg', 0.92);
      return;
    }

    const editBtn = e.target.closest('.edit_baru');
    if (editBtn && document.getElementById('sb-modal-body')?.contains(editBtn)) {
      e.preventDefault();
      handleEditStatus(editBtn);
      return;
    }
    if (e.target && (e.target.id === 'bukti-modal' || e.target.id === 'bukti-modal-close')) {
      closeBuktiInCard();
      return;
    }
    if (e.target && (e.target.id === 'bukti-camera-modal' || e.target.id === 'bukti-camera-close')) {
      closeBuktiCameraInCard();
      if (!sbCameraState.uploading) sbCameraState.target = null;
      return;
    }
    if (e.target && (e.target.id === 'sb-gallery-modal' || e.target.id === 'sb-gallery-close')) {
      closeGalleryInCard();
      return;
    }
    if (e.target && e.target.id === 'sb-gallery-prev') {
      prevGalleryInCard();
      return;
    }
    if (e.target && e.target.id === 'sb-gallery-next') {
      nextGalleryInCard();
    }
  });

  document.addEventListener('change', (e) => {
    const input = e.target.closest && e.target.closest('.bukti-input');
    if (input) {
      handleUploadInput(input);
      return;
    }
    const cameraInput = e.target.closest && e.target.closest('.bukti-camera-input');
    if (cameraInput) {
      handleCameraInput(cameraInput);
    }
  });

  document.addEventListener('keydown', (e) => {
    const galleryOpen = document.getElementById('sb-gallery-modal')?.classList.contains('is-open');
    if (galleryOpen && e.key === 'ArrowRight') {
      e.preventDefault();
      nextGalleryInCard();
      return;
    }
    if (galleryOpen && e.key === 'ArrowLeft') {
      e.preventDefault();
      prevGalleryInCard();
      return;
    }
    if (e.key === 'Escape') {
      closeCatatanInCard();
      closeGalleryInCard();
      closeBuktiInCard();
      closeBuktiCameraInCard();
      if (!sbCameraState.uploading) sbCameraState.target = null;
    }
  });

  // initial + auto refresh
  lastGridSignature = gridSignatureFromDom();
  refreshDateTimeInfo();
  setInterval(refreshDateTimeInfo, 1000);
  setInterval(refreshGrid, 5000);
</script>
</body>
</html>
