Files
breadcrumb-the-shire/modules/helpdesk/web/js/helpdesk-settings.js

182 lines
6.0 KiB
JavaScript

/**
* 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();
},
};
}