Add shared auth login flow

This commit is contained in:
2026-06-15 11:20:22 +02:00
parent b648d789e9
commit da29732cba
9 changed files with 883 additions and 1 deletions
+129
View File
@@ -0,0 +1,129 @@
(function initHelpIconOverlayModule() {
const CLOSE_HANDLERS = {
'.sg-pulldown-demo': (root) => {
root.querySelectorAll('.sg-pulldown-demo').forEach((demo) => {
const trigger = demo.querySelector('.sg-pulldown-demo__trigger');
demo.dataset.open = 'false';
if (trigger) {
trigger.setAttribute('aria-expanded', 'false');
}
});
},
'.sg-sandwich-menu-wrap': (root) => {
root.querySelectorAll('.sg-sandwich-menu-wrap').forEach((wrap) => {
const button = wrap.querySelector('.sg-sandwich-button');
wrap.dataset.open = 'false';
if (button) {
button.setAttribute('aria-expanded', 'false');
}
});
},
};
const getViewportWidth = () => {
if (window.visualViewport && typeof window.visualViewport.width === 'number') {
return window.visualViewport.width;
}
return window.innerWidth;
};
const getSafeInsetPx = () => {
const rootStyles = getComputedStyle(document.documentElement);
const spacingSmallRaw = rootStyles.getPropertyValue('--spacing-small').trim();
const rootFontSize = parseFloat(rootStyles.fontSize) || 16;
const spacingSmallValue = parseFloat(spacingSmallRaw);
if (Number.isNaN(spacingSmallValue)) {
return 0;
}
if (spacingSmallRaw.endsWith('rem')) {
return spacingSmallValue * rootFontSize;
}
return spacingSmallValue;
};
const closeAllHelpIcons = (root) => {
root.querySelectorAll('.sg-help-icon-wrap').forEach((wrap) => {
const button = wrap.querySelector('.sg-help-icon');
const panel = wrap.querySelector('.sg-help-icon-panel');
wrap.dataset.open = 'false';
if (panel) {
panel.style.removeProperty('transform');
}
if (button) {
button.setAttribute('aria-expanded', 'false');
}
});
};
window.sgInitHelpIconOverlays = (options = {}) => {
const root = options.root || document;
const closeOnOpenSelectors = options.closeOnOpenSelectors || [];
const outsideClickIgnoreSelectors = options.outsideClickIgnoreSelectors || [];
root.querySelectorAll('.sg-help-icon-wrap').forEach((wrap) => {
if (wrap.dataset.helpIconInit === 'true') {
return;
}
wrap.dataset.helpIconInit = 'true';
const button = wrap.querySelector('.sg-help-icon');
const panel = wrap.querySelector('.sg-help-icon-panel');
if (!button || !panel) {
return;
}
button.addEventListener('click', (event) => {
event.stopPropagation();
const nextState = wrap.dataset.open !== 'true';
closeAllHelpIcons(root);
closeOnOpenSelectors.forEach((selector) => {
const handler = CLOSE_HANDLERS[selector];
if (handler) {
handler(root);
}
});
if (!nextState) {
return;
}
wrap.dataset.align = 'left';
wrap.dataset.open = 'true';
button.setAttribute('aria-expanded', 'true');
const viewportWidth = getViewportWidth();
const panelRect = panel.getBoundingClientRect();
if (panelRect.right > viewportWidth) {
wrap.dataset.align = 'right';
}
const alignedPanelRect = panel.getBoundingClientRect();
if (alignedPanelRect.left < 0) {
wrap.dataset.align = 'left';
}
const clampedRect = panel.getBoundingClientRect();
const safeInset = getSafeInsetPx();
let shiftX = 0;
if (clampedRect.right > (viewportWidth - safeInset)) {
shiftX -= clampedRect.right - (viewportWidth - safeInset);
}
if ((clampedRect.left + shiftX) < safeInset) {
shiftX += safeInset - (clampedRect.left + shiftX);
}
if (shiftX !== 0) {
panel.style.transform = `translateX(${shiftX}px)`;
}
});
});
document.addEventListener('click', (event) => {
const isInsideIgnoredZone = ['.sg-help-icon-wrap', ...outsideClickIgnoreSelectors]
.some((selector) => event.target.closest(selector));
if (isInsideIgnoredZone) {
return;
}
closeAllHelpIcons(root);
});
};
})();
+56
View File
@@ -0,0 +1,56 @@
<?php
declare(strict_types=1);
require_once __DIR__ . '/../modules/shared/auth/service.php';
require_once __DIR__ . '/../modules/shared/auth/ui/login.php';
require_once __DIR__ . '/../modules/shared/auth/ui/home.php';
$env = expand_env_values(parse_env_file(__DIR__ . '/../.env'));
$pdo = connect_database($env);
auth_bootstrap_session();
auth_ensure_schema($pdo);
if (($_SERVER['REQUEST_METHOD'] ?? 'GET') === 'POST') {
$csrfToken = (string) ($_POST['csrf_token'] ?? '');
if (!auth_validate_csrf_token($csrfToken)) {
render_auth_login_page([
'identifier_value' => (string) ($_POST['identifier'] ?? ''),
'errors' => [
'identifier' => 'Ungültiges Sicherheits-Token. Bitte Seite neu laden.',
'password' => null,
],
]);
exit;
}
$loginResult = auth_login(
$pdo,
(string) ($_POST['identifier'] ?? ''),
(string) ($_POST['password'] ?? '')
);
if (($loginResult['ok'] ?? false) === true) {
header('Location: ' . auth_take_return_to());
exit;
}
render_auth_login_page([
'identifier_value' => (string) ($_POST['identifier'] ?? ''),
'errors' => $loginResult['errors'] ?? [],
]);
exit;
}
$currentUser = auth_current_user($pdo);
if ($currentUser !== null) {
render_auth_home_page($currentUser);
exit;
}
render_auth_login_page([
'identifier_value' => '',
'errors' => [
'identifier' => null,
'password' => null,
],
]);
+9
View File
@@ -0,0 +1,9 @@
<?php
declare(strict_types=1);
require_once __DIR__ . '/../modules/shared/auth/service.php';
auth_bootstrap_session();
auth_logout();
header('Location: /');
exit;
+8
View File
@@ -1,4 +1,12 @@
<?php
declare(strict_types=1);
require_once __DIR__ . '/../../modules/shared/auth/service.php';
$env = expand_env_values(parse_env_file(__DIR__ . '/../../.env'));
$pdo = connect_database($env);
auth_bootstrap_session();
auth_ensure_schema($pdo);
auth_require_user($pdo);
require_once __DIR__ . '/../../modules/erp/direktverkauf/ui/index.php';