<!DOCTYPE html>
<html lang="id">
<head>
  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1" />
  <meta name="csrf-token" content="{{ csrf_token() }}">
  <title>OKELaundry - Admin Dashboard</title>
  @include('partials.favicon')
  @include('partials.brand-fonts')
  <link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" />
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/cropperjs@1.6.2/dist/cropper.min.css" />
  <style>
    :root{
      --brand-deep:#1f7fb7;--brand-main:#3aa5e6;--brand-accent:#4BB8FA;--brand-soft:#7BCBF6;
      --brand-tint:#C4E2F5;--brand-tint-2:#E5F2FB;
      --blue:#4BB8FA;--danger:#dc2626;--text:#0F172A;--muted:#5B6B80;--line:#D9E6F2;--bg:#F4F9FE;--card:#FFFFFF;--shadow:0 14px 30px rgba(31,127,183,.10);
      font-family:'Plus Jakarta Sans',Inter,Segoe UI,Roboto,system-ui,sans-serif
    }
    *{box-sizing:border-box} body{margin:0;background:linear-gradient(180deg,#F4F9FE 0%,#E5F2FB 100%);color:var(--text)}
    button,input,select,textarea{font:inherit}.hidden{display:none !important}.page-wrap{max-width:1280px;margin:0 auto;padding:24px}.screen{background:var(--card);border:1px solid #D9E6F2;border-radius:24px;overflow:hidden;box-shadow:var(--shadow)}
    .admin-bar{background:linear-gradient(135deg,var(--brand-deep),var(--brand-main));color:#fff;padding:18px 28px;display:flex;justify-content:space-between;align-items:center;font-weight:700}.admin-bar-title{font-size:20px;color:#fff}.admin-bar-meta{font-size:12px;display:flex;align-items:center;gap:12px}.admin-bar-meta a{color:#fff;text-decoration:none;font-weight:700;opacity:.9}.admin-bar-meta a:hover{text-decoration:underline;opacity:1}
    .brand-strip{background:linear-gradient(135deg,rgba(255,255,255,.08),rgba(255,255,255,0) 45%),linear-gradient(90deg,var(--brand-deep),var(--brand-main),var(--brand-accent));color:#fff;padding:12px 28px;display:flex;justify-content:space-between;align-items:center}
    .brand-mini{display:flex;align-items:center;gap:10px;font-weight:700}.brand-sub{font-size:14px;opacity:.95}.logo-dot{width:22px;height:22px;border-radius:50%;background:#fff1;border:2px solid #fff;box-shadow:inset 0 0 0 4px #C4E2F5}
    .admin-shell{padding:26px 28px 34px;background:var(--bg)}.admin-title{margin:4px 0 18px;text-align:center;font-size:34px;letter-spacing:-0.03em}.sub-tabs{display:flex;gap:12px;justify-content:flex-start;flex-wrap:wrap;margin-bottom:16px}
    .sub-tab,.stage-btn{border:none;border-radius:12px;padding:12px 18px;background:#E5F2FB;color:#1f3a5b;font-weight:700;cursor:pointer;transition:background .2s ease}.sub-tab:hover{background:#C4E2F5}.sub-tab.active{background:linear-gradient(135deg,var(--brand-main),var(--brand-accent));color:#fff}
    .admin-tab-panel{display:none;margin-top:24px}.admin-tab-panel.active{display:block}.data-grid{display:grid;grid-template-columns:1.05fr .95fr;gap:24px;align-items:start}.panel{background:#fff;border:1px solid var(--line);border-radius:22px;padding:18px;box-shadow:0 6px 16px rgba(31,127,183,.06)}
    .map-card{background:#fff;border:1px solid var(--line);border-radius:22px;padding:18px}.map-card h4{margin:0 0 12px;font-size:20px}#adminMap{height:360px;border-radius:16px;overflow:hidden;background:#E5F2FB}.map-note{font-size:12px;color:var(--muted);margin-top:8px}
    .input-main,.select-main,.textarea-main{width:100%;border:1px solid #D9E6F2;border-radius:12px;padding:14px 16px;background:#fff;color:var(--text);transition:border-color .2s ease}.input-main:focus,.select-main:focus,.textarea-main:focus{outline:none;border-color:var(--brand-accent);box-shadow:0 0 0 3px rgba(75,184,250,.18)}.textarea-main{min-height:96px;resize:vertical}.stack{display:grid;gap:10px}
    .toolbar,.pair-actions,.stage-buttons{display:flex;gap:10px;flex-wrap:wrap}.btn-secondary,.btn-muted{border:none;border-radius:12px;padding:12px 16px;font-weight:700;cursor:pointer;transition:transform .15s ease}.btn-secondary{background:linear-gradient(135deg,var(--brand-main),var(--brand-accent));color:#fff;box-shadow:0 10px 20px rgba(75,184,250,.28)}.btn-secondary:hover{transform:translateY(-1px)}.btn-secondary:active{transform:translateY(1px)}.btn-secondary:disabled{opacity:.6;cursor:not-allowed;transform:none}
    .btn-muted{background:#E5F2FB;color:#1f3a5b;border:1px solid #C4E2F5}.btn-muted:hover{background:#C4E2F5}.list-card{margin-top:16px;border:1px solid var(--line);border-radius:18px;background:#fff;padding:14px}.list-head{font-weight:700;margin-bottom:10px}.list{max-height:320px;overflow:auto;display:grid;gap:10px}.list-item{border:1px solid #E2EAF3;border-radius:14px;padding:12px;background:#fff;transition:border-color .2s ease}.list-item:hover{border-color:var(--brand-tint)}
    .list-actions{display:flex;gap:8px;margin-top:10px}.list-actions button{flex:1;padding:10px 12px;border-radius:10px;border:1px solid #C4E2F5;background:#F4F9FE;cursor:pointer;font-weight:600;transition:background .15s ease}.list-actions button:hover{background:#E5F2FB}.meta-muted{font-size:13px;color:var(--muted)}.order-grid{max-width:960px;margin:0 auto}.detail-box{padding:12px 0 4px}.stage-btn{background:#F4F9FE;border:1px solid #C4E2F5;padding:10px 14px;transition:background .15s ease}.stage-btn:hover{background:#E5F2FB}.stage-btn.active{background:var(--blue);color:#fff;border-color:var(--blue)}.stage-btn.reject{background:#fff2f2;color:var(--danger);border-color:#fecaca}
    .rekap-wrap{max-width:980px;margin:0 auto}.small-note{font-size:13px;color:var(--muted);margin-top:0;line-height:1.55}table{width:100%;border-collapse:collapse;background:#fff;border-radius:16px;overflow:hidden;border:1px solid var(--line);margin-top:14px}th,td{padding:12px 14px;border-bottom:1px solid #E5F2FB;text-align:left;font-size:14px}th{background:#E5F2FB;color:var(--brand-deep);font-size:13px;text-transform:none}
    .summary-card{margin-top:18px;background:#fff;border:1px solid var(--line);border-radius:18px;padding:18px 20px;line-height:1.75}.footer-note{margin-top:24px;padding-top:16px;border-top:1px solid var(--line);font-size:12px;color:var(--muted)}
    .loading-overlay{display:none;position:fixed;inset:0;background:rgba(255,255,255,.7);z-index:9999;place-items:center}.loading-overlay.show{display:grid}.loading-spinner{width:40px;height:40px;border:4px solid #eee;border-top-color:var(--brand-main);border-radius:50%;animation:spin .6s linear infinite}@keyframes spin{to{transform:rotate(360deg)}}
    .toast{position:fixed;bottom:24px;right:24px;padding:14px 20px;border-radius:14px;color:#fff;font-weight:700;font-size:14px;z-index:9999;opacity:0;transform:translateY(12px);transition:opacity .3s ease,transform .3s ease}.toast.show{opacity:1;transform:translateY(0)}.toast.success{background:#2ECC71}.toast.error{background:var(--danger)}
    @media (max-width:980px){.data-grid{grid-template-columns:1fr}.admin-bar{flex-direction:column;gap:8px;text-align:center}}
    .ic{width:16px;height:16px;display:inline-block;vertical-align:middle;fill:none;stroke:currentColor;stroke-width:1.8;stroke-linecap:round;stroke-linejoin:round}
    .btn-wa{display:block;text-align:center;text-decoration:none;padding:10px 12px;border-radius:10px;border:1px solid #1ebe57;background:#25D366;color:#fff;font-weight:700;font-size:13px;cursor:pointer;transition:background .15s ease}
    .btn-wa:hover{background:#1ebe57}
    .btn-wa.disabled{background:#e7e7e7;border-color:#dcdcdc;color:#9a9a9a;cursor:not-allowed}
    .btn-print{display:block;text-align:center;text-decoration:none;padding:10px 12px;border-radius:10px;border:1px solid #5b6cff;background:#4A67FF;color:#fff;font-weight:700;font-size:13px;cursor:pointer;transition:background .15s ease}
    .btn-print:hover{background:#3a55ee}
    .stats-row{display:grid;grid-template-columns:repeat(5,1fr);gap:14px;margin-bottom:8px}
    .stat-card{background:#fff;border:1px solid var(--line);border-radius:18px;padding:16px 18px;box-shadow:0 6px 16px rgba(36,18,18,.04)}
    .stat-label{font-size:12px;color:var(--muted);font-weight:700;text-transform:uppercase;letter-spacing:.02em}
    .stat-value{margin-top:6px;font-size:26px;font-weight:800;color:var(--brand-deep);letter-spacing:-0.02em}
    @media (max-width:980px){.stats-row{grid-template-columns:repeat(2,1fr)}}
  </style>
</head>
<body>
  <div class="loading-overlay" id="loadingOverlay"><div class="loading-spinner"></div></div>
  <div class="toast" id="toast"></div>

  <div class="page-wrap">
    <section class="screen">
      <div class="admin-bar">
        <div class="admin-bar-title">OKE Laundry Administration</div>
        <div class="admin-bar-meta">
          <span>Selamat datang, {{ Auth::user()->name }}</span>
          <a href="/" target="_blank">Lihat Portal</a>
          <a href="#" onclick="event.preventDefault(); document.getElementById('logout-form').submit();">Keluar</a>
        </div>
      </div>
      <form id="logout-form" action="{{ route('logout') }}" method="POST" class="hidden">
        @csrf
      </form>
      <div class="brand-strip"><div class="brand-mini">@include('partials.brand-logo', ['size' => 26])</div><div class="brand-sub">Cuci Ekspres - Pasti Beres</div></div>
      <div class="admin-shell">
        <h2 class="admin-title">Admin Dashboard</h2>

        <div id="statsRow" class="stats-row">
          <div class="stat-card"><div class="stat-label">Order Hari Ini</div><div class="stat-value" id="statToday">0</div></div>
          <div class="stat-card"><div class="stat-label">Total Order</div><div class="stat-value" id="statTotal">0</div></div>
          <div class="stat-card"><div class="stat-label">Sedang Diproses</div><div class="stat-value" id="statProses">0</div></div>
          <div class="stat-card"><div class="stat-label">Selesai</div><div class="stat-value" id="statSelesai">0</div></div>
          <div class="stat-card"><div class="stat-label">Total Ongkir</div><div class="stat-value" id="statOngkir">Rp 0</div></div>
        </div>

        <div id="panelDataCustomer" class="admin-tab-panel active">
          <div class="data-grid">
            <div>
              <div class="map-card">
                <h4>Peta Lokasi</h4>
                <div id="adminMap"></div>
                <div class="map-note">Zona hijau = radius 2 km (gratis ongkir). Klik peta untuk memilih titik lokasi customer.</div>
              </div>
            </div>
            <div>
              <div class="panel">
                <div class="sub-tabs">
                  <button type="button" class="sub-tab sub-tab-nav active" data-target="data">Data Pelanggan</button>
                  <button type="button" class="sub-tab sub-tab-nav" data-target="order">Kelola Pesanan</button>
                  <button type="button" class="sub-tab sub-tab-nav" data-target="rekap">Rekap Pesanan</button>
                  <button type="button" class="sub-tab sub-tab-nav" data-target="settings">Pengaturan</button>
                </div>
                <div class="stack">
                  <input id="custName" class="input-main" placeholder="Nama Customer" aria-label="Nama Customer" />
                  <input id="custPhone" class="input-main" placeholder="No Hp" aria-label="Nomor HP" />
                  <input id="custAddr" class="input-main" placeholder="Alamat" aria-label="Alamat" />
                </div>
                <div class="toolbar" style="margin-top:12px">
                  <button type="button" id="btnAddCust" class="btn-secondary">Tambah/Perbarui Pelanggan</button>
                  <button type="button" id="btnUseMarker" class="btn-muted">Gunakan Titik</button>
                  <button type="button" id="btnGeoLoc" class="btn-muted" title="Pakai lokasi GPS perangkat ini">Gunakan Lokasi Saya</button>
                  <button type="button" id="btnShowCust" class="btn-muted">Tampilkan</button>
                </div>
                <div class="list-card">
                  <div class="list-head">Daftar Customer</div>
                  <div id="custList" class="list"></div>
                </div>
              </div>
            </div>
          </div>
        </div>

        <div id="panelKelolaOrder" class="admin-tab-panel">
          <div class="order-grid">
            <div class="panel">
              <div class="sub-tabs">
                <button type="button" class="sub-tab sub-tab-nav" data-target="data">Data Pelanggan</button>
                <button type="button" class="sub-tab sub-tab-nav active" data-target="order">Kelola Pesanan</button>
                <button type="button" class="sub-tab sub-tab-nav" data-target="rekap">Rekap Pesanan</button>
                <button type="button" class="sub-tab sub-tab-nav" data-target="settings">Pengaturan</button>
              </div>
              <h4 style="margin-top:0">Kelola Pesanan</h4>
              <div class="stack">
                <div style="display:flex;gap:10px">
                  <input id="orderNomor" class="input-main" placeholder="No resi (contoh: OKL-20260610-001)" aria-label="Nomor Resi" style="flex:1" />
                  <button type="button" id="btnGenResi" class="btn-muted" title="Buat nomor resi otomatis">Generate</button>
                </div>
                <select id="orderCust" class="select-main" aria-label="Pilih Customer"><option value="">Pilih Customer</option></select>
                <select id="orderStatus" class="select-main" aria-label="Status Pesanan">
                  <option value="jemput">Jemput</option>
                  <option value="proses">Proses</option>
                  <option value="antar">Antar</option>
                  <option value="selesai">Selesai</option>
                  <option value="ditolak">Ditolak</option>
                </select>
              </div>

              <div id="processStageContainer" class="detail-box hidden">
                <div class="meta-muted">Detail Proses:</div>
                <div class="stage-buttons" style="margin-top:8px">
                  <button type="button" class="stage-btn" data-stage="cuci">Cuci</button>
                  <button type="button" class="stage-btn" data-stage="kering">Kering</button>
                  <button type="button" class="stage-btn" data-stage="setrika">Setrika</button>
                  <button type="button" class="stage-btn reject" data-stage="ditolak"><svg class="ic" viewBox="0 0 24 24" style="margin-right:4px"><circle cx="12" cy="12" r="9"/><path d="M15 9l-6 6M9 9l6 6"/></svg> Ditolak</button>
                </div>
              </div>

              <textarea id="orderNote" class="textarea-main" placeholder="Catatan (opsional)" aria-label="Catatan Order"></textarea>
              <input id="orderJarak" class="input-main" placeholder="Jarak otomatis (Haversine)" readonly style="background:#F4F9FE;margin-top:10px" aria-label="Jarak" />
              <div class="pair-actions" style="margin-top:12px">
                <button type="button" id="btnAddOrder" class="btn-secondary">Tambah/Update Order</button>
              </div>

              <div class="list-card">
                <div class="list-head" style="display:flex;justify-content:space-between;align-items:center;gap:10px;flex-wrap:wrap">
                  <span>Daftar Order</span>
                  <span style="display:flex;align-items:center;gap:10px">
                    <span class="meta-muted" id="orderCount" style="font-weight:600"></span>
                    <button type="button" id="btnExportOrder" class="btn-muted" style="padding:8px 14px;font-size:13px" title="Export ke CSV (bisa dibuka di Excel)">Export Excel</button>
                  </span>
                </div>
                <div class="filter-row" style="display:grid;grid-template-columns:1.4fr 1fr 1fr;gap:8px;margin-bottom:10px">
                  <input id="orderSearch" class="input-main" placeholder="Cari resi atau nama pelanggan..." aria-label="Cari order" style="padding:10px 12px" />
                  <select id="orderFilterStatus" class="select-main" aria-label="Filter status" style="padding:10px 12px">
                    <option value="">Semua status</option>
                    <option value="jemput">Jemput</option>
                    <option value="proses">Proses</option>
                    <option value="antar">Antar</option>
                    <option value="selesai">Selesai</option>
                    <option value="ditolak">Ditolak</option>
                  </select>
                  <input id="orderFilterDate" type="date" class="input-main" aria-label="Filter tanggal" style="padding:10px 12px" />
                </div>
                <div id="orderList" class="list"></div>
              </div>
            </div>
          </div>
        </div>

        <div id="panelRekap" class="admin-tab-panel">
          <div class="rekap-wrap">
            <div class="panel">
              <div class="sub-tabs">
                <button type="button" class="sub-tab sub-tab-nav" data-target="data">Data Pelanggan</button>
                <button type="button" class="sub-tab sub-tab-nav" data-target="order">Kelola Pesanan</button>
                <button type="button" class="sub-tab sub-tab-nav active" data-target="rekap">Rekap Pesanan</button>
                <button type="button" class="sub-tab sub-tab-nav" data-target="settings">Pengaturan</button>
              </div>
              <h4 style="margin-top:0;display:flex;justify-content:space-between;align-items:center;gap:10px;flex-wrap:wrap">
                <span>Rekap Pesanan Per-Customer</span>
                <button type="button" id="btnExportRekap" class="btn-muted" style="padding:8px 14px;font-size:13px;font-weight:700" title="Export rekap ke CSV (bisa dibuka di Excel)">Export Excel</button>
              </h4>
              <div class="small-note">Rekap otomatis berdasarkan semua data order yang tersimpan di database. Setiap baris mewakili satu customer.</div>
              <table id="rekapTable">
                <thead>
                  <tr><th>Customer</th><th>Jumlah Order</th><th>Total Biaya Ongkir</th><th>Rata-rata Jarak</th><th>Order Terakhir</th><th>Struk</th></tr>
                </thead>
                <tbody></tbody>
              </table>
              <div id="rekapSummary" class="summary-card"></div>
            </div>
          </div>
        </div>

        <div id="panelSettings" class="admin-tab-panel">
          <div class="rekap-wrap">
            <div class="panel">
              <div class="sub-tabs">
                <button type="button" class="sub-tab sub-tab-nav" data-target="data">Data Pelanggan</button>
                <button type="button" class="sub-tab sub-tab-nav" data-target="order">Kelola Pesanan</button>
                <button type="button" class="sub-tab sub-tab-nav" data-target="rekap">Rekap Pesanan</button>
                <button type="button" class="sub-tab sub-tab-nav active" data-target="settings">Pengaturan</button>
              </div>
              <h4 style="margin-top:0">Branding Web</h4>
              <div class="small-note" style="margin-bottom:14px">Logo akan tampil di header semua halaman (pelanggan, login, admin, struk). Favicon tampil di tab browser.</div>

              <div style="display:grid;grid-template-columns:1fr 1fr;gap:16px" class="branding-grid">
                <div class="list-card" style="margin:0">
                  <div class="list-head">Logo Web</div>
                  <div style="display:flex;align-items:center;gap:14px;margin-bottom:10px">
                    <div id="logoPreview" style="width:120px;height:60px;border-radius:10px;border:1px solid var(--line);background:linear-gradient(90deg,var(--brand-deep),var(--brand-main));display:flex;align-items:center;justify-content:center;color:#fff;font-weight:800;overflow:hidden">OKELaundry</div>
                    <div class="meta-muted" style="font-size:12px;line-height:1.55">Format PNG/JPG/WebP/SVG, maks <b>1&nbsp;MB</b>. Disarankan transparent PNG, tinggi 64–128 px.</div>
                  </div>
                  <form id="logoForm" enctype="multipart/form-data" class="stack">
                    <input id="logoFile" type="file" accept="image/png,image/jpeg,image/webp,image/svg+xml" class="input-main" required />
                    <div class="toolbar">
                      <button type="submit" class="btn-secondary">Unggah Logo</button>
                      <button type="button" id="btnDeleteLogo" class="btn-muted">Hapus Logo</button>
                    </div>
                  </form>
                </div>
                <div class="list-card" style="margin:0">
                  <div class="list-head">Favicon</div>
                  <div style="display:flex;align-items:center;gap:14px;margin-bottom:10px">
                    <div id="faviconPreview" style="width:48px;height:48px;border-radius:10px;border:1px solid var(--line);background:#E5F2FB;display:flex;align-items:center;justify-content:center;color:var(--brand-deep);font-weight:800;overflow:hidden">OL</div>
                    <div class="meta-muted" style="font-size:12px;line-height:1.55">Format PNG/ICO/SVG/WebP, maks <b>512&nbsp;KB</b>. Disarankan kotak (mis. 64×64 atau 192×192).</div>
                  </div>
                  <form id="faviconForm" enctype="multipart/form-data" class="stack">
                    <input id="faviconFile" type="file" accept="image/png,image/x-icon,image/svg+xml,image/webp,.ico" class="input-main" required />
                    <div class="toolbar">
                      <button type="submit" class="btn-secondary">Unggah Favicon</button>
                      <button type="button" id="btnDeleteFavicon" class="btn-muted">Hapus Favicon</button>
                    </div>
                  </form>
                </div>
              </div>

              <hr style="border:none;border-top:1px solid var(--line);margin:22px 0">
              <h4 style="margin:0 0 6px">Informasi Mitra (OKE Laundry)</h4>
              <div class="small-note" style="margin-bottom:12px">Data ini dipakai untuk perhitungan ongkir (Haversine), titik default peta, dan halaman "Tentang". Klik peta di tab Data Pelanggan untuk mendapatkan koordinat.</div>
              <form id="mitraForm" class="stack" style="margin-bottom:8px">
                <div style="display:grid;grid-template-columns:1fr 1fr;gap:10px" class="branding-grid">
                  <div class="stack" style="gap:6px"><label class="meta-muted" for="mitraName">Nama Mitra</label><input id="mitraName" class="input-main" maxlength="255" required /></div>
                  <div class="stack" style="gap:6px"><label class="meta-muted" for="mitraPhone">No HP / WhatsApp</label><input id="mitraPhone" class="input-main" placeholder="081234567890" /></div>
                </div>
                <div class="stack" style="gap:6px"><label class="meta-muted" for="mitraAddress">Alamat</label><input id="mitraAddress" class="input-main" maxlength="500" /></div>
                <div class="stack" style="gap:6px"><label class="meta-muted" for="mitraHours">Jam Operasional</label><input id="mitraHours" class="input-main" placeholder="Setiap hari, 08.00 - 18.00 WIB" maxlength="255" /></div>
                <div style="display:grid;grid-template-columns:1fr 1fr;gap:10px" class="branding-grid">
                  <div class="stack" style="gap:6px"><label class="meta-muted" for="mitraLat">Latitude</label><input id="mitraLat" type="number" step="any" class="input-main" required /></div>
                  <div class="stack" style="gap:6px"><label class="meta-muted" for="mitraLng">Longitude</label><input id="mitraLng" type="number" step="any" class="input-main" required /></div>
                </div>
                <div style="display:grid;grid-template-columns:1fr 1fr;gap:10px" class="branding-grid">
                  <div class="stack" style="gap:6px"><label class="meta-muted" for="mitraCost">Tarif Ongkir per km (Rp)</label><input id="mitraCost" type="number" min="0" class="input-main" required /></div>
                  <div class="stack" style="gap:6px"><label class="meta-muted" for="mitraRadius">Radius Gratis Ongkir (km)</label><input id="mitraRadius" type="number" step="any" min="0" class="input-main" required /></div>
                </div>
                <div class="toolbar"><button type="submit" class="btn-secondary">Simpan Informasi Mitra</button></div>
              </form>

              <hr style="border:none;border-top:1px solid var(--line);margin:22px 0">
              <h4 style="margin:0 0 6px">Banner Halaman Pelanggan</h4>
              <div class="small-note" style="margin-bottom:12px">
                Banner tampil di slider halaman pelanggan dengan rasio <b>3:1</b>. Ukuran ideal <b>1200&times;400 px</b> (JPG/PNG/WebP, maks 2&nbsp;MB).
                Setelah memilih gambar, atur posisi &amp; zoom pada kotak crop di bawah agar pas dengan slide.
              </div>

              <form id="bannerForm" enctype="multipart/form-data" class="stack" style="margin-bottom:8px">
                <input id="bannerImage" type="file" accept="image/jpeg,image/png,image/webp" class="input-main" required />

                <div id="cropWrap" style="display:none">
                  <div style="background:#0c2433;border-radius:12px;overflow:hidden;max-height:320px">
                    <img id="cropImage" style="max-width:100%;display:block" />
                  </div>
                  <div class="toolbar" style="margin-top:10px;align-items:center">
                    <button type="button" class="btn-muted" id="cropZoomIn" title="Perbesar">Zoom +</button>
                    <button type="button" class="btn-muted" id="cropZoomOut" title="Perkecil">Zoom &minus;</button>
                    <button type="button" class="btn-muted" id="cropReset" title="Reset">Reset</button>
                    <span class="meta-muted" style="font-size:12px">Geser gambar untuk atur posisi, scroll untuk zoom.</span>
                  </div>
                </div>

                <input id="bannerTitle" type="text" class="input-main" placeholder="Judul (opsional)" maxlength="120" />
                <input id="bannerCaption" type="text" class="input-main" placeholder="Caption / deskripsi singkat (opsional)" maxlength="255" />
                <input id="bannerSort" type="number" class="input-main" placeholder="Urutan (0 = paling depan)" min="0" max="9999" value="0" />
                <div class="toolbar">
                  <button type="submit" class="btn-secondary">Unggah Banner</button>
                </div>
              </form>

              <div class="list-card">
                <div class="list-head">Daftar Banner</div>
                <div id="bannerList" class="list" style="max-height:none"></div>
              </div>

              <hr style="border:none;border-top:1px solid var(--line);margin:22px 0">
              <h4 style="margin:0 0 6px">Profil Admin</h4>
              <div class="small-note" style="margin-bottom:12px">Ubah nama, email, dan password akun admin Anda.</div>

              <div style="display:grid;grid-template-columns:1fr 1fr;gap:16px" class="branding-grid">
                <div class="list-card" style="margin:0">
                  <div class="list-head">Identitas</div>
                  <form id="profileForm" class="stack">
                    <div class="stack" style="gap:6px">
                      <label class="meta-muted" for="profName">Nama Lengkap</label>
                      <input id="profName" type="text" class="input-main" required maxlength="255" />
                    </div>
                    <div class="stack" style="gap:6px">
                      <label class="meta-muted" for="profEmail">Email</label>
                      <input id="profEmail" type="email" class="input-main" required maxlength="255" />
                    </div>
                    <div class="toolbar">
                      <button type="submit" class="btn-secondary">Simpan Profil</button>
                    </div>
                  </form>
                </div>
                <div class="list-card" style="margin:0">
                  <div class="list-head">Ubah Password</div>
                  <form id="passwordForm" class="stack">
                    <div class="stack" style="gap:6px">
                      <label class="meta-muted" for="profCurrentPwd">Password Saat Ini</label>
                      <input id="profCurrentPwd" type="password" class="input-main" required autocomplete="current-password" />
                    </div>
                    <div class="stack" style="gap:6px">
                      <label class="meta-muted" for="profNewPwd">Password Baru (min 8 karakter)</label>
                      <input id="profNewPwd" type="password" class="input-main" required minlength="8" autocomplete="new-password" />
                    </div>
                    <div class="stack" style="gap:6px">
                      <label class="meta-muted" for="profNewPwdConfirm">Konfirmasi Password Baru</label>
                      <input id="profNewPwdConfirm" type="password" class="input-main" required minlength="8" autocomplete="new-password" />
                    </div>
                    <div class="toolbar">
                      <button type="submit" class="btn-secondary">Ubah Password</button>
                    </div>
                  </form>
                </div>
              </div>
            </div>
          </div>
        </div>

        <div id="panelCourier" class="admin-tab-panel hidden" hidden>>
          <div class="order-grid">
            <div class="panel">
              <h4 style="margin-top:0">Kelola Data Kurir</h4>
              <div class="stack">
                <input id="courierName" class="input-main" placeholder="Nama Kurir" aria-label="Nama Kurir" />
                <input id="courierPhone" class="input-main" placeholder="No HP Kurir" aria-label="No HP Kurir" />
              </div>
              <div class="toolbar" style="margin-top:12px">
                <button type="button" id="btnAddCourier" class="btn-secondary">Tambah/Update Kurir</button>
              </div>
              <div class="list-card">
                <div class="list-head">Daftar Kurir</div>
                <div id="courierList" class="list"></div>
              </div>
            </div>
          </div>
        </div>

        <div class="footer-note">©2025 OKE Laundry — Sistem Informasi Pelacakan dan Pengelolaan Pesanan Laundry Berbasis Web. Dibangun dengan Laravel, MySQL, Leaflet.js & OpenStreetMap.</div>
      </div>
    </section>
  </div>
  <script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/cropperjs@1.6.2/dist/cropper.min.js"></script>
  <script>
    // === Utility Functions ===
    function escapeHtml(str) {
      if (!str) return '';
      const div = document.createElement('div');
      div.textContent = str;
      return div.innerHTML;
    }

    function showLoading() { document.getElementById('loadingOverlay').classList.add('show'); }
    function hideLoading() { document.getElementById('loadingOverlay').classList.remove('show'); }

    function showToast(message, type = 'success') {
      const toast = document.getElementById('toast');
      toast.textContent = message;
      toast.className = 'toast ' + type + ' show';
      setTimeout(() => { toast.classList.remove('show'); }, 3000);
    }

    function isValidPhoneID(phone) {
      // Format Indonesia: 08xxxxxxxxx, +628xxxxxxxxx, 628xxxxxxxxx (10–14 digit total setelah normalisasi)
      const v = String(phone || '').trim();
      return /^(\+62|62|0)8[1-9][0-9]{7,11}$/.test(v);
    }

    // Auto-format input HP saat user mengetik: rapikan jadi 0812-3456-7890
    function attachPhoneFormatter(el) {
      if (!el) return;
      el.addEventListener('input', () => {
        let digits = el.value.replace(/[^\d+]/g, '');
        // Normalisasi awalan +62 / 62 menjadi 0
        if (digits.startsWith('+62')) digits = '0' + digits.slice(3);
        else if (digits.startsWith('62')) digits = '0' + digits.slice(2);
        digits = digits.replace(/\D/g, '').slice(0, 13);
        // Kelompokkan 4-4-sisanya
        let out = digits;
        if (digits.length > 4 && digits.length <= 8) out = digits.slice(0,4) + '-' + digits.slice(4);
        else if (digits.length > 8) out = digits.slice(0,4) + '-' + digits.slice(4,8) + '-' + digits.slice(8);
        el.value = out;
      });
    }

    // === State ===
    let mitra = { lat: -6.39778, lng: 106.79769, cost_per_km: 3500, free_radius_km: 2 };
    let customers = [];
    let orders = [];
    let couriers = [];
    let lastPos = null;
    let currentProcessStage = null;
    const csrfToken = document.querySelector('meta[name="csrf-token"]').getAttribute('content');

    // === Map ===
    const adminMap = L.map('adminMap').setView([mitra.lat, mitra.lng], 14);
    L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { attribution: '&copy; OpenStreetMap' }).addTo(adminMap);
    let mitraCircle = L.circle([mitra.lat, mitra.lng], { radius: 2000, color: 'green', fillOpacity: 0.08 }).addTo(adminMap);
    let mitraMarker = L.marker([mitra.lat, mitra.lng]).addTo(adminMap).bindPopup('OKE Laundry (Mitra)');
    let adminMarker = null;

    async function loadMitra() {
      try {
        const m = await apiCall('/admin/settings/mitra');
        mitra = {
          lat: parseFloat(m.lat), lng: parseFloat(m.lng),
          cost_per_km: parseInt(m.cost_per_km, 10) || 0,
          free_radius_km: parseFloat(m.free_radius_km) || 0,
        };
        mitraMarker.setLatLng([mitra.lat, mitra.lng]).bindPopup(m.name || 'OKE Laundry (Mitra)');
        mitraCircle.setLatLng([mitra.lat, mitra.lng]).setRadius(mitra.free_radius_km * 1000);
        adminMap.setView([mitra.lat, mitra.lng], 14);
      } catch (_) { /* pakai default */ }
    }

    function haversine(lat1, lon1, lat2, lon2) {
      const R = 6371, dLat = (lat2 - lat1) * Math.PI / 180, dLon = (lon2 - lon1) * Math.PI / 180;
      const a = Math.sin(dLat/2)**2 + Math.cos(lat1 * Math.PI/180) * Math.cos(lat2 * Math.PI/180) * Math.sin(dLon/2)**2;
      return 2 * R * Math.asin(Math.sqrt(a));
    }

    // === DOM References ===
    const custName = document.getElementById('custName'), custPhone = document.getElementById('custPhone'), custAddr = document.getElementById('custAddr'),
          orderNomor = document.getElementById('orderNomor'), orderCust = document.getElementById('orderCust'), orderStatus = document.getElementById('orderStatus'),
          orderCourier = document.getElementById('orderCourier'),
          orderNote = document.getElementById('orderNote'), orderJarak = document.getElementById('orderJarak'), custList = document.getElementById('custList'),
          orderList = document.getElementById('orderList'), processStageContainer = document.getElementById('processStageContainer'),
          stageButtons = Array.from(document.querySelectorAll('.stage-btn')),
          courierName = document.getElementById('courierName'), courierPhone = document.getElementById('courierPhone'),
          courierList = document.getElementById('courierList');

    // === API Helper ===
    async function apiCall(url, options = {}) {
      const defaultHeaders = { 'X-CSRF-TOKEN': csrfToken };
      if (options.body && typeof options.body === 'object' && !(options.body instanceof FormData)) {
        defaultHeaders['Content-Type'] = 'application/json';
        options.body = JSON.stringify(options.body);
      }
      options.headers = { ...defaultHeaders, ...(options.headers || {}) };
      
      const res = await fetch(url, options);
      if (!res.ok) {
        const errorData = await res.json().catch(() => ({}));
        const message = errorData.message || errorData.errors 
          ? Object.values(errorData.errors || {}).flat().join(', ') || errorData.message
          : 'Terjadi kesalahan pada server';
        throw new Error(message);
      }
      return res.json();
    }

    // === Load Data ===
    async function loadData() {
      showLoading();
      try {
        const [custData, courierData, orderData] = await Promise.all([
          apiCall('/admin/customers'),
          apiCall('/admin/couriers'),
          apiCall('/admin/orders')
        ]);
        customers = custData;
        couriers = courierData;
        orders = orderData;
        renderCust();
        renderCourier();
        renderOrder();
        showToast('Data berhasil dimuat');
      } catch (e) {
        showToast('Gagal memuat data: ' + e.message, 'error');
      } finally {
        hideLoading();
      }
    }

    // === Map Interaction ===
    adminMap.on('click', e => {
      lastPos = e.latlng;
      if (adminMarker) adminMap.removeLayer(adminMarker);
      adminMarker = L.marker(e.latlng).addTo(adminMap);
      const d = haversine(mitra.lat, mitra.lng, e.latlng.lat, e.latlng.lng);
      const dist = Math.round(d), cost = dist <= mitra.free_radius_km ? 0 : Math.round((dist - mitra.free_radius_km) * mitra.cost_per_km);
      orderJarak.value = dist + ' km';
      adminMarker.bindPopup('Titik Pelanggan<br>Jarak: ' + dist + ' km<br>' + (cost > 0 ? 'Rp ' + cost.toLocaleString() : 'Gratis Ongkir')).openPopup();
    });

    function setMarkerOnAdminMap(pos, label) {
      if (!pos) return;
      if (adminMarker) adminMap.removeLayer(adminMarker);
      adminMarker = L.marker([pos.lat, pos.lng]).addTo(adminMap).bindPopup(label || 'Titik customer').openPopup();
      adminMap.setView([pos.lat, pos.lng], 15);
      lastPos = { lat: pos.lat, lng: pos.lng };
      orderJarak.value = Math.round(haversine(mitra.lat, mitra.lng, pos.lat, pos.lng)) + ' km';
    }

    // === Customer CRUD ===
    function renderCust() {
      custList.innerHTML = '';
      orderCust.innerHTML = '<option value="">Pilih Customer</option>';
      if (!customers.length) {
        custList.innerHTML = '<div class="meta-muted" style="text-align:center;padding:16px">Belum ada data customer.</div>';
      }
      customers.forEach((c, i) => {
        const item = document.createElement('div');
        item.className = 'list-item';
        item.innerHTML = `<div style="font-weight:800">${escapeHtml(c.name)}</div><div class="meta-muted">${escapeHtml(c.phone) || '-'}</div><div class="meta-muted">${escapeHtml(c.address) || '-'}</div><div class="list-actions"><a href="/admin/customers/${c.id}/history" target="_blank" rel="noopener" style="flex:1;padding:10px 12px;border-radius:10px;border:1px solid #C4E2F5;background:#F4F9FE;text-align:center;font-weight:600;text-decoration:none;color:#1f3a5b">Riwayat</a><button type="button" onclick="editCust(${i})">Edit</button><button type="button" onclick="delCust(${c.id})">Hapus</button></div>`;
        custList.appendChild(item);
        const opt = document.createElement('option');
        opt.value = c.id;
        opt.textContent = c.name;
        orderCust.appendChild(opt);
      });
    }

    window.editCust = function(i) {
      const c = customers[i];
      custName.value = c.name; custPhone.value = c.phone || ''; custAddr.value = c.address || '';
      if (c.latitude && c.longitude) {
        setMarkerOnAdminMap({lat: c.latitude, lng: c.longitude}, c.name);
      } else if (c.address) {
        showOnMap(c.address);
      }
      custName.focus();
    };

    window.delCust = async function(id) {
      if (!confirm('Hapus customer ini? Semua order customer ini juga akan terhapus.')) return;
      showLoading();
      try {
        customers = await apiCall(`/admin/customers/${id}`, { method: 'DELETE' });
        renderCust();
        orders = await apiCall('/admin/orders');
        renderOrder();
        showToast('Customer berhasil dihapus');
      } catch (e) {
        showToast('Gagal menghapus customer: ' + e.message, 'error');
      } finally {
        hideLoading();
      }
    };

    document.getElementById('btnAddCust').onclick = async () => {
      if (!custName.value.trim()) return showToast('Nama customer wajib diisi', 'error');
      const phoneVal = custPhone.value.trim().replace(/-/g, '');
      if (phoneVal && !isValidPhoneID(phoneVal)) {
        return showToast('Format nomor HP tidak valid (contoh: 081234567890)', 'error');
      }
      const data = {
        name: custName.value.trim(),
        phone: phoneVal,
        address: custAddr.value.trim(),
        latitude: lastPos ? lastPos.lat : null,
        longitude: lastPos ? lastPos.lng : null
      };
      showLoading();
      try {
        customers = await apiCall('/admin/customers', { method: 'POST', body: data });
        renderCust();
        custName.value = ''; custPhone.value = ''; custAddr.value = '';
        if (adminMarker) { adminMap.removeLayer(adminMarker); adminMarker = null; }
        lastPos = null;
        showToast('Customer berhasil disimpan');
      } catch (e) {
        showToast('Gagal menyimpan: ' + e.message, 'error');
      } finally {
        hideLoading();
      }
    };

    document.getElementById('btnUseMarker').onclick = () => {
      if (!lastPos) return showToast('Klik peta dulu untuk memilih titik customer', 'error');
      showToast('Titik peta siap digunakan untuk customer');
    };

    document.getElementById('btnShowCust').onclick = () => {
      if (!custAddr.value.trim()) return showToast('Masukkan alamat terlebih dahulu', 'error');
      showOnMap(custAddr.value.trim());
    };

    document.getElementById('btnGeoLoc').onclick = () => {
      if (!('geolocation' in navigator)) {
        return showToast('Browser tidak mendukung Geolocation', 'error');
      }
      showLoading();
      navigator.geolocation.getCurrentPosition(
        (pos) => {
          hideLoading();
          const p = { lat: pos.coords.latitude, lng: pos.coords.longitude };
          setMarkerOnAdminMap(p, 'Lokasi Saya');
          showToast('Lokasi GPS berhasil diambil');
        },
        (err) => {
          hideLoading();
          const msg = err.code === 1
            ? 'Izin lokasi ditolak. Aktifkan izin di browser.'
            : err.code === 2 ? 'Lokasi tidak tersedia.'
            : err.code === 3 ? 'Permintaan lokasi timeout.'
            : 'Gagal mengambil lokasi.';
          showToast(msg, 'error');
        },
        { enableHighAccuracy: true, timeout: 10000, maximumAge: 0 }
      );
    };

    async function showOnMap(address) {
      try {
        const res = await fetch('https://nominatim.openstreetmap.org/search?format=json&q=' + encodeURIComponent(address));
        const data = await res.json();
        if (!data.length) return showToast('Alamat tidak ditemukan di peta', 'error');
        const pos = { lat: parseFloat(data[0].lat), lng: parseFloat(data[0].lon) };
        setMarkerOnAdminMap(pos, 'Titik Alamat: ' + address);
      } catch (e) {
        showToast('Gagal memuat peta alamat', 'error');
      }
    }

    // === Courier CRUD ===
    function renderCourier() {
      if (!courierList) return;
      courierList.innerHTML = '';
      populateOrderCourierDropdown();
      if (!couriers.length) {
        courierList.innerHTML = '<div class="meta-muted" style="text-align:center;padding:16px">Belum ada data kurir.</div>';
      }
      couriers.forEach((c, i) => {
        const item = document.createElement('div');
        item.className = 'list-item';
        item.innerHTML = `<div style="font-weight:800">${escapeHtml(c.name)}</div><div class="meta-muted">${escapeHtml(c.phone) || '-'}</div><div class="list-actions"><button type="button" onclick="editCourier(${i})">Edit</button><button type="button" onclick="delCourier(${c.id})">Hapus</button></div>`;
        courierList.appendChild(item);
      });
    }

    function populateOrderCourierDropdown() {
      if (!orderCourier) return;
      const val = orderCourier.value;
      orderCourier.innerHTML = '<option value="">Pilih Kurir (Opsional)</option>';
      couriers.forEach(c => {
        const opt = document.createElement('option');
        opt.value = c.id;
        opt.textContent = c.name;
        orderCourier.appendChild(opt);
      });
      orderCourier.value = val;
    }

    window.editCourier = function(i) {
      const c = couriers[i];
      courierName.value = c.name;
      courierPhone.value = c.phone || '';
      courierName.focus();
    };

    window.delCourier = async function(id) {
      if (!confirm('Hapus kurir ini?')) return;
      showLoading();
      try {
        couriers = await apiCall(`/admin/couriers/${id}`, { method: 'DELETE' });
        renderCourier();
        orders = await apiCall('/admin/orders');
        renderOrder();
        showToast('Kurir berhasil dihapus');
      } catch (e) {
        showToast('Gagal menghapus kurir: ' + e.message, 'error');
      } finally {
        hideLoading();
      }
    };

    document.getElementById('btnAddCourier').onclick = async () => {
      const courierPhoneVal = courierPhone.value.trim().replace(/-/g, '');
      if (!courierName.value.trim() || !courierPhoneVal) return showToast('Nama dan No HP kurir wajib diisi', 'error');
      if (!isValidPhoneID(courierPhoneVal)) {
        return showToast('Format nomor HP kurir tidak valid (contoh: 081234567890)', 'error');
      }
      const data = { name: courierName.value.trim(), phone: courierPhoneVal };
      showLoading();
      try {
        couriers = await apiCall('/admin/couriers', { method: 'POST', body: data });
        renderCourier();
        courierName.value = ''; courierPhone.value = '';
        showToast('Kurir berhasil disimpan');
      } catch (e) {
        showToast('Gagal menyimpan kurir: ' + e.message, 'error');
      } finally {
        hideLoading();
      }
    };

    // === Order CRUD ===
    function waLink(phone, nomor, status, processStage, cost) {
      if (!phone) return null;
      let digits = String(phone).replace(/[^0-9]/g, '');
      if (digits.startsWith('0')) digits = '62' + digits.slice(1);
      else if (!digits.startsWith('62')) digits = '62' + digits;
      const statusLabel = status === 'proses' && processStage
        ? 'Proses (' + processStage + ')'
        : status.charAt(0).toUpperCase() + status.slice(1);
      const ongkir = cost > 0 ? 'Rp ' + Number(cost).toLocaleString() : 'Gratis Ongkir';
      const trackUrl = window.location.origin + '/?resi=' + encodeURIComponent(nomor);
      const msg = 'Halo, berikut update pesanan laundry Anda di OKE Laundry.\n\n'
        + 'No. Resi: ' + nomor + '\n'
        + 'Status: ' + statusLabel + '\n'
        + 'Ongkir: ' + ongkir + '\n\n'
        + 'Lacak pesanan Anda kapan saja melalui halaman tracking OKE Laundry:\n'
        + trackUrl + '\n\n'
        + 'Terima kasih.';
      return 'https://wa.me/' + digits + '?text=' + encodeURIComponent(msg);
    }

    function downloadCSV(filename, rows) {
      // BOM + CRLF agar Excel buka dengan benar (UTF-8 + delimiter ;)
      const escape = (v) => {
        const s = (v === null || v === undefined) ? '' : String(v);
        if (/["\n,;]/.test(s)) return '"' + s.replace(/"/g, '""') + '"';
        return s;
      };
      const csv = '\uFEFF' + rows.map(r => r.map(escape).join(';')).join('\r\n');
      const blob = new Blob([csv], { type: 'text/csv;charset=utf-8;' });
      const url = URL.createObjectURL(blob);
      const a = document.createElement('a');
      a.href = url;
      a.download = filename;
      document.body.appendChild(a);
      a.click();
      document.body.removeChild(a);
      URL.revokeObjectURL(url);
    }

    function getFilteredOrders() {
      const q = (document.getElementById('orderSearch')?.value || '').trim().toLowerCase();
      const statusFilter = document.getElementById('orderFilterStatus')?.value || '';
      const dateFilter = document.getElementById('orderFilterDate')?.value || '';
      let dateMatch = '';
      if (dateFilter) {
        const [y, m, d] = dateFilter.split('-');
        dateMatch = `${d}/${m}/${y}`;
      }
      return orders.filter(o => {
        if (q && !((o.nomor || '').toLowerCase().includes(q) || (o.nama || '').toLowerCase().includes(q))) return false;
        if (statusFilter && o.status !== statusFilter) return false;
        if (dateMatch && !(o.date || '').startsWith(dateMatch)) return false;
        return true;
      });
    }

    function exportOrders() {
      const data = getFilteredOrders();
      if (!data.length) return showToast('Tidak ada data order untuk diekspor', 'error');
      const rows = [['No Resi', 'Pelanggan', 'Telepon', 'Tanggal', 'Status', 'Tahap', 'Jarak (km)', 'Ongkir (Rp)', 'Kurir', 'Catatan']];
      data.forEach(o => {
        rows.push([
          o.nomor, o.nama, o.phone || '', o.date,
          o.status, o.processStage || '',
          o.jarak ?? '', o.cost ?? 0,
          o.courier_name || '', o.note || ''
        ]);
      });
      const fname = 'order_okelaundry_' + new Date().toISOString().slice(0, 10) + '.csv';
      downloadCSV(fname, rows);
      showToast('Export ' + data.length + ' order selesai');
    }

    function exportRekap() {
      if (!orders.length) return showToast('Belum ada data untuk direkap', 'error');
      const mapAgg = {};
      orders.forEach(o => {
        if (!mapAgg[o.nama]) mapAgg[o.nama] = { nama: o.nama, jumlah: 0, total: 0, jarakTotal: 0, last: '' };
        mapAgg[o.nama].jumlah += 1;
        mapAgg[o.nama].total += (o.cost || 0);
        mapAgg[o.nama].jarakTotal += (o.jarak || 0);
        if (!mapAgg[o.nama].last || o.date > mapAgg[o.nama].last) mapAgg[o.nama].last = o.date;
      });
      const list = Object.values(mapAgg);
      const rows = [['Customer', 'Jumlah Order', 'Total Ongkir (Rp)', 'Rata-rata Jarak (km)', 'Order Terakhir']];
      list.forEach(m => {
        rows.push([m.nama, m.jumlah, m.total, m.jumlah ? (m.jarakTotal / m.jumlah).toFixed(1) : 0, m.last || '']);
      });
      const fname = 'rekap_okelaundry_' + new Date().toISOString().slice(0, 10) + '.csv';
      downloadCSV(fname, rows);
      showToast('Export rekap selesai (' + list.length + ' pelanggan)');
    }

    function renderStats() {
      const todayStr = new Date().toLocaleDateString('id-ID');
      let today = 0, proses = 0, selesai = 0, ongkir = 0;
      orders.forEach(o => {
        if (o.status === 'proses') proses++;
        if (o.status === 'selesai') selesai++;
        ongkir += (o.cost || 0);
        const datePart = (o.date || '').split(',')[0].trim();
        if (datePart && datePart === todayStr) today++;
      });
      document.getElementById('statToday').textContent = today;
      document.getElementById('statTotal').textContent = orders.length;
      document.getElementById('statProses').textContent = proses;
      document.getElementById('statSelesai').textContent = selesai;
      document.getElementById('statOngkir').textContent = ongkir > 0 ? 'Rp ' + ongkir.toLocaleString('id-ID') : 'Rp 0';
    }

    function renderOrder() {
      orderList.innerHTML = '';
      const q = (document.getElementById('orderSearch')?.value || '').trim().toLowerCase();
      const statusFilter = document.getElementById('orderFilterStatus')?.value || '';
      const dateFilter = document.getElementById('orderFilterDate')?.value || ''; // YYYY-MM-DD

      // Format dateFilter ke dd/mm/yyyy untuk dicocokkan dengan o.date
      let dateMatch = '';
      if (dateFilter) {
        const [y, m, d] = dateFilter.split('-');
        dateMatch = `${d}/${m}/${y}`;
      }

      const filtered = orders.filter(o => {
        if (q && !((o.nomor || '').toLowerCase().includes(q) || (o.nama || '').toLowerCase().includes(q))) return false;
        if (statusFilter && o.status !== statusFilter) return false;
        if (dateMatch && !(o.date || '').startsWith(dateMatch)) return false;
        return true;
      });

      const counter = document.getElementById('orderCount');
      if (counter) counter.textContent = filtered.length === orders.length
        ? `${orders.length} order`
        : `${filtered.length} dari ${orders.length} order`;

      if (!filtered.length) {
        orderList.innerHTML = orders.length
          ? '<div class="meta-muted" style="text-align:center;padding:16px">Tidak ada order yang cocok dengan filter.</div>'
          : '<div class="meta-muted" style="text-align:center;padding:16px">Belum ada data order.</div>';
        renderStats();
        buildRekap();
        return;
      }
      filtered.forEach((o, i) => {
        const statusText = o.status === 'proses' && o.processStage ? `${o.status} • ${o.processStage}` : o.status;
        const cost = o.cost > 0 ? 'Rp ' + o.cost.toLocaleString() : 'Gratis Ongkir';
        const courierText = o.courier_name ? ` • Kurir: ${escapeHtml(o.courier_name)}` : '';
        const wa = waLink(o.phone, o.nomor, o.status, o.processStage, o.cost);
        const waBtn = wa
          ? `<a href="${wa}" target="_blank" rel="noopener" class="btn-wa" title="Kirim update status via WhatsApp">Kirim WA</a>`
          : `<button type="button" class="btn-wa disabled" title="Nomor HP pelanggan belum diisi" disabled>Kirim WA</button>`;
        const item = document.createElement('div');
        item.className = 'list-item';
        item.innerHTML = `<div style="display:flex;justify-content:space-between;gap:10px;align-items:start"><div><div style="font-weight:800">${escapeHtml(o.nomor)}</div><div class="meta-muted">${escapeHtml(o.nama)}</div><div class="meta-muted">${escapeHtml(statusText)} • ${cost} • ${o.jarak} km${courierText}</div></div><div style="display:grid;gap:8px;min-width:96px">${waBtn}<button type="button" onclick="editOrder(${o.id})">Edit</button><button type="button" onclick="delOrder(${o.id})">Hapus</button></div></div>`;
        orderList.appendChild(item);
      });
      renderStats();
      buildRekap();
    }

    window.editOrder = function(id) {
      const o = orders.find(x => x.id === id);
      if (!o) return;
      orderNomor.value = o.nomor;
      orderCust.value = o.customer_id;
      if (orderCourier) orderCourier.value = o.courier_id || '';
      orderStatus.value = o.status;
      orderNote.value = o.note || '';
      orderJarak.value = o.jarak ? o.jarak + ' km' : '';
      currentProcessStage = o.processStage || null;
      syncProcessStageUI();
      orderNomor.focus();
      // Switch to order tab
      activateAdminTab('order');
    };

    window.delOrder = async function(id) {
      if (!confirm('Hapus order ini?')) return;
      showLoading();
      try {
        orders = await apiCall(`/admin/orders/${id}`, { method: 'DELETE' });
        renderOrder();
        showToast('Order berhasil dihapus');
      } catch (e) {
        showToast('Gagal menghapus order: ' + e.message, 'error');
      } finally {
        hideLoading();
      }
    };

    function syncProcessStageUI() {
      const show = orderStatus.value === 'proses';
      processStageContainer.classList.toggle('hidden', !show);
      stageButtons.forEach(btn => btn.classList.toggle('active', btn.dataset.stage === currentProcessStage && show));
    }

    orderStatus.addEventListener('change', () => { if (orderStatus.value !== 'proses') currentProcessStage = null; syncProcessStageUI(); });

    stageButtons.forEach(btn => {
      btn.addEventListener('click', () => {
        const stage = btn.dataset.stage;
        if (stage === 'ditolak') { orderStatus.value = 'ditolak'; currentProcessStage = null; syncProcessStageUI(); return; }
        orderStatus.value = 'proses'; currentProcessStage = stage; syncProcessStageUI();
      });
    });

    document.getElementById('btnAddOrder').onclick = async () => {
      if (!orderNomor.value.trim() || orderCust.value === '') return showToast('Lengkapi nomor order dan customer', 'error');

      const payload = {
        receipt_number: orderNomor.value.trim(),
        customer_id: orderCust.value,
        status_name: orderStatus.value,
        note: orderNote.value.trim(),
        process_stage: orderStatus.value === 'proses' ? currentProcessStage : null,
        distance_input: orderJarak.value ? parseFloat(orderJarak.value) : null,
        courier_id: orderCourier ? (orderCourier.value || null) : null
      };

      showLoading();
      try {
        orders = await apiCall('/admin/orders', { method: 'POST', body: payload });
        renderOrder();
        orderNomor.value = ''; orderNote.value = ''; orderJarak.value = ''; currentProcessStage = null; orderStatus.value = 'jemput'; if (orderCourier) orderCourier.value = ''; syncProcessStageUI();
        if (adminMarker) { adminMap.removeLayer(adminMarker); adminMarker = null; }
        lastPos = null;
        showToast('Order berhasil disimpan');
      } catch (e) {
        showToast('Gagal menyimpan order: ' + e.message, 'error');
      } finally {
        hideLoading();
      }
    };

    // === Generate Receipt Number ===
    document.getElementById('btnGenResi').onclick = async () => {
      showLoading();
      try {
        const data = await apiCall('/admin/orders/generate-receipt');
        orderNomor.value = data.receipt_number;
        showToast('Nomor resi dibuat: ' + data.receipt_number);
      } catch (e) {
        showToast('Gagal membuat nomor resi: ' + e.message, 'error');
      } finally {
        hideLoading();
      }
    };

    // === Banner CRUD ===
    let banners = [];
    const bannerList = document.getElementById('bannerList');

    async function loadBanners() {
      try {
        banners = await apiCall('/admin/banners');
        renderBanners();
      } catch (e) {
        showToast('Gagal memuat banner: ' + e.message, 'error');
      }
    }

    function renderBanners() {
      if (!bannerList) return;
      bannerList.innerHTML = '';
      if (!banners.length) {
        bannerList.innerHTML = '<div class="meta-muted" style="text-align:center;padding:16px">Belum ada banner. Halaman pelanggan akan menampilkan banner default.</div>';
        return;
      }
      banners.forEach(b => {
        const item = document.createElement('div');
        item.className = 'list-item';
        item.style.display = 'flex';
        item.style.gap = '14px';
        item.style.alignItems = 'center';
        const safeTitle = escapeHtml(b.title || '');
        const safeCaption = escapeHtml(b.caption || '');
        item.innerHTML = `
          <img src="${b.url}" alt="${safeTitle}" style="width:140px;height:80px;object-fit:cover;border-radius:10px;border:1px solid var(--line);background:#f4efec;flex:none" />
          <div style="flex:1;min-width:0">
            <div style="font-weight:800">${safeTitle || '<span class="meta-muted" style="font-weight:600">(Tanpa judul)</span>'}</div>
            <div class="meta-muted" style="margin-top:2px">${safeCaption || '<i>tanpa caption</i>'}</div>
            <div class="meta-muted" style="margin-top:2px">Urutan: ${b.sort_order} • Diunggah ${new Date(b.created_at).toLocaleDateString('id-ID')}</div>
          </div>
          <button type="button" onclick="delBanner(${b.id})" class="btn-muted" style="padding:8px 14px;font-size:13px">Hapus</button>
        `;
        bannerList.appendChild(item);
      });
    }

    window.delBanner = async function(id) {
      if (!confirm('Hapus banner ini?')) return;
      showLoading();
      try {
        banners = await apiCall(`/admin/banners/${id}`, { method: 'DELETE' });
        renderBanners();
        showToast('Banner berhasil dihapus');
      } catch (e) {
        showToast('Gagal menghapus banner: ' + e.message, 'error');
      } finally {
        hideLoading();
      }
    };

    // === Cropper banner (rasio 3:1) ===
    let bannerCropper = null;
    const bannerImageInput = document.getElementById('bannerImage');
    const cropWrap = document.getElementById('cropWrap');
    const cropImage = document.getElementById('cropImage');

    bannerImageInput?.addEventListener('change', () => {
      const file = bannerImageInput.files[0];
      if (bannerCropper) { bannerCropper.destroy(); bannerCropper = null; }
      if (!file) { cropWrap.style.display = 'none'; return; }
      if (file.size > 2 * 1024 * 1024) { showToast('Ukuran file maksimal 2 MB', 'error'); bannerImageInput.value = ''; cropWrap.style.display = 'none'; return; }
      const reader = new FileReader();
      reader.onload = (ev) => {
        cropImage.src = ev.target.result;
        cropWrap.style.display = 'block';
        bannerCropper = new Cropper(cropImage, {
          aspectRatio: 3,
          viewMode: 1,
          dragMode: 'move',
          autoCropArea: 1,
          background: false,
          responsive: true,
        });
      };
      reader.readAsDataURL(file);
    });

    document.getElementById('cropZoomIn')?.addEventListener('click', () => bannerCropper && bannerCropper.zoom(0.1));
    document.getElementById('cropZoomOut')?.addEventListener('click', () => bannerCropper && bannerCropper.zoom(-0.1));
    document.getElementById('cropReset')?.addEventListener('click', () => bannerCropper && bannerCropper.reset());

    function getCroppedBlob() {
      return new Promise((resolve) => {
        if (!bannerCropper) return resolve(null);
        bannerCropper.getCroppedCanvas({
          width: 1200, height: 400,
          imageSmoothingQuality: 'high',
          fillColor: '#0c2433',
        }).toBlob((blob) => resolve(blob), 'image/jpeg', 0.9);
      });
    }

    document.getElementById('bannerForm')?.addEventListener('submit', async (e) => {
      e.preventDefault();
      const file = bannerImageInput.files[0];
      if (!file) return showToast('Pilih file gambar terlebih dahulu', 'error');

      const fd = new FormData();
      const cropped = await getCroppedBlob();
      if (cropped) {
        fd.append('image', cropped, 'banner.jpg');
      } else {
        if (file.size > 2 * 1024 * 1024) return showToast('Ukuran file maksimal 2 MB', 'error');
        fd.append('image', file);
      }
      fd.append('title', document.getElementById('bannerTitle').value || '');
      fd.append('caption', document.getElementById('bannerCaption').value || '');
      fd.append('sort_order', document.getElementById('bannerSort').value || '0');

      showLoading();
      try {
        const res = await fetch('/admin/banners', {
          method: 'POST',
          headers: { 'X-CSRF-TOKEN': csrfToken, 'Accept': 'application/json' },
          body: fd,
        });
        if (!res.ok) {
          const data = await res.json().catch(() => ({}));
          const msg = data.errors ? Object.values(data.errors).flat().join(', ') : (data.message || 'Gagal mengunggah banner');
          throw new Error(msg);
        }
        banners = await res.json();
        renderBanners();
        e.target.reset();
        if (bannerCropper) { bannerCropper.destroy(); bannerCropper = null; }
        cropWrap.style.display = 'none';
        showToast('Banner berhasil diunggah');
      } catch (err) {
        showToast('Gagal mengunggah: ' + err.message, 'error');
      } finally {
        hideLoading();
      }
    });

    // === Branding settings ===
    const logoPreview = document.getElementById('logoPreview');
    const faviconPreview = document.getElementById('faviconPreview');

    function applyBrandingPreview(s) {
      if (s.logo_url) {
        logoPreview.innerHTML = `<img src="${s.logo_url}" alt="Logo" style="max-width:100%;max-height:100%;object-fit:contain"/>`;
      } else {
        logoPreview.textContent = 'OKELaundry';
      }
      if (s.favicon_url) {
        faviconPreview.innerHTML = `<img src="${s.favicon_url}" alt="Favicon" style="max-width:100%;max-height:100%;object-fit:contain"/>`;
      } else {
        faviconPreview.textContent = 'OL';
      }
      // Update favicon di tab admin sekarang juga
      let link = document.querySelector('link[rel="icon"]');
      if (s.favicon_url) {
        if (!link) { link = document.createElement('link'); link.rel = 'icon'; document.head.appendChild(link); }
        link.href = s.favicon_url + '?v=' + Date.now();
      } else if (link) {
        link.remove();
      }
    }

    async function loadBranding() {
      try {
        const s = await apiCall('/admin/settings');
        applyBrandingPreview(s);
      } catch (e) { /* abaikan */ }
    }

    async function uploadBranding(formData, url) {
      showLoading();
      try {
        const res = await fetch(url, {
          method: 'POST',
          headers: { 'X-CSRF-TOKEN': csrfToken, 'Accept': 'application/json' },
          body: formData,
        });
        if (!res.ok) {
          const data = await res.json().catch(() => ({}));
          const msg = data.errors ? Object.values(data.errors).flat().join(', ') : (data.message || 'Gagal mengunggah');
          throw new Error(msg);
        }
        const s = await res.json();
        applyBrandingPreview(s);
        showToast('Berhasil disimpan');
      } catch (err) {
        showToast(err.message, 'error');
      } finally {
        hideLoading();
      }
    }

    document.getElementById('logoForm')?.addEventListener('submit', (e) => {
      e.preventDefault();
      const f = document.getElementById('logoFile').files[0];
      if (!f) return showToast('Pilih file logo terlebih dahulu', 'error');
      const fd = new FormData(); fd.append('image', f);
      uploadBranding(fd, '/admin/settings/logo').then(() => e.target.reset());
    });

    document.getElementById('faviconForm')?.addEventListener('submit', (e) => {
      e.preventDefault();
      const f = document.getElementById('faviconFile').files[0];
      if (!f) return showToast('Pilih file favicon terlebih dahulu', 'error');
      const fd = new FormData(); fd.append('image', f);
      uploadBranding(fd, '/admin/settings/favicon').then(() => e.target.reset());
    });

    document.getElementById('btnDeleteLogo')?.addEventListener('click', async () => {
      if (!confirm('Hapus logo dan kembali ke default?')) return;
      try {
        const s = await apiCall('/admin/settings/logo', { method: 'DELETE' });
        applyBrandingPreview(s);
        showToast('Logo dihapus');
      } catch (e) { showToast('Gagal: ' + e.message, 'error'); }
    });

    document.getElementById('btnDeleteFavicon')?.addEventListener('click', async () => {
      if (!confirm('Hapus favicon dan kembali ke default?')) return;
      try {
        const s = await apiCall('/admin/settings/favicon', { method: 'DELETE' });
        applyBrandingPreview(s);
        showToast('Favicon dihapus');
      } catch (e) { showToast('Gagal: ' + e.message, 'error'); }
    });

    // === Profil Admin ===
    async function loadMitraForm() {
      try {
        const m = await apiCall('/admin/settings/mitra');
        const set = (id, v) => { const el = document.getElementById(id); if (el) el.value = v ?? ''; };
        set('mitraName', m.name); set('mitraPhone', m.phone); set('mitraAddress', m.address);
        set('mitraHours', m.hours); set('mitraLat', m.lat); set('mitraLng', m.lng);
        set('mitraCost', m.cost_per_km); set('mitraRadius', m.free_radius_km);
      } catch (_) { /* abaikan */ }
    }

    document.getElementById('mitraForm')?.addEventListener('submit', async (e) => {
      e.preventDefault();
      const payload = {
        name: document.getElementById('mitraName').value.trim(),
        phone: document.getElementById('mitraPhone').value.trim().replace(/-/g, ''),
        address: document.getElementById('mitraAddress').value.trim(),
        hours: document.getElementById('mitraHours').value.trim(),
        lat: document.getElementById('mitraLat').value,
        lng: document.getElementById('mitraLng').value,
        cost_per_km: document.getElementById('mitraCost').value,
        free_radius_km: document.getElementById('mitraRadius').value,
      };
      showLoading();
      try {
        const m = await apiCall('/admin/settings/mitra', { method: 'PUT', body: payload });
        // Update state + peta tanpa reload
        mitra = { lat: parseFloat(m.lat), lng: parseFloat(m.lng), cost_per_km: parseInt(m.cost_per_km,10)||0, free_radius_km: parseFloat(m.free_radius_km)||0 };
        mitraMarker.setLatLng([mitra.lat, mitra.lng]).bindPopup(m.name || 'OKE Laundry (Mitra)');
        mitraCircle.setLatLng([mitra.lat, mitra.lng]).setRadius(mitra.free_radius_km * 1000);
        showToast('Informasi mitra disimpan');
      } catch (err) {
        showToast('Gagal: ' + err.message, 'error');
      } finally {
        hideLoading();
      }
    });

    async function loadProfile() {
      try {
        const p = await apiCall('/admin/profile');
        const fName = document.getElementById('profName');
        const fEmail = document.getElementById('profEmail');
        if (fName) fName.value = p.name || '';
        if (fEmail) fEmail.value = p.email || '';
      } catch (_) { /* abaikan */ }
    }

    document.getElementById('profileForm')?.addEventListener('submit', async (e) => {
      e.preventDefault();
      const name = document.getElementById('profName').value.trim();
      const email = document.getElementById('profEmail').value.trim();
      if (!name || !email) return showToast('Nama dan email wajib diisi', 'error');
      showLoading();
      try {
        await apiCall('/admin/profile', {
          method: 'PUT',
          body: { name, email },
        });
        // Update sambutan di top bar
        const greet = document.querySelector('.admin-bar-meta span');
        if (greet) greet.textContent = 'Selamat datang, ' + name;
        showToast('Profil berhasil diperbarui');
      } catch (err) {
        showToast('Gagal: ' + err.message, 'error');
      } finally {
        hideLoading();
      }
    });

    document.getElementById('passwordForm')?.addEventListener('submit', async (e) => {
      e.preventDefault();
      const cur = document.getElementById('profCurrentPwd').value;
      const pwd = document.getElementById('profNewPwd').value;
      const conf = document.getElementById('profNewPwdConfirm').value;
      if (pwd !== conf) return showToast('Konfirmasi password tidak cocok', 'error');
      if (pwd.length < 8) return showToast('Password baru minimal 8 karakter', 'error');
      showLoading();
      try {
        await apiCall('/admin/profile/password', {
          method: 'PUT',
          body: { current_password: cur, password: pwd, password_confirmation: conf },
        });
        e.target.reset();
        showToast('Password berhasil diubah');
      } catch (err) {
        showToast('Gagal: ' + err.message, 'error');
      } finally {
        hideLoading();
      }
    });

    // === Tab Navigation ===
    const tabs = {
      data: document.getElementById('panelDataCustomer'),
      order: document.getElementById('panelKelolaOrder'),
      rekap: document.getElementById('panelRekap'),
      settings: document.getElementById('panelSettings'),
      courier: document.getElementById('panelCourier')
    };
    const subTabNavs = Array.from(document.querySelectorAll('.sub-tab-nav'));

    function activateAdminTab(key) {
      Object.entries(tabs).forEach(([name, panel]) => panel.classList.toggle('active', name === key));
      subTabNavs.forEach(btn => btn.classList.toggle('active', btn.dataset.target === key));
      if (key === 'data') setTimeout(() => adminMap.invalidateSize(), 100);
      if (key === 'rekap') buildRekap();
    }

    subTabNavs.forEach(btn => {
      btn.addEventListener('click', () => activateAdminTab(btn.dataset.target));
    });

    // === Rekap ===
    function buildRekap() {
      const tbody = document.querySelector('#rekapTable tbody'), summary = document.getElementById('rekapSummary');
      tbody.innerHTML = '';
      const mapAgg = {}, statusCount = { jemput:0, proses:0, antar:0, selesai:0, ditolak:0 };

      orders.forEach(o => {
        if (!mapAgg[o.nama]) mapAgg[o.nama] = { nama: o.nama, jumlah: 0, total: 0, jarakTotal: 0, last: null, lastIso: null, lastId: null };
        mapAgg[o.nama].jumlah += 1;
        mapAgg[o.nama].total += (o.cost || 0);
        mapAgg[o.nama].jarakTotal += (o.jarak || 0);

        const parts = o.date.split(', ');
        const dateParts = parts[0].split('/');
        const isoDate = `${dateParts[2]}-${dateParts[1]}-${dateParts[0]}T${(parts[1] || '00:00:00').replace(/\./g, ':')}`;

        if (!mapAgg[o.nama].lastIso || new Date(isoDate) > new Date(mapAgg[o.nama].lastIso)) {
          mapAgg[o.nama].last = o.date;
          mapAgg[o.nama].lastIso = isoDate;
          mapAgg[o.nama].lastId = o.id;
        }
        if (statusCount[o.status] !== undefined) statusCount[o.status] += 1;
      });

      const list = Object.values(mapAgg);
      if (!list.length) {
        tbody.innerHTML = '<tr><td colspan="6">Belum ada data order.</td></tr>';
        summary.innerHTML = 'Belum ada data untuk direkap.';
        return;
      }
      let totalOrders = 0, totalOngkir = 0, totalJarak = 0;
      list.forEach(m => {
        const tr = document.createElement('tr'), rata = m.jumlah ? (m.jarakTotal / m.jumlah).toFixed(1) : '-';
        const strukBtn = m.lastId
          ? `<a href="/admin/orders/${m.lastId}/receipt" target="_blank" rel="noopener" class="btn-print" style="display:inline-block;padding:7px 12px;font-size:12px">Cetak Struk</a>`
          : '-';
        tr.innerHTML = `<td>${escapeHtml(m.nama)}</td><td>${m.jumlah}</td><td>${m.total > 0 ? 'Rp ' + m.total.toLocaleString() : 'Gratis Ongkir'}</td><td>${rata} km</td><td>${escapeHtml(m.last) || '-'}</td><td>${strukBtn}</td>`;
        tbody.appendChild(tr);
        totalOrders += m.jumlah; totalOngkir += m.total; totalJarak += m.jarakTotal;
      });
      summary.innerHTML = `<div>Total Semua Customer: <b>${totalOrders}</b> order</div><div>Total Ongkir: <b>${totalOngkir > 0 ? 'Rp ' + totalOngkir.toLocaleString() : 'Gratis Ongkir'}</b></div><div>Total Jarak Keseluruhan: <b>${totalJarak.toFixed(1)} km</b></div><div>Customer Aktif: <b>${list.length}</b> pelanggan</div><div style="margin-top:6px">Rekap Status: Jemput <b>${statusCount.jemput}</b> • Proses <b>${statusCount.proses}</b> • Antar <b>${statusCount.antar}</b> • Selesai <b>${statusCount.selesai}</b> • Ditolak <b>${statusCount.ditolak}</b></div>`;
    }

    // === Init ===
    ['orderSearch','orderFilterStatus','orderFilterDate'].forEach(id => {
      const el = document.getElementById(id);
      if (el) el.addEventListener('input', renderOrder);
    });
    document.getElementById('btnExportOrder')?.addEventListener('click', exportOrders);
    document.getElementById('btnExportRekap')?.addEventListener('click', exportRekap);
    loadData();
    loadBanners();
    loadBranding();
    loadProfile();
    loadMitra();
    loadMitraForm();
    attachPhoneFormatter(custPhone);
    attachPhoneFormatter(courierPhone);
    attachPhoneFormatter(document.getElementById('mitraPhone'));
  </script>
</body>
</html>
