diff --git a/pages/admin/tenants/_form.phtml b/pages/admin/tenants/_form.phtml
index 1067c51..4b53be7 100644
--- a/pages/admin/tenants/_form.phtml
+++ b/pages/admin/tenants/_form.phtml
@@ -811,10 +811,11 @@ $openOverrideCard = $detailsOpenAll;
+
- >
+ >
@@ -862,7 +863,7 @@ $openOverrideCard = $detailsOpenAll;
- >
+ >
@@ -890,11 +891,12 @@ $openOverrideCard = $detailsOpenAll;
-
diff --git a/web/js/app-init.js b/web/js/app-init.js
index e6d29b8..d268dbb 100644
--- a/web/js/app-init.js
+++ b/web/js/app-init.js
@@ -18,6 +18,7 @@ import { initTenantSwitcher } from './components/app-tenant-switcher.js';
import { initColorDefaultToggle } from './components/app-color-default-toggle.js';
import { initCustomFieldOptionsToggle } from './components/app-custom-field-options-toggle.js';
import { initTenantSsoToggle } from './components/app-tenant-sso-toggle.js';
+import { initTenantLdapToggle } from './components/app-tenant-ldap-toggle.js';
import { initTelemetrySettings } from './components/app-settings-telemetry.js';
import { initStandardDetailPages } from './components/app-standard-detail-page.js';
import { initAutoSubmit } from './components/app-auto-submit.js';
@@ -231,6 +232,10 @@ const bootRuntime = async () => {
scope: 'global',
configPath: 'components.tenantSsoToggle',
});
+ runtime.register('tenant-ldap-toggle', initTenantLdapToggle, {
+ scope: 'global',
+ configPath: 'components.tenantLdapToggle',
+ });
runtime.register('standard-detail-page', initStandardDetailPages, {
scope: 'global',
configPath: 'components.standardDetailPage',
diff --git a/web/js/components/app-tenant-ldap-toggle.js b/web/js/components/app-tenant-ldap-toggle.js
new file mode 100644
index 0000000..f66438c
--- /dev/null
+++ b/web/js/components/app-tenant-ldap-toggle.js
@@ -0,0 +1,96 @@
+/**
+ * Shows/hides LDAP configuration fields based on tenant LDAP toggle.
+ * Mirrors app-tenant-sso-toggle.js pattern for Microsoft SSO.
+ */
+import { resolveHost } from '../core/app-dom.js';
+
+const syncControlState = (container, show) => {
+ if (!(container instanceof HTMLElement)) {
+ return;
+ }
+
+ container.hidden = !show;
+ container.querySelectorAll('input, select, textarea, button').forEach((control) => {
+ if ((control instanceof HTMLInputElement) && control.type === 'hidden') {
+ return;
+ }
+
+ if (!control.dataset.initialDisabled) {
+ control.dataset.initialDisabled = control.hasAttribute('disabled') ? '1' : '0';
+ }
+
+ if (!show) {
+ control.setAttribute('disabled', 'disabled');
+ return;
+ }
+
+ if (control.dataset.initialDisabled === '1') {
+ control.setAttribute('disabled', 'disabled');
+ return;
+ }
+
+ control.removeAttribute('disabled');
+ });
+};
+
+const initRoot = (root) => {
+ const enabledInput = root.querySelector('[data-tenant-ldap-enabled]');
+ if (!(enabledInput instanceof HTMLInputElement)) {
+ return null;
+ }
+
+ const syncToggle = root.querySelector('[data-tenant-ldap-sync-toggle]');
+ const enabledBlocks = root.querySelectorAll('[data-tenant-ldap-when-enabled]');
+ const syncFieldsBlock = root.querySelector('[data-tenant-ldap-sync-fields]');
+
+ const updateUi = () => {
+ const ldapEnabled = enabledInput.checked;
+ const syncEnabled = syncToggle instanceof HTMLInputElement ? syncToggle.checked : false;
+
+ enabledBlocks.forEach((block) => syncControlState(block, ldapEnabled));
+ syncControlState(syncFieldsBlock, ldapEnabled && syncEnabled);
+ };
+
+ enabledInput.addEventListener('change', updateUi);
+ if (syncToggle instanceof HTMLInputElement) {
+ syncToggle.addEventListener('change', updateUi);
+ }
+
+ updateUi();
+ return () => {
+ enabledInput.removeEventListener('change', updateUi);
+ if (syncToggle instanceof HTMLInputElement) {
+ syncToggle.removeEventListener('change', updateUi);
+ }
+ };
+};
+
+export function initTenantLdapToggle(root = document, options = {}) {
+ const host = resolveHost(root);
+ const selector = String(options.selector || '[data-tenant-ldap-root]').trim() || '[data-tenant-ldap-root]';
+ const roots = Array.from(host.querySelectorAll(selector));
+ if (!roots.length) {
+ return { destroy: () => {} };
+ }
+
+ const cleanupFns = [];
+ roots.forEach((item) => {
+ if (!(item instanceof HTMLElement) || item.dataset.tenantLdapToggleBound === '1') {
+ return;
+ }
+ item.dataset.tenantLdapToggleBound = '1';
+ const cleanup = initRoot(item);
+ if (typeof cleanup === 'function') {
+ cleanupFns.push(cleanup);
+ }
+ cleanupFns.push(() => {
+ delete item.dataset.tenantLdapToggleBound;
+ });
+ });
+
+ const destroy = () => {
+ cleanupFns.forEach((cleanup) => cleanup());
+ };
+
+ return { destroy };
+}