fix: api/toast/timeAgo 함수를 body 스크립트보다 앞에 배치

스크립트 실행 순서: __INIT__ → 유틸함수 → 페이지 스크립트

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
chpark
2026-03-27 13:20:53 +09:00
parent d576cb5255
commit 2343c1903d
+31 -39
View File
@@ -116,48 +116,40 @@ tr:hover td{background:rgba(255,255,255,.02)}
</div>
<div class="content">
<%- typeof ssrData !== 'undefined' ? ssrData : '' %>
<script>
function api(method, url, data) {
var opts = { method: method, headers: { 'Content-Type': 'application/json' }, credentials: 'same-origin' };
if (data) opts.body = JSON.stringify(data);
return fetch(url, opts).then(function(r) {
var ct = r.headers.get('content-type') || '';
if (r.status === 401) { window.location.href = '/login?redirect=' + encodeURIComponent(window.location.pathname); return []; }
if (!ct.includes('application/json')) { console.error('API non-JSON:', r.status); return []; }
return r.json();
}).catch(function(err) { console.error('API Error:', err); return []; });
}
function toast(msg, type) {
type = type || 'success';
var el = document.getElementById('toast');
el.textContent = msg;
el.style.borderLeftColor = type === 'success' ? 'var(--success)' : type === 'error' ? 'var(--danger)' : 'var(--warning)';
el.style.borderLeftWidth = '3px';
el.classList.add('show');
setTimeout(function() { el.classList.remove('show'); }, 3000);
}
function timeAgo(dateStr) {
if (!dateStr) return '-';
var diff = Date.now() - new Date(dateStr).getTime();
var m = Math.floor(diff / 60000);
if (m < 1) return '방금';
if (m < 60) return m + '분 전';
var h = Math.floor(m / 60);
if (h < 24) return h + '시간 전';
return Math.floor(h / 24) + '일 전';
}
</script>
<%- body %>
</div>
</div>
<div class="toast" id="toast"></div>
<script>
function api(method, url, data) {
const opts = { method, headers: { 'Content-Type': 'application/json' }, credentials: 'same-origin' };
if (data) opts.body = JSON.stringify(data);
return fetch(url, opts).then(r => {
const ct = r.headers.get('content-type') || '';
if (r.status === 401) {
window.location.href = '/login?redirect=' + encodeURIComponent(window.location.pathname);
return [];
}
if (!ct.includes('application/json')) {
console.error('API returned non-JSON:', r.status, ct);
return [];
}
return r.json();
}).catch(err => {
console.error('API Error:', err);
return [];
});
}
function toast(msg, type = 'success') {
const el = document.getElementById('toast');
el.textContent = msg;
el.style.borderLeftColor = type === 'success' ? 'var(--success)' : type === 'error' ? 'var(--danger)' : 'var(--warning)';
el.style.borderLeftWidth = '3px';
el.classList.add('show');
setTimeout(() => el.classList.remove('show'), 3000);
}
function timeAgo(dateStr) {
if (!dateStr) return '-';
const diff = Date.now() - new Date(dateStr).getTime();
const m = Math.floor(diff / 60000);
if (m < 1) return '방금';
if (m < 60) return m + '분 전';
const h = Math.floor(m / 60);
if (h < 24) return h + '시간 전';
return Math.floor(h / 24) + '일 전';
}
</script>
</body>
</html>