bugfixing

This commit is contained in:
2026-02-07 22:46:39 +01:00
parent ae63817172
commit 6646adb4a4
2 changed files with 34 additions and 8 deletions

View File

@@ -1,6 +1,5 @@
"""Customer CRUD API endpoints with automatic deployment on create.""" """Customer CRUD API endpoints with automatic deployment on create."""
import asyncio
import logging import logging
from datetime import datetime from datetime import datetime
from typing import Optional from typing import Optional

View File

@@ -27,14 +27,28 @@ async function api(method, path, body = null) {
if (body) { if (body) {
opts.body = JSON.stringify(body); opts.body = JSON.stringify(body);
} }
const resp = await fetch(`/api${path}`, opts); let resp;
try {
resp = await fetch(`/api${path}`, opts);
} catch (networkErr) {
console.error(`API network error: ${method} ${path}`, networkErr);
throw new Error('Network error — server not reachable.');
}
if (resp.status === 401) { if (resp.status === 401) {
logout(); logout();
throw new Error('Session expired.'); throw new Error('Session expired.');
} }
const data = await resp.json(); let data;
try {
data = await resp.json();
} catch (jsonErr) {
console.error(`API JSON parse error: ${method} ${path} (status ${resp.status})`, jsonErr);
throw new Error(`Server error (HTTP ${resp.status}).`);
}
if (!resp.ok) { if (!resp.ok) {
throw new Error(data.detail || data.message || 'Request failed.'); const msg = data.detail || data.message || 'Request failed.';
console.error(`API error: ${method} ${path} (status ${resp.status})`, msg);
throw new Error(msg);
} }
return data; return data;
} }
@@ -221,7 +235,8 @@ function showNewCustomerModal() {
document.getElementById('cust-subdomain-suffix').textContent = `.${cfg.base_domain || 'domain.com'}`; document.getElementById('cust-subdomain-suffix').textContent = `.${cfg.base_domain || 'domain.com'}`;
}).catch(() => {}); }).catch(() => {});
const modal = new bootstrap.Modal(document.getElementById('customer-modal')); const modalEl = document.getElementById('customer-modal');
const modal = bootstrap.Modal.getOrCreateInstance(modalEl);
// Enable subdomain field for new customers // Enable subdomain field for new customers
document.getElementById('cust-subdomain').disabled = false; document.getElementById('cust-subdomain').disabled = false;
modal.show(); modal.show();
@@ -242,7 +257,8 @@ function editCurrentCustomer() {
document.getElementById('customer-modal-error').classList.add('d-none'); document.getElementById('customer-modal-error').classList.add('d-none');
document.getElementById('customer-save-btn').textContent = 'Save Changes'; document.getElementById('customer-save-btn').textContent = 'Save Changes';
const modal = new bootstrap.Modal(document.getElementById('customer-modal')); const modalEl = document.getElementById('customer-modal');
const modal = bootstrap.Modal.getOrCreateInstance(modalEl);
modal.show(); modal.show();
} }
@@ -268,14 +284,25 @@ async function saveCustomer() {
payload.subdomain = document.getElementById('cust-subdomain').value.toLowerCase(); payload.subdomain = document.getElementById('cust-subdomain').value.toLowerCase();
await api('POST', '/customers', payload); await api('POST', '/customers', payload);
} }
bootstrap.Modal.getInstance(document.getElementById('customer-modal')).hide(); // Close modal safely
const modalEl = document.getElementById('customer-modal');
const modalInstance = bootstrap.Modal.getInstance(modalEl);
if (modalInstance) {
modalInstance.hide();
} else {
modalEl.classList.remove('show');
document.body.classList.remove('modal-open');
document.querySelector('.modal-backdrop')?.remove();
}
loadDashboard(); loadDashboard();
if (editId && currentCustomerId == editId) { if (editId && currentCustomerId == editId) {
viewCustomer(editId); viewCustomer(editId);
} }
} catch (err) { } catch (err) {
errorEl.textContent = err.message; console.error('saveCustomer error:', err);
errorEl.textContent = err.message || 'An unknown error occurred.';
errorEl.classList.remove('d-none'); errorEl.classList.remove('d-none');
errorEl.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
} finally { } finally {
spinner.classList.add('d-none'); spinner.classList.add('d-none');
} }