@@ -123,9 +119,180 @@ require dirname(__DIR__) . '/../structure/layout_start.php';
+
+
+
+
+
Bridge-Datei & Tabellenfreigaben
+
Die Bridge-Konfiguration dient ausschliesslich der Erstellung der Bridge-Datei und steuert, welche Tabellen dort freigegeben sind.
+
+
+
+
+
+
+
+
+
+
Freigegebene Tabellen (Bridge-Datei)
+
+
+
+
+
+
+
+
Tabellen fuer Placeholder-Auswahl
+
+
+
+ Nur diese Tabellen erscheinen spaeter bei der Placeholder-Auswahl.
+
+
+
+
+
'dashboard', 'label' => 'Dashboard', 'href' => $appBaseUrl . '/admin/dashboard.php'],
- ['id' => 'settings', 'label' => 'Administration','href' => $appBaseUrl . '/admin/settings.php'],
- ['id' => 'bridge', 'label' => 'Bridge Setup', 'href' => $appBaseUrl . '/admin/bridge.php'],
- ['id' => 'profile', 'label' => 'Mein Konto', 'href' => $appBaseUrl . '/admin/profile.php'],
+ ['id' => 'dashboard', 'label' => 'Dashboard', 'href' => $appBaseUrl . '/admin/dashboard.php'],
+ ['id' => 'settings', 'label' => 'API & Tabellen', 'href' => $appBaseUrl . '/admin/settings.php'],
+ ['id' => 'profile', 'label' => 'Mein Konto', 'href' => $appBaseUrl . '/admin/profile.php'],
];
$navActive = $navActive ?? null;
@@ -48,8 +47,7 @@ $showNavLinks = !$hasHeaderTabs && !empty($navLinks);
diff --git a/public/assets/js/account.js b/public/assets/js/account.js
index 55ca390..3bbd21e 100644
--- a/public/assets/js/account.js
+++ b/public/assets/js/account.js
@@ -1,5 +1,6 @@
import { apiAction } from './api.js';
import { initUserPanel, initAccountPage } from './ui-user.js';
+import { initBridgeSetupPage } from './bridge-setup-page.js';
import { mountLogoutButton, ensureFloatingLogout } from './ui-auth.js';
async function ensureAuthenticated() {
@@ -24,6 +25,7 @@ document.addEventListener('DOMContentLoaded', async () => {
if (!ok) return;
initUserPanel();
initAccountPage();
+ initBridgeSetupPage();
mountLogoutButton('#btn-logout', { redirect: '/login.php' });
ensureFloatingLogout({ redirect: '/login.php' });
});
diff --git a/public/assets/js/bridge-setup-page.js b/public/assets/js/bridge-setup-page.js
index 7f26ad0..fe2c943 100644
--- a/public/assets/js/bridge-setup-page.js
+++ b/public/assets/js/bridge-setup-page.js
@@ -3,11 +3,15 @@ import { apiAction, toast } from './api.js';
const state = {
setup: null,
loading: false,
+ allTables: [],
+ selectedTables: [],
};
let form;
-let tablesInput;
-let tablesPreview;
+let tablesAllSelect;
+let tablesSelectedSelect;
+let tablesAddBtn;
+let tablesRemoveBtn;
let modeInputs;
let directFields;
let configFields;
@@ -15,12 +19,18 @@ let statusLabel;
let loadBtn;
let configExampleBtn;
let configExampleDialog;
+let bridgeSetupDialog;
+let openBridgeSetupBtn;
+let adminLoadBridgeBtn;
+let closeBridgeSetupBtn;
export function initBridgeSetupPage() {
form = document.getElementById('bridgeSetupForm');
if (!form) return;
- tablesInput = form.elements.tables;
- tablesPreview = document.getElementById('selectedTables');
+ tablesAllSelect = document.getElementById('bridgeTablesAll');
+ tablesSelectedSelect = document.getElementById('bridgeTablesSelected');
+ tablesAddBtn = document.getElementById('bridgeTablesAdd');
+ tablesRemoveBtn = document.getElementById('bridgeTablesRemove');
directFields = document.getElementById('directFields');
configFields = document.getElementById('configFields');
statusLabel = document.getElementById('setupStatus');
@@ -28,13 +38,33 @@ export function initBridgeSetupPage() {
configExampleBtn = document.getElementById('btn-config-example');
configExampleDialog = document.getElementById('configExampleDialog');
modeInputs = Array.from(form.querySelectorAll('input[name="db_mode"]'));
+ bridgeSetupDialog = document.getElementById('bridgeSetupDialog');
+ openBridgeSetupBtn = document.getElementById('btn-open-bridge-setup');
+ adminLoadBridgeBtn = document.getElementById('btn-admin-load-bridge');
+ closeBridgeSetupBtn = document.getElementById('btn-close-bridge-setup');
form.addEventListener('submit', submitBridgeSetup);
- tablesInput?.addEventListener('input', () => updateTablesPreview(parseTablesInput()));
+ tablesAddBtn?.addEventListener('click', () => addSelectedTables(getSelectedOptions(tablesAllSelect)));
+ tablesRemoveBtn?.addEventListener('click', () => removeSelectedTables(getSelectedOptions(tablesSelectedSelect)));
loadBtn?.addEventListener('click', loadTablesFromBridge);
configExampleBtn?.addEventListener('click', () => {
if (configExampleDialog?.showModal) configExampleDialog.showModal();
});
+ openBridgeSetupBtn?.addEventListener('click', () => {
+ if (bridgeSetupDialog?.showModal) bridgeSetupDialog.showModal();
+ if (!state.allTables.length) {
+ loadTablesFromBridge(null, { preserveSelection: state.selectedTables.length > 0 });
+ }
+ });
+ closeBridgeSetupBtn?.addEventListener('click', () => {
+ if (bridgeSetupDialog?.open) bridgeSetupDialog.close();
+ });
+ adminLoadBridgeBtn?.addEventListener('click', (ev) => {
+ if (bridgeSetupDialog?.showModal && !bridgeSetupDialog.open) {
+ bridgeSetupDialog.showModal();
+ }
+ loadTablesFromBridge(ev, { preserveSelection: state.selectedTables.length > 0 });
+ });
modeInputs.forEach(input => {
input.addEventListener('change', () => applyModeVisibility(input.value));
});
@@ -82,12 +112,12 @@ async function loadBridgeSetup() {
}
}
-function fillForm(setup) {
+function fillForm(setup, options = {}) {
const data = normalizeSetupInput(setup);
state.setup = data;
- if (tablesInput) {
- tablesInput.value = data.tables.join(', ');
- updateTablesPreview(data.tables);
+ if (!options.skipTables) {
+ state.selectedTables = data.tables.slice();
+ updateTableSelects();
}
const activeMode = (data.mode || 'direct').toLowerCase();
modeInputs.forEach(input => {
@@ -135,22 +165,50 @@ function applyModeVisibility(mode) {
configFields?.classList[config]('hidden');
}
-function parseTablesInput() {
- if (!tablesInput) return [];
- return tablesInput.value
- .split(/[\s,]+/)
- .map(part => part.trim())
- .filter(Boolean)
- .filter((value, index, arr) => arr.indexOf(value) === index);
+function updateTableSelects() {
+ const all = state.allTables || [];
+ const selected = state.selectedTables || [];
+ const selectedSet = new Set(selected);
+ const orderedSelected = all.length ? all.filter(name => selectedSet.has(name)) : selected;
+ const available = all.length ? all.filter(name => !selectedSet.has(name)) : [];
+ renderSelect(tablesAllSelect, available, 'Noch keine Tabellen geladen.');
+ renderSelect(tablesSelectedSelect, orderedSelected, 'Noch keine Tabellen ausgewaehlt.');
}
-function updateTablesPreview(list) {
- if (!tablesPreview) return;
+function renderSelect(selectEl, list, emptyLabel) {
+ if (!selectEl) return;
+ selectEl.innerHTML = '';
if (!list.length) {
- tablesPreview.innerHTML = '