PT MEDAN VACATIONS TRAVEL — Daftar Booking
:root{–ink:#122425;–muted:#6c7a7a;–line:#dce7e5;–brand:#0b5d5a;–brand-dark:#074441;–accent:#d99b38;–paper:#fff;–canvas:#f3f7f6;–danger:#b42318;–shadow:0 14px 40px rgba(10,43,41,.09)}*{box-sizing:border-box}body{margin:0;color:var(–ink);background:var(–canvas);font:15px/1.5 Inter,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,”Segoe UI”,sans-serif}button,input,select{font:inherit}button{cursor:pointer}.topbar{min-height:72px;display:flex;align-items:center;justify-content:space-between;gap:20px;padding:13px clamp(20px,5vw,72px);background:var(–paper);border-bottom:1px solid var(–line)}.brand{display:flex;align-items:center;gap:12px;color:var(–ink);text-decoration:none}.brand-mark{width:42px;height:42px;display:grid;place-items:center;border-radius:13px;color:#fff;background:linear-gradient(135deg,var(–brand),#13807a);font-weight:800;letter-spacing:-1px;box-shadow:0 7px 16px rgba(11,93,90,.23)}.brand strong{display:block;font-size:15px;letter-spacing:.08em}.brand small{display:block;margin-top:1px;color:var(–muted);font-size:11px;letter-spacing:.04em}.top-actions{display:flex;align-items:center;gap:15px}.nav-link{color:var(–brand);font-size:13px;font-weight:750;text-decoration:none}.nav-link:hover{text-decoration:underline}.status{display:flex;align-items:center;gap:8px;color:var(–muted);font-size:13px}.status i{width:8px;height:8px;border-radius:50%;background:#20a879;box-shadow:0 0 0 4px #e2f5ed}main{max-width:1440px;margin:0 auto;padding:36px clamp(20px,5vw,72px) 60px}.intro{display:flex;justify-content:space-between;align-items:end;gap:24px;margin-bottom:26px}.eyebrow{margin:0 0 7px;color:var(–brand);font-size:12px;font-weight:800;letter-spacing:.13em}h1{margin:0;font-size:clamp(27px,4vw,38px);letter-spacing:-.045em;line-height:1.12}.intro p{max-width:620px;margin:9px 0 0;color:var(–muted)}.today{padding:9px 13px;white-space:nowrap;border:1px solid var(–line);border-radius:10px;background:#fff;color:var(–muted);font-size:13px}.dashboard{display:grid;grid-template-columns:repeat(3,1fr);gap:16px;margin-bottom:26px}.stat{min-height:122px;position:relative;overflow:hidden;padding:20px;border:1px solid var(–line);border-radius:16px;background:#fff;box-shadow:0 3px 12px rgba(13,58,55,.025)}.stat:after{content:””;position:absolute;width:84px;height:84px;border-radius:50%;right:-26px;bottom:-35px;background:#e6f3f1}.stat:nth-child(2):after{background:#fff3df}.stat:nth-child(3):after{background:#eaf0ff}.stat-label{position:relative;z-index:1;color:var(–muted);font-size:13px;font-weight:650}.stat-value{position:relative;z-index:1;margin-top:7px;font-size:35px;font-weight:780;letter-spacing:-.06em}.stat-note{position:relative;z-index:1;color:var(–brand);font-size:12px;font-weight:700}.layout{display:grid;grid-template-columns:minmax(315px,.83fr) minmax(0,1.75fr);align-items:start;gap:24px}.card{border:1px solid var(–line);border-radius:17px;background:var(–paper);box-shadow:var(–shadow)}.form-card{position:sticky;top:18px;padding:25px}.card-head{display:flex;align-items:center;justify-content:space-between;gap:12px;margin-bottom:21px}.card-head h2{margin:0;font-size:18px;letter-spacing:-.025em}.card-head span{padding:4px 9px;border-radius:99px;background:#e9f5f3;color:var(–brand);font-size:11px;font-weight:800}.form-grid{display:grid;gap:14px}.field{display:grid;gap:6px}.field label{color:#485756;font-size:12px;font-weight:750}.field input,.field select{width:100%;padding:10px 12px;outline:none;border:1px solid #cbdad7;border-radius:9px;color:var(–ink);background:#fff;transition:border .15s,box-shadow .15s}.field input:focus,.field select:focus{border-color:var(–brand);box-shadow:0 0 0 3px rgba(11,93,90,.12)}.field input:disabled{color:#8a9695;background:#f4f7f6}.required{color:var(–danger)}.date-grid,.guest-grid{display:grid;grid-template-columns:1fr 1fr;gap:11px}.guest-grid{grid-template-columns:repeat(3,1fr)}.guest-grid input{text-align:center}.guest-grid label{text-align:center}.form-actions{display:flex;gap:10px;padding-top:4px}.btn{border:0;border-radius:9px;padding:10px 14px;font-weight:750;transition:transform .15s,background .15s,box-shadow .15s}.btn:hover{transform:translateY(-1px)}.btn-primary{flex:1;color:#fff;background:var(–brand);box-shadow:0 6px 14px rgba(11,93,90,.2)}.btn-primary:hover{background:var(–brand-dark)}.btn-secondary{color:#4d5a59;background:#edf3f2}.info{margin:0;padding:10px 11px;border-radius:9px;color:#48605d;background:#f1f6f5;font-size:11.5px}.info strong{color:var(–brand)}.table-card{overflow:hidden}.table-top{display:flex;align-items:center;justify-content:space-between;gap:15px;padding:21px 23px 16px;border-bottom:1px solid var(–line)}.table-top h2{margin:0;font-size:18px;letter-spacing:-.025em}.table-top small{color:var(–muted)}.search{display:flex;align-items:center;gap:7px;width:245px;padding:8px 10px;border:1px solid #cbdad7;border-radius:9px;color:var(–muted)}.search input{width:100%;padding:0;outline:none;border:0;background:transparent;font-size:13px}.filter-row{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:14px 23px;background:#fbfdfd;border-bottom:1px solid var(–line)}.filter-meta{color:var(–muted);font-size:12px}.table-wrap{overflow-x:auto}.booking-table{width:100%;min-width:790px;border-collapse:collapse;text-align:left}.booking-table th{padding:12px 16px;color:#728080;background:#fff;font-size:11px;font-weight:800;letter-spacing:.055em;text-transform:uppercase;white-space:nowrap}.booking-table td{padding:15px 16px;border-top:1px solid #edf1f0;vertical-align:middle;font-size:13px}.booking-table tbody tr:hover{background:#fbfdfd}.num{color:var(–brand);font-weight:800}.agent{font-weight:750}.sub{display:block;max-width:200px;overflow:hidden;color:var(–muted);font-size:11.5px;white-space:nowrap;text-overflow:ellipsis}.package{color:#334443;font-weight:650}.date-range{white-space:nowrap}.counts{display:flex;gap:5px}.count{padding:3px 6px;border-radius:5px;background:#f0f5f4;color:#526260;font-size:10px;font-weight:750;white-space:nowrap}.actions{display:flex;gap:2px}.icon-btn{display:grid;width:29px;height:29px;place-items:center;border:0;border-radius:7px;background:transparent;color:#57706e}.icon-btn:hover{background:#e8f3f1;color:var(–brand)}.icon-btn.delete:hover{color:var(–danger);background:#fff0ef}.empty{padding:50px 20px;color:var(–muted);text-align:center}.empty strong{display:block;margin-bottom:3px;color:#485756}.toast{position:fixed;z-index:9;right:22px;bottom:22px;display:flex;align-items:center;gap:9px;max-width:calc(100vw – 44px);padding:12px 15px;border:1px solid #bce7d8;border-radius:11px;color:#135e48;background:#effbf5;box-shadow:0 15px 30px rgba(18,47,40,.16);font-size:13px;font-weight:650;transform:translateY(90px);opacity:0;transition:.25s ease}.toast.show{transform:translateY(0);opacity:1}.toast i{display:grid;width:18px;height:18px;place-items:center;border-radius:50%;color:#fff;background:#1c9b72;font-size:12px;font-style:normal}@media(max-width:1050px){.layout{grid-template-columns:1fr}.form-card{position:static}.form-grid{grid-template-columns:repeat(2,1fr)}.form-grid .field:first-child,.form-grid .field:nth-child(2),.form-grid .info{grid-column:1/-1}}@media(max-width:680px){.topbar{min-height:64px;padding:11px 18px}.status{display:none}main{padding:27px 16px 40px}.intro{align-items:start;flex-direction:column;gap:12px}.today{padding:7px 10px}.dashboard{gap:10px}.stat{min-height:105px;padding:15px;border-radius:13px}.stat-value{font-size:28px}.stat-note{display:none}.layout{gap:16px}.form-card{padding:19px}.form-grid{grid-template-columns:1fr}.form-grid .field:first-child,.form-grid .field:nth-child(2),.form-grid .info{grid-column:auto}.table-top{align-items:start;flex-direction:column;padding:18px 18px 13px}.search{width:100%}.filter-row{padding:12px 18px}.booking-table th,.booking-table td{padding-left:13px;padding-right:13px}.top-actions{gap:0}.nav-link{font-size:12px}.date-grid{grid-template-columns:1fr}.guest-grid{gap:7px}}
.field textarea{width:100%;min-height:76px;padding:10px 12px;outline:none;border:1px solid #cbdad7;border-radius:9px;color:var(–ink);background:#fff;resize:vertical}.field textarea:focus{border-color:var(–brand);box-shadow:0 0 0 3px rgba(11,93,90,.12)}.room-grid,.meal-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:8px}.room-grid input,.meal-grid input{text-align:center}.room-grid label,.meal-grid label{text-align:center}.booking-table{min-width:1120px}.partner-add{display:flex;justify-content:flex-end}.partner-drafts{display:grid;gap:10px}.partner-draft{display:grid;grid-template-columns:1fr 1fr;gap:9px;padding:11px;border:1px solid #d9e9e6;border-radius:9px;background:#f6faf9}.partner-draft .partner-wide{grid-column:1/-1}.partner-draft label{display:block;margin-bottom:4px;color:#51625f;font-size:10px;font-weight:750}.partner-draft select,.partner-draft input,.partner-draft textarea{width:100%;padding:8px;outline:none;border:1px solid #cbdad7;border-radius:7px;background:#fff;font-size:12px}.partner-draft textarea{min-height:58px;resize:vertical}.partner-draft select:focus,.partner-draft input:focus,.partner-draft textarea:focus{border-color:var(–brand);box-shadow:0 0 0 3px rgba(11,93,90,.1)}.partner-quantities{display:grid;grid-template-columns:repeat(3,1fr);gap:6px}.partner-draft button{height:33px;border:0;border-radius:7px;color:var(–danger);background:#fff0ef;font-size:11px;font-weight:750;padding:4px 8px}.partner-cell{margin-bottom:7px}.partner-cell:last-child{margin-bottom:0}.partner-cell b{display:block;font-size:12px}.partner-cell span{display:block;color:var(–muted);font-size:11px}@media(max-width:680px){.room-grid,.meal-grid{gap:6px}.partner-draft{grid-template-columns:1fr}.partner-draft .partner-wide{grid-column:auto}.partner-draft button{width:100%}}
.brand-logo{width:48px;height:48px;object-fit:contain;border-radius:50%;background:#fff;flex:none}
DASHBOARD
Daftar Booking
Catat jadwal perjalanan, paket wisata, serta komposisi jumlah tamu setiap Travel Agent.
Total Booking
0
Booking tercatat
Total Peserta
0
Adult, Kid & Infant
Agent Aktif
0
Memiliki booking
Jadwal Booking
Gunakan pencarian untuk menemukan agent atau paket.
Menampilkan 0 data
| No. | Nama Agent | Nama Tamu | Paket Wisata | Perjalanan | Jumlah Tamu | Rincian Layanan Voucher | Aksi |
|---|
Belum ada booking yang cocokTambahkan booking baru atau ubah pencarian Anda.
✓Booking tersimpan
(() => {
const GUEST_KEY = ‘enos-tour-medan-guest-list-v1’, BOOKING_KEY = ‘enos-tour-medan-booking-list-v1′;
const initialGuestData = [
{agent:’Nusantara Holiday’}, {agent:’Wonderful Travel’}, {agent:’Lion City Getaways’}, {agent:’Sawasdee Tours’}
];
const initialPartnerData = [
{name:’Grand Mercure Medan’,category:’Hotel’}, {name:’Restoran Tip Top’,category:’Restaurant’}, {name:’Samosir Boat Service’,category:’Lainnya’,customType:’Transportasi’}
];
const sampleBookings = [
{id:’b1′,no:1,agent:’Nusantara Holiday’,guestName:’Budi Santoso’,package:’Danau Toba & Samosir’,start:’2026-09-18′,end:’2026-09-20′,adult:4,kid:1,infant:0,partner:’Grand Mercure Medan — Hotel’,partnerItems:[{id:’bp1′,partner:’Grand Mercure Medan — Hotel’,checkIn:’2026-09-18′,checkOut:’2026-09-20′}],single:1,twinKing:2,extraBed:1,breakfast:5,lunch:5,dinner:5,description:’Early check-in bila tersedia.’},
{id:’b2′,no:2,agent:’Wonderful Travel’,guestName:’Siti Nur Aisyah’,package:’Medan – Berastagi – Danau Toba’,start:’2026-09-25′,end:’2026-09-28′,adult:8,kid:2,infant:1,partner:’Restoran Tip Top — Restaurant’,partnerItems:[{id:’bp2′,partner:’Grand Mercure Medan — Hotel’,checkIn:’2026-09-25′,checkOut:’2026-09-28′},{id:’bp3′,partner:’Restoran Tip Top — Restaurant’,checkIn:’2026-09-26′,checkOut:’2026-09-26′}],single:0,twinKing:4,extraBed:2,breakfast:10,lunch:10,dinner:10,description:’Menu halal, tanpa makanan laut.’}
];
const $ = id => document.getElementById(id), form = $(‘bookingForm’), rows = $(‘bookingRows’), search = $(‘searchInput’);
let bookings = loadBookings();
function safeParse(key, fallback) { try { const data = JSON.parse(localStorage.getItem(key)); return Array.isArray(data) ? data : fallback; } catch { return fallback; } }
function esc(v) { return String(v ?? ”).replace(/[&”‘]/g,c=>({‘&’:’&’,”:’>’,'”‘:’"’,”‘”:’'’}[c])); }
function save() { localStorage.setItem(BOOKING_KEY, JSON.stringify(bookings)); }
function toast(message) { $(‘toastText’).textContent=message; $(‘toast’).classList.add(‘show’); clearTimeout(toast.timer); toast.timer=setTimeout(()=>$(‘toast’).classList.remove(‘show’),2900); }
function fmtDate(value) { return value?new Intl.DateTimeFormat(‘id-ID’,{day:’2-digit’,month:’short’,year:’numeric’}).format(new Date(value+’T00:00:00′)):’—’; }
function nights(b) { return Math.max(0,Math.round((new Date(b.end+’T00:00:00′)-new Date(b.start+’T00:00:00′))/86400000)); }
function loadBookings() { return safeParse(BOOKING_KEY, sampleBookings); }
function agents() { return […new Set(safeParse(GUEST_KEY, initialGuestData).map(g => String(g.agent || ”).trim()).filter(Boolean))].sort((a,b) => a.localeCompare(b,’id’)); }
function refreshAgents() { const selected=$(‘agent’).value, list=agents(); $(‘agent’).innerHTML=’Pilih nama agent’+list.map(name=>`${esc(name)}`).join(”); $(‘agent’).value=list.includes(selected)?selected:”; $(‘agentInfo’).innerHTML=list.length?`
${list.length} agent tersedia. Daftar otomatis diambil dari halaman Daftar Agent.`:’
Belum ada agent. Tambahkan agent terlebih dahulu di halaman Daftar Agent.’; $(‘submitBtn’).disabled=!list.length; }
function itemsFor(b) { const raw=Array.isArray(b.partnerItems) && b.partnerItems.length ? b.partnerItems : (b.partner ? [{id:’legacy-‘+b.id,partner:b.partner,checkIn:b.start,checkOut:b.end}] : []); return raw.map(p=>({…p,single:Number(p.single??b.single??0),twinKing:Number(p.twinKing??b.twinKing??0),extraBed:Number(p.extraBed??b.extraBed??0),breakfast:Number(p.breakfast??b.breakfast??0),lunch:Number(p.lunch??b.lunch??0),dinner:Number(p.dinner??b.dinner??0),description:p.description??b.description??”})); }
function filtered() { const q=search.value.trim().toLowerCase(); return bookings.filter(b => !q || [b.no,b.agent,b.guestName,b.package,b.partner,b.start,b.end,…itemsFor(b).flatMap(p=>[p.partner,p.description])].join(‘ ‘).toLowerCase().includes(q)); }
function render() { const visible=filtered(), total=bookings.reduce((n,b)=>n+Number(b.adult||0)+Number(b.kid||0)+Number(b.infant||0),0); $(‘totalBookings’).textContent=bookings.length; $(‘totalGuests’).textContent=total; $(‘totalAgents’).textContent=new Set(bookings.map(b=>b.agent)).size; $(‘filterMeta’).textContent=`Menampilkan ${visible.length} dari ${bookings.length} booking`; $(‘durationMeta’).textContent=bookings.length ? `${total} peserta keseluruhan` : ”; rows.innerHTML=visible.map(b=>{const ps=itemsFor(b),services=ps.map(p=>p.partner).filter(Boolean).join(‘, ‘);return `
| ${String(b.no).padStart(3,’0′)} | ${esc(b.agent)} | ${esc(b.guestName||’—’)} | ${esc(b.package)} | ${fmtDate(b.start)} — ${fmtDate(b.end)}${nights(b)} malam | A ${b.adult}K ${b.kid}I ${b.infant} ${Number(b.adult)+Number(b.kid)+Number(b.infant)} peserta | ${esc(services||’—’)} | |
`;}).join(”); $(’emptyState’).hidden=visible.length!==0; document.querySelector(‘.table-wrap’).hidden=visible.length===0; }
function resetForm() { form.reset(); $(‘editingId’).value=”; $(‘adult’).value=1; [‘kid’,’infant’].forEach(id => $(id).value=0); $(‘formTitle’).textContent=’Tambah Booking’; $(‘formBadge’).textContent=’BARU’; $(‘submitBtn’).textContent=’Simpan Booking’; $(‘cancelBtn’).hidden=true; refreshAgents(); }
function edit(id) { const b=bookings.find(x=>x.id===id); if(!b)return; refreshAgents(); $(‘editingId’).value=b.id; $(‘agent’).value=b.agent; $(‘guestName’).value=b.guestName||”; $(‘package’).value=b.package; $(‘startDate’).value=b.start; $(‘endDate’).value=b.end; $(‘adult’).value=b.adult; $(‘kid’).value=b.kid; $(‘infant’).value=b.infant; $(‘formTitle’).textContent=’Edit Booking’; $(‘formBadge’).textContent=`NO. ${String(b.no).padStart(3,’0′)}`; $(‘submitBtn’).textContent=’Simpan Perubahan’; $(‘cancelBtn’).hidden=false; document.querySelector(‘.form-card’).scrollIntoView({behavior:’smooth’,block:’start’}); }
form.addEventListener(‘submit’, e => { e.preventDefault(); if(!form.reportValidity())return; const start=$(‘startDate’).value,end=$(‘endDate’).value; if(end<start){toast('Tanggal selesai harus setelah tanggal mulai'); $('endDate').focus();return;} const data={agent:$('agent').value,guestName:$('guestName').value.trim(),package:$('package').value.trim(),start,end,adult:Number($('adult').value),kid:Number($('kid').value),infant:Number($('infant').value)}; if(data.adult+data.kid+data.infantb.id===id),data);toast(‘Booking berhasil diperbarui’);}else{bookings.push({id:crypto.randomUUID?crypto.randomUUID():String(Date.now()),no:Math.max(0,…bookings.map(b=>Number(b.no)||0))+1,…data});toast(‘Booking baru berhasil ditambahkan’);}save();render();resetForm(); });
rows.addEventListener(‘click’,e=>{const btn=e.target.closest(‘button[data-action]’);if(!btn)return;const b=bookings.find(x=>x.id===btn.dataset.id);if(!b)return;if(btn.dataset.action===’edit’)edit(b.id);else if(confirm(`Hapus booking ${b.agent} — ${b.package}?`)){bookings=bookings.filter(x=>x.id!==b.id);save();render();if($(‘editingId’).value===b.id)resetForm();toast(‘Booking telah dihapus’);}});
$(‘cancelBtn’).addEventListener(‘click’,resetForm); search.addEventListener(‘input’,render); window.addEventListener(‘focus’,()=>{bookings=loadBookings();refreshAgents();render();}); window.addEventListener(‘storage’,e=>{if(e.key===GUEST_KEY)refreshAgents();if(e.key===BOOKING_KEY){bookings=loadBookings();render();}}); $(‘today’).textContent=new Intl.DateTimeFormat(‘id-ID’,{weekday:’long’,day:’numeric’,month:’long’,year:’numeric’}).format(new Date()); refreshAgents();render();
})();