Files
AriWeb-Suite/portal/usage/public/allocate.js
T

20 lines
3.6 KiB
JavaScript
Raw Normal View History

/* portal/usage/public/allocate.js — 서비스별 보장량 배정 변경(§13.3). 구매 총량·사용 중·다른 서비스 배정·예약·미배정·변경 후 값을 함께 보인다. */
(() => {
const el = document.getElementById('alloc'); const subject = el.dataset.subject; const esc = aw.esc;
const fmt = (n, unit) => (unit.startsWith('bytes') ? (n >= 1024 ** 3 ? (n / 1024 ** 3).toFixed(2) + ' GB' : n >= 1024 ** 2 ? (n / 1024 ** 2).toFixed(0) + ' MB' : n + ' B') : Number(n).toLocaleString('ko-KR'));
const GB = 1024 ** 3;
async function load() {
try {
const d = await aw.api('GET', `/api/usage/allocate/${encodeURIComponent(subject)}`);
el.innerHTML = `<div class="card"><h2>${esc(d.subject.label)} · ${esc(d.subject.name)}</h2><p class="sub">월간 항목은 ${esc(d.nextResetText)} 에 초기화됩니다. 실제 사용량보다 작게 줄일 수 없고, 월간 항목은 이번 달 누적보다 작게 줄일 수 없습니다.</p></div>` +
d.resources.map((r) => { const bytes = r.unit.startsWith('bytes'); const step = bytes ? GB : 1; return `<div class="card" data-res="${r.resource}"><h2>${esc(r.label)}${r.monthly ? ' (월간)' : ''}</h2>
<dl class="kv"><dt>구매 총량</dt><dd>${esc(r.texts.purchased)}</dd><dt>이 서비스 사용 중(배정)</dt><dd>${esc(r.texts.mine)} <span class="badge neutral">○ ${esc(r.mineState)}</span></dd><dt>다른 서비스 배정</dt><dd>${esc(r.texts.others)}</dd><dt>진행 중 작업 예약</dt><dd>${esc(r.texts.reserved)}</dd><dt>미배정</dt><dd>${esc(r.texts.unallocated)}</dd><dt>실제 사용${r.monthly ? '(이번 달 누적)' : ''}</dt><dd>${esc(r.texts.used)}${r.measuredAt ? ` <span class="sub">계측 ${aw.fmt(r.measuredAt)}</span>` : ''}${r.stale ? ' <span class="badge warning">! 정보 확인 지연 — 늘릴 수 없음</span>' : ''}</dd>${r.limit ? `<dt>제한</dt><dd class="err">${r.limit.limited ? '100% 제한 적용 중' : r.limit.level + '% 경고'}</dd>` : ''}</dl>
<form class="f" data-dirty-guard><label for="in-${r.resource}">변경 후 값 (${bytes ? 'GB' : '개'})</label><input id="in-${r.resource}" type="number" min="${Math.ceil(r.floor / step)}" max="${Math.floor(r.max / step)}" step="${bytes ? '0.5' : '1'}" value="${bytes ? (r.mine / GB).toFixed(1) : r.mine}"><p class="hint">허용 범위 ${fmt(r.floor, r.unit)} ~ ${fmt(r.max, r.unit)} (미배정 ${esc(r.texts.unallocated)} 안에서)</p><p class="err" id="err-${r.resource}"></p><div class="actions"><button class="btn primary" type="submit">적용</button></div></form></div>`; }).join('');
el.querySelectorAll('form').forEach((f) => { f.onsubmit = async (ev) => { ev.preventDefault(); const res = f.closest('[data-res]').dataset.res; const r = d.resources.find((x) => x.resource === res); const v = Number(f.querySelector('input').value); const amount = r.unit.startsWith('bytes') ? Math.round(v * GB) : Math.round(v); const errEl = f.querySelector('.err'); errEl.textContent = '';
try { const out = await aw.api('POST', `/api/usage/allocate/${encodeURIComponent(subject)}`, { resource: res, amount }); aw.markClean(); aw.toast(`${r.label} 배정을 ${fmt(out.before, r.unit)} → ${fmt(out.after, r.unit)} 로 바꿨습니다`); load(); }
catch (e) { errEl.textContent = e.message + (e.detail?.cleanupNeeded ? ` (정리 필요: ${fmt(e.detail.cleanupNeeded, r.unit)})` : e.detail?.shortage ? ` (부족: ${fmt(e.detail.shortage, r.unit)})` : ''); } }; });
} catch (e) { aw.state(el, e.status === 403 ? 'forbidden' : e.status === 404 ? 'empty' : 'failed', e.message, { retry: load }); }
}
load();
})();