/** * Helpdesk settings page — auth mode toggle and connection test. */ import { showAsyncFlash } from '/js/components/app-async-flash.js'; import { postForm } from '/js/core/app-http.js'; import { resolveHost } from '/js/core/app-dom.js'; const EMPTY_API = Object.freeze({ destroy: () => {} }); const resolveContainer = (root) => { const host = resolveHost(root); if (host instanceof HTMLElement && host.matches('.app-details-container[data-app-component="helpdesk-settings"]')) { return host; } return host.querySelector('.app-details-container[data-app-component="helpdesk-settings"]'); }; const readMessage = (form, key, fallback) => { if (!form || !form.dataset) { return fallback; } return form.dataset[key] || fallback; }; export function initHelpdeskSettings(root = document) { const container = resolveContainer(root); if (!container) { return EMPTY_API; } const settingsForm = container.querySelector('#helpdesk-settings-form'); if (!settingsForm) { return EMPTY_API; } const listenerController = new AbortController(); const requestController = new AbortController(); const on = (target, type, handler, options = {}) => { if (!target || typeof target.addEventListener !== 'function') { return; } target.addEventListener(type, handler, { ...options, signal: listenerController.signal }); }; const authModeSelect = settingsForm.querySelector('#auth_mode'); const basicFields = settingsForm.querySelector('#helpdesk-basic-auth-fields'); const oauth2Fields = settingsForm.querySelector('#helpdesk-oauth2-fields'); const testButton = settingsForm.querySelector('#helpdesk-test-connection-button'); const testResult = settingsForm.querySelector('#helpdesk-connection-test-result'); const overrideToggle = settingsForm.querySelector('#helpdesk-override-toggle'); const tenantFields = settingsForm.querySelector('#helpdesk-tenant-fields'); const saveTargetInput = settingsForm.querySelector('#helpdesk-settings-save-target'); const tenantAuthModeSelect = settingsForm.querySelector('#tenant_auth_mode'); const tenantBasicFields = settingsForm.querySelector('#helpdesk-tenant-basic-auth-fields'); const tenantOauth2Fields = settingsForm.querySelector('#helpdesk-tenant-oauth2-fields'); const setTestResult = (variant, message) => { if (!testResult) { return; } testResult.textContent = message; testResult.dataset.variant = variant; testResult.hidden = !message; }; const setButtonLoadingState = (isLoading) => { if (!testButton) { return; } const defaultLabel = testButton.dataset.labelDefault || readMessage(settingsForm, 'testDefaultLabel', 'Test connection'); const loadingLabel = testButton.dataset.labelLoading || readMessage(settingsForm, 'testLoadingLabel', 'Testing...'); testButton.disabled = isLoading; testButton.toggleAttribute('aria-busy', isLoading); testButton.textContent = isLoading ? loadingLabel : defaultLabel; }; const toggleAuthFields = () => { if (!authModeSelect || !basicFields || !oauth2Fields) { return; } const isOAuth2 = authModeSelect.value === 'oauth2'; basicFields.toggleAttribute('hidden', isOAuth2); oauth2Fields.toggleAttribute('hidden', !isOAuth2); }; on(authModeSelect, 'change', toggleAuthFields); toggleAuthFields(); on(overrideToggle, 'change', () => { if (tenantFields) { tenantFields.hidden = !overrideToggle.checked; } }); on(settingsForm, 'submit', () => { if (!saveTargetInput) { return; } const tenantPanel = settingsForm.querySelector('[data-tab-panel="tenant"]:not([hidden])'); saveTargetInput.value = tenantPanel ? 'tenant' : 'global'; }); on(tenantAuthModeSelect, 'change', () => { if (!tenantBasicFields || !tenantOauth2Fields || !tenantAuthModeSelect) { return; } const isOAuth2 = tenantAuthModeSelect.value === 'oauth2'; tenantBasicFields.hidden = isOAuth2; tenantOauth2Fields.hidden = !isOAuth2; }); on(testButton, 'click', async () => { setButtonLoadingState(true); setTestResult('info', readMessage(settingsForm, 'testLoadingLabel', 'Testing...')); try { const csrfInput = settingsForm.querySelector('input[name="csrf_token"]'); const csrfMeta = document.querySelector('meta[name="csrf-token"]'); const csrfToken = csrfInput?.value || csrfMeta?.content || ''; const body = new URLSearchParams(); if (csrfToken) { body.set('csrf_token', csrfToken); } const data = await postForm( new URL('helpdesk/settings/test-connection-data', document.baseURI).href, body, { signal: requestController.signal } ); const successMessage = readMessage(settingsForm, 'testSuccessMessage', 'Connection successful'); const defaultErrorMessage = readMessage(settingsForm, 'testErrorMessage', 'Connection failed'); if (data?.ok === true) { const message = data.message || successMessage; setTestResult('success', message); showAsyncFlash('success', message); } else { let message = defaultErrorMessage; if (data?.error) { message = data.error; } if (data?.debug?.http_code) { message += ` (HTTP ${data.debug.http_code})`; } setTestResult('warning', message); showAsyncFlash('error', message); } } catch (error) { if (error instanceof Error && error.name === 'AbortError') { return; } const defaultErrorMessage = readMessage(settingsForm, 'testErrorMessage', 'Connection failed'); let message = defaultErrorMessage; const status = error?.status ?? 0; if (status > 0) { message = `${defaultErrorMessage} (HTTP ${status})`; } setTestResult('warning', message); showAsyncFlash('error', message); } finally { setButtonLoadingState(false); } }); return { destroy: () => { listenerController.abort(); requestController.abort(); }, }; }