Files
marcandClaude Sonnet 4.6 1118681fd2 Restructure frontend into templates/ and static/
- Move index.html → templates/index.html (Jinja2, uses url_for)
- Extract CSS → static/css/app.css
- Split JS into static/js/: utils.js, dashboard.js, shares.js,
  quotas.js, users.js, main.js
- Switch app.py from send_file to render_template

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 22:19:46 +02:00

29 lines
1.2 KiB
JavaScript

function esc(s) {
return String(s ?? '')
.replace(/&/g, '&amp;').replace(/</g, '&lt;')
.replace(/>/g, '&gt;').replace(/"/g, '&quot;');
}
function showToast(msg, type = 'success') {
const colors = { success: '#3fb950', danger: '#f85149', warning: '#d29922' };
const id = 'toast-' + Date.now();
document.getElementById('toastContainer').insertAdjacentHTML('beforeend', `
<div id="${id}" class="toast align-items-center border-0 mb-2" role="alert"
style="background:#161b22; border-left:3px solid ${colors[type] ?? colors.success} !important; color:#e6edf3;">
<div class="d-flex">
<div class="toast-body">${esc(msg)}</div>
<button type="button" class="btn-close btn-close-white me-2 m-auto" data-bs-dismiss="toast"></button>
</div>
</div>`);
const el = document.getElementById(id);
const t = new bootstrap.Toast(el, { delay: 3500 });
t.show();
el.addEventListener('hidden.bs.toast', () => el.remove());
}
async function apiFetch(url, opts) {
const res = await fetch(url, opts);
if (res.status === 401) { location.reload(); return null; }
return res;
}