/* ============================================================================= ADMIN MODULE - Missing Function Handlers These functions are called by onclick handlers in admin HTML files ============================================================================= */ (function () { "use strict"; // ============================================================================= // MODAL HELPERS // ============================================================================= function showModal(modalId) { const modal = document.getElementById(modalId); if (modal) { if (modal.showModal) { modal.showModal(); } else { modal.classList.add("open"); modal.style.display = "flex"; } } } function hideModal(modalId) { const modal = document.getElementById(modalId); if (modal) { if (modal.close) { modal.close(); } else { modal.classList.remove("open"); modal.style.display = "none"; } } } function showNotification(message, type) { if (typeof window.showNotification === "function") { window.showNotification(message, type); } else if (typeof window.GBAlerts !== "undefined") { if (type === "success") window.GBAlerts.success("Admin", message); else if (type === "error") window.GBAlerts.error("Admin", message); else if (type === "warning") window.GBAlerts.warning("Admin", message); else window.GBAlerts.info("Admin", message); } else { console.log(`[${type}] ${message}`); } } // ============================================================================= // ACCOUNTS.HTML FUNCTIONS // ============================================================================= function showSmtpModal() { showModal("smtp-modal"); } function closeSmtpModal() { hideModal("smtp-modal"); } function testSmtpConnection() { const host = document.getElementById("smtp-host")?.value; const port = document.getElementById("smtp-port")?.value; const username = document.getElementById("smtp-username")?.value; if (!host || !port) { showNotification("Please fill in SMTP host and port", "error"); return; } showNotification("Testing SMTP connection...", "info"); fetch("/api/settings/smtp/test", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ host, port: parseInt(port), username }), }) .then((response) => response.json()) .then((data) => { if (data.success) { showNotification("SMTP connection successful!", "success"); } else { showNotification( "SMTP connection failed: " + (data.error || "Unknown error"), "error", ); } }) .catch((err) => { showNotification("Connection test failed: " + err.message, "error"); }); } function connectAccount(provider) { showNotification(`Connecting to ${provider}...`, "info"); // OAuth flow would redirect to provider window.location.href = `/api/auth/oauth/${provider}?redirect=/admin/accounts`; } function disconnectAccount(provider) { if (!confirm(`Disconnect ${provider} account?`)) return; fetch(`/api/settings/accounts/${provider}/disconnect`, { method: "POST" }) .then((response) => response.json()) .then((data) => { if (data.success) { showNotification(`${provider} disconnected`, "success"); location.reload(); } else { showNotification("Failed to disconnect: " + data.error, "error"); } }) .catch((err) => showNotification("Error: " + err.message, "error")); } // ============================================================================= // ADMIN-DASHBOARD.HTML FUNCTIONS // ============================================================================= function showInviteMemberModal() { showModal("invite-member-modal"); } function closeInviteMemberModal() { hideModal("invite-member-modal"); } function showBulkInviteModal() { showModal("bulk-invite-modal"); } function closeBulkInviteModal() { hideModal("bulk-invite-modal"); } function sendInvitation() { const email = document.getElementById("invite-email")?.value; const role = document.getElementById("invite-role")?.value || "member"; if (!email) { showNotification("Please enter an email address", "error"); return; } fetch("/api/admin/invitations", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ email, role }), }) .then((response) => response.json()) .then((data) => { if (data.success) { showNotification("Invitation sent to " + email, "success"); closeInviteMemberModal(); } else { showNotification("Failed to send invitation: " + data.error, "error"); } }) .catch((err) => showNotification("Error: " + err.message, "error")); } function sendBulkInvitations() { const emailsText = document.getElementById("bulk-emails")?.value || ""; const role = document.getElementById("bulk-role")?.value || "member"; const emails = emailsText .split(/[\n,;]+/) .map((e) => e.trim()) .filter((e) => e); if (emails.length === 0) { showNotification("Please enter at least one email address", "error"); return; } fetch("/api/admin/invitations/bulk", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ emails, role }), }) .then((response) => response.json()) .then((data) => { showNotification( `${data.sent || emails.length} invitations sent`, "success", ); closeBulkInviteModal(); }) .catch((err) => showNotification("Error: " + err.message, "error")); } function resendInvitation(invitationId) { fetch(`/api/admin/invitations/${invitationId}/resend`, { method: "POST" }) .then((response) => response.json()) .then((data) => { if (data.success) { showNotification("Invitation resent", "success"); } else { showNotification("Failed to resend: " + data.error, "error"); } }) .catch((err) => showNotification("Error: " + err.message, "error")); } function cancelInvitation(invitationId) { if (!confirm("Cancel this invitation?")) return; fetch(`/api/admin/invitations/${invitationId}`, { method: "DELETE" }) .then((response) => response.json()) .then((data) => { if (data.success) { showNotification("Invitation cancelled", "success"); location.reload(); } }) .catch((err) => showNotification("Error: " + err.message, "error")); } // ============================================================================= // BILLING-DASHBOARD.HTML FUNCTIONS // ============================================================================= function updateBillingPeriod(period) { const params = new URLSearchParams({ period }); // Update dashboard stats via HTMX or fetch if (typeof htmx !== "undefined") { htmx.ajax("GET", `/api/admin/billing/stats?${params}`, "#billing-stats"); } else { fetch(`/api/admin/billing/stats?${params}`) .then((r) => r.json()) .then((data) => updateBillingStats(data)) .catch((err) => console.error("Failed to update billing period:", err)); } } function updateBillingStats(data) { if (data.totalRevenue) { const el = document.getElementById("total-revenue"); if (el) el.textContent = formatCurrency(data.totalRevenue); } if (data.activeSubscriptions) { const el = document.getElementById("active-subscriptions"); if (el) el.textContent = data.activeSubscriptions; } } function exportBillingReport() { const period = document.getElementById("billingPeriod")?.value || "current"; showNotification("Generating billing report...", "info"); fetch(`/api/admin/billing/export?period=${period}`) .then((response) => { if (response.ok) return response.blob(); throw new Error("Export failed"); }) .then((blob) => { const url = URL.createObjectURL(blob); const a = document.createElement("a"); a.href = url; a.download = `billing-report-${period}.csv`; a.click(); URL.revokeObjectURL(url); showNotification("Report downloaded", "success"); }) .catch((err) => showNotification("Export failed: " + err.message, "error"), ); } function toggleBreakdownView() { const chart = document.getElementById("breakdown-chart"); const table = document.getElementById("breakdown-table"); if (chart && table) { const showingChart = !chart.classList.contains("hidden"); chart.classList.toggle("hidden", showingChart); table.classList.toggle("hidden", !showingChart); } } function showQuotaSettings() { showModal("quota-settings-modal"); } function closeQuotaSettings() { hideModal("quota-settings-modal"); } function saveQuotaSettings() { const form = document.getElementById("quota-form"); if (!form) return; const formData = new FormData(form); const quotas = Object.fromEntries(formData); fetch("/api/admin/billing/quotas", { method: "PUT", headers: { "Content-Type": "application/json" }, body: JSON.stringify(quotas), }) .then((response) => response.json()) .then((data) => { if (data.success) { showNotification("Quota settings saved", "success"); closeQuotaSettings(); } else { showNotification("Failed to save: " + data.error, "error"); } }) .catch((err) => showNotification("Error: " + err.message, "error")); } function configureAlerts() { showModal("alerts-config-modal"); } function closeAlertsConfig() { hideModal("alerts-config-modal"); } function saveAlertSettings() { const form = document.getElementById("alerts-form"); if (!form) return; const formData = new FormData(form); const settings = Object.fromEntries(formData); fetch("/api/admin/billing/alerts", { method: "PUT", headers: { "Content-Type": "application/json" }, body: JSON.stringify(settings), }) .then((response) => response.json()) .then((data) => { if (data.success) { showNotification("Alert settings saved", "success"); closeAlertsConfig(); } }) .catch((err) => showNotification("Error: " + err.message, "error")); } // ============================================================================= // BILLING.HTML FUNCTIONS // ============================================================================= function showUpgradeModal() { showModal("upgrade-modal"); } function closeUpgradeModal() { hideModal("upgrade-modal"); } function showCancelModal() { showModal("cancel-modal"); } function closeCancelModal() { hideModal("cancel-modal"); } function showAddPaymentModal() { showModal("add-payment-modal"); } function closeAddPaymentModal() { hideModal("add-payment-modal"); } function showEditAddressModal() { showModal("edit-address-modal"); } function closeEditAddressModal() { hideModal("edit-address-modal"); } function exportInvoices() { showNotification("Exporting invoices...", "info"); fetch("/api/billing/invoices/export") .then((response) => { if (response.ok) return response.blob(); throw new Error("Export failed"); }) .then((blob) => { const url = URL.createObjectURL(blob); const a = document.createElement("a"); a.href = url; a.download = "invoices.csv"; a.click(); URL.revokeObjectURL(url); showNotification("Invoices exported", "success"); }) .catch((err) => showNotification("Export failed: " + err.message, "error"), ); } function contactSales() { window.open( "mailto:sales@example.com?subject=Enterprise Plan Inquiry", "_blank", ); } function showDowngradeOptions() { closeCancelModal(); showUpgradeModal(); // Focus on lower-tier plans const planSelector = document.querySelector(".plan-options"); if (planSelector) { planSelector.scrollIntoView({ behavior: "smooth" }); } } function selectPlan(planId) { document.querySelectorAll(".plan-option").forEach((el) => { el.classList.toggle("selected", el.dataset.plan === planId); }); } function confirmUpgrade() { const selectedPlan = document.querySelector(".plan-option.selected"); if (!selectedPlan) { showNotification("Please select a plan", "error"); return; } const planId = selectedPlan.dataset.plan; fetch("/api/billing/subscription/upgrade", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ plan_id: planId }), }) .then((response) => response.json()) .then((data) => { if (data.success) { showNotification("Plan upgraded successfully!", "success"); closeUpgradeModal(); location.reload(); } else { showNotification("Upgrade failed: " + data.error, "error"); } }) .catch((err) => showNotification("Error: " + err.message, "error")); } function confirmCancellation() { const reason = document.getElementById("cancel-reason")?.value; fetch("/api/billing/subscription/cancel", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ reason }), }) .then((response) => response.json()) .then((data) => { if (data.success) { showNotification("Subscription cancelled", "success"); closeCancelModal(); location.reload(); } else { showNotification("Cancellation failed: " + data.error, "error"); } }) .catch((err) => showNotification("Error: " + err.message, "error")); } // ============================================================================= // COMPLIANCE-DASHBOARD.HTML FUNCTIONS // ============================================================================= function updateFramework(framework) { // Update dashboard for selected compliance framework if (typeof htmx !== "undefined") { htmx.ajax( "GET", `/api/compliance/dashboard?framework=${framework}`, "#compliance-content", ); } else { fetch(`/api/compliance/dashboard?framework=${framework}`) .then((r) => r.json()) .then((data) => updateComplianceDashboard(data)) .catch((err) => console.error("Failed to update framework:", err)); } } function updateComplianceDashboard(data) { // Update various dashboard elements if (data.score) { const el = document.getElementById("compliance-score"); if (el) el.textContent = data.score + "%"; } } function generateComplianceReport() { const framework = document.getElementById("complianceFramework")?.value || "soc2"; showNotification("Generating compliance report...", "info"); fetch(`/api/compliance/report?framework=${framework}`) .then((response) => { if (response.ok) return response.blob(); throw new Error("Report generation failed"); }) .then((blob) => { const url = URL.createObjectURL(blob); const a = document.createElement("a"); a.href = url; a.download = `compliance-report-${framework}.pdf`; a.click(); URL.revokeObjectURL(url); showNotification("Report generated", "success"); }) .catch((err) => showNotification("Report failed: " + err.message, "error"), ); } function startAuditPrep() { showModal("audit-prep-modal"); } function closeAuditPrep() { hideModal("audit-prep-modal"); } function showEvidenceUpload() { showModal("evidence-upload-modal"); } function closeEvidenceUpload() { hideModal("evidence-upload-modal"); } function uploadEvidence() { const fileInput = document.getElementById("evidence-file"); const category = document.getElementById("evidence-category")?.value; if (!fileInput?.files?.length) { showNotification("Please select a file", "error"); return; } const formData = new FormData(); formData.append("file", fileInput.files[0]); formData.append("category", category); fetch("/api/compliance/evidence", { method: "POST", body: formData, }) .then((response) => response.json()) .then((data) => { if (data.success) { showNotification("Evidence uploaded", "success"); closeEvidenceUpload(); } else { showNotification("Upload failed: " + data.error, "error"); } }) .catch((err) => showNotification("Error: " + err.message, "error")); } function filterLogs() { const category = document.getElementById("logCategory")?.value || "all"; if (typeof htmx !== "undefined") { htmx.ajax( "GET", `/api/compliance/audit-log?category=${category}`, "#audit-log-list", ); } } function exportAuditLog() { const category = document.getElementById("logCategory")?.value || "all"; showNotification("Exporting audit log...", "info"); fetch(`/api/compliance/audit-log/export?category=${category}`) .then((response) => { if (response.ok) return response.blob(); throw new Error("Export failed"); }) .then((blob) => { const url = URL.createObjectURL(blob); const a = document.createElement("a"); a.href = url; a.download = "audit-log.csv"; a.click(); URL.revokeObjectURL(url); showNotification("Audit log exported", "success"); }) .catch((err) => showNotification("Export failed: " + err.message, "error"), ); } // ============================================================================= // GROUPS.HTML FUNCTIONS // ============================================================================= function closeDetailPanel() { const panel = document.getElementById("detail-panel"); if (panel) { panel.classList.remove("open"); } } function openDetailPanel(groupId) { const panel = document.getElementById("detail-panel"); if (panel) { panel.classList.add("open"); // Load group details if (typeof htmx !== "undefined") { htmx.ajax("GET", `/api/admin/groups/${groupId}`, "#panel-content"); } } } function createGroup() { showModal("create-group-modal"); } function closeCreateGroup() { hideModal("create-group-modal"); } function saveGroup() { const name = document.getElementById("group-name")?.value; const description = document.getElementById("group-description")?.value; if (!name) { showNotification("Please enter a group name", "error"); return; } fetch("/api/admin/groups", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ name, description }), }) .then((response) => response.json()) .then((data) => { if (data.success) { showNotification("Group created", "success"); closeCreateGroup(); location.reload(); } else { showNotification("Failed to create group: " + data.error, "error"); } }) .catch((err) => showNotification("Error: " + err.message, "error")); } function deleteGroup(groupId) { if (!confirm("Delete this group? This action cannot be undone.")) return; fetch(`/api/admin/groups/${groupId}`, { method: "DELETE" }) .then((response) => response.json()) .then((data) => { if (data.success) { showNotification("Group deleted", "success"); closeDetailPanel(); location.reload(); } }) .catch((err) => showNotification("Error: " + err.message, "error")); } // ============================================================================= // ROLE MANAGEMENT FUNCTIONS (roles.html) // ============================================================================= let currentRole = null; let availablePermissions = []; let assignedPermissions = []; function selectRole(roleId, element) { // Update UI selection document .querySelectorAll(".role-item") .forEach((item) => item.classList.remove("selected")); if (element) element.classList.add("selected"); // Show role detail document .getElementById("role-placeholder") ?.style.setProperty("display", "none"); document .getElementById("role-detail") ?.style.setProperty("display", "block"); // Fetch role details fetch(`/api/rbac/roles/${roleId}`) .then((response) => response.json()) .then((data) => { currentRole = data; renderRoleDetail(data); }) .catch((err) => showNotification("Failed to load role: " + err.message, "error"), ); } function renderRoleDetail(role) { document.getElementById("role-display-name").textContent = role.displayName || role.name; document.getElementById("role-name").textContent = role.name; const typeBadge = document.getElementById("role-type-badge"); if (typeBadge) { typeBadge.textContent = role.isSystem ? "System" : "Custom"; typeBadge.className = "role-type-badge " + (role.isSystem ? "system" : "custom"); } // Enable/disable delete button based on system role const deleteBtn = document.getElementById("btn-delete-role"); if (deleteBtn) deleteBtn.disabled = role.isSystem; // Load permissions loadRolePermissions(role.id); } function loadRolePermissions(roleId) { Promise.all([ fetch("/api/rbac/permissions").then((r) => r.json()), fetch(`/api/rbac/roles/${roleId}/permissions`).then((r) => r.json()), ]) .then(([allPerms, rolePerms]) => { assignedPermissions = rolePerms || []; availablePermissions = (allPerms || []).filter( (p) => !assignedPermissions.find((rp) => rp.id === p.id), ); renderPermissionLists(); }) .catch((err) => console.error("Failed to load permissions:", err)); } function renderPermissionLists() { const availableList = document.getElementById("available-permissions"); const assignedList = document.getElementById("assigned-permissions"); if (availableList) { availableList.innerHTML = availablePermissions .map( (p) => `
`, ) .join("") || '${data.description || "No description available"}
`; } } }) .catch((err) => showNotification("Failed to load evidence: " + err.message, "error"), ); } // ============================================================================= // OPERATIONS DASHBOARD FUNCTIONS // ============================================================================= let autoRefreshEnabled = true; let autoRefreshInterval = null; function toggleAutoRefresh() { autoRefreshEnabled = !autoRefreshEnabled; const label = document.getElementById("autoRefreshLabel"); if (label) { label.textContent = `Auto-refresh: ${autoRefreshEnabled ? "ON" : "OFF"}`; } if (autoRefreshEnabled) { startAutoRefresh(); } else { stopAutoRefresh(); } } function startAutoRefresh() { if (autoRefreshInterval) clearInterval(autoRefreshInterval); autoRefreshInterval = setInterval(() => { refreshHealth(); }, 30000); } function stopAutoRefresh() { if (autoRefreshInterval) { clearInterval(autoRefreshInterval); autoRefreshInterval = null; } } function showAlertConfig() { showModal("alert-config-modal"); } function closeAlertConfig() { hideModal("alert-config-modal"); } function showTraceDetail(traceId) { showNotification(`Loading trace ${traceId}...`, "info"); fetch(`/api/ops/traces/${traceId}`) .then((response) => response.json()) .then((data) => { showModal("trace-detail-modal"); const content = document.getElementById("trace-detail-content"); if (content) { content.innerHTML = `