const state = { companyId: null, propertyGroupId: null, rooms: [], }; function escapeHtml(s) { return String(s) .replace(/&/g, '&') .replace(//g, '>') .replace(/"/g, '"'); } function formatPrice(n) { if (n == null) return '—'; return `${Number(n).toLocaleString('ko-KR')}원`; } function formatStatus(open) { if (open == null) return '—'; return open ? '예약가능' : '마감'; } function formatDt(iso) { return formatUtcDateTime(iso); } function formatMarkupBadgeLabel(amount, { compact = false } = {}) { if (amount == null || amount === 0) return null; const abs = Math.abs(Number(amount)); const sign = Number(amount) >= 0 ? '+' : '-'; if (compact) { if (abs >= 10000) return `${sign}${Math.round(abs / 10000)}만`; if (abs >= 1000) return `${sign}${Math.round(abs / 1000)}천`; return `${sign}${abs.toLocaleString('ko-KR')}`; } return `${sign}${abs.toLocaleString('ko-KR')}`; } function markupBadgeLabel(row) { const amountLabel = formatMarkupBadgeLabel(row?.markup_amount); if (amountLabel) return amountLabel; if (row?.markup_type === 'percent' && row?.markup_value != null) { const v = Number(row.markup_value); return `${v >= 0 ? '+' : ''}${v}%`; } if (row?.markup_type === 'fixed' && row?.markup_value != null) { return formatMarkupBadgeLabel(Number(row.markup_value)) || 'M'; } return 'M'; } function renderRuleBadges(row) { if (!row?.rule_kind) return '—'; const badges = []; if (row.rule_kind === 'hardblock' || row.rule_kind === 'hardblock_markup') { badges.push('HB'); } if (row.rule_kind === 'markup' || row.rule_kind === 'hardblock_markup') { const label = markupBadgeLabel(row); badges.push(`${escapeHtml(label)}`); } return `${badges.join('')}`; } function defaultDateRange() { const end = new Date(); const start = new Date(); start.setDate(start.getDate() - 14); const fmt = (d) => { const y = d.getFullYear(); const m = String(d.getMonth() + 1).padStart(2, '0'); const day = String(d.getDate()).padStart(2, '0'); return `${y}-${m}-${day}`; }; return { start: fmt(start), end: fmt(end) }; } function renderRooms() { const sel = document.getElementById('histRoomFilter'); const current = sel.value; sel.innerHTML = ''; state.rooms.forEach((r) => { const opt = document.createElement('option'); opt.value = r.id; opt.textContent = r.display_name; sel.appendChild(opt); }); if (current && state.rooms.some((r) => r.id === current)) { sel.value = current; } } function renderRows(items) { const body = document.getElementById('histTableBody'); const summary = document.getElementById('histSummary'); if (!items.length) { body.innerHTML = '변경 이력이 없습니다.'; summary.textContent = '0건'; return; } summary.textContent = `${items.length}건 (최신순)`; body.innerHTML = items.map((row) => { const priceChange = row.previous_base_price != null && row.previous_base_price !== row.base_price ? `${formatPrice(row.previous_base_price)} → ${formatPrice(row.base_price)}` : formatPrice(row.base_price); const statusChange = row.previous_status_label ? `${formatStatus(row.previous_inventory_open)} → ${formatStatus(row.inventory_open)}` : formatStatus(row.inventory_open); return ` ${escapeHtml(formatDt(row.changed_at))} ${escapeHtml(row.target_date)} ${escapeHtml(row.display_name)} ${renderRuleBadges(row)} ${priceChange} ${statusChange} `; }).join(''); } async function loadHistory() { if (!state.propertyGroupId) return; const start = document.getElementById('histStartDate').value; const end = document.getElementById('histEndDate').value; const roomId = document.getElementById('histRoomFilter').value; const params = new URLSearchParams({ property_group_id: state.propertyGroupId, limit: '500', }); if (start) params.set('start_date', start); if (end) params.set('end_date', end); if (roomId) params.set('room_mapping_id', roomId); const loading = document.getElementById('histLoading'); loading.classList.remove('hidden'); try { const data = await API.get(`/api/v1/snapshot-history?${params}`); renderRows(data.items || []); } catch (err) { document.getElementById('histTableBody').innerHTML = `로드 실패: ${escapeHtml(err.message)}`; } finally { loading.classList.add('hidden'); } } async function loadRooms() { if (!state.propertyGroupId) { state.rooms = []; renderRooms(); return; } state.rooms = await API.get( `/api/v1/room-mappings?property_group_id=${state.propertyGroupId}&exposed_only=true`, ); renderRooms(); } document.getElementById('histRefreshBtn').addEventListener('click', loadHistory); (async function init() { if (!requireAuth()) return; await applyAdminNav(); const range = defaultDateRange(); document.getElementById('histStartDate').value = range.start; document.getElementById('histEndDate').value = range.end; try { await initCatalogSelectors({ onPropertyChange: async (propertyGroupId) => { state.propertyGroupId = propertyGroupId; state.companyId = document.getElementById('companySelect').value; await loadRooms(); await loadHistory(); }, }); } catch (err) { console.error(err); } })();