big update

This commit is contained in:
2025-12-10 01:05:55 +01:00
parent db4e718d85
commit e59ceaad99
12 changed files with 667 additions and 344 deletions

View File

@@ -0,0 +1,40 @@
<?php
// partials/structure/app_config.php
// Gemeinsame Basisparameter für die Landingpages im Emailtemplate-Projekt.
if (!isset($layoutContext) || !is_array($layoutContext)) {
$scheme = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https' : 'http';
$host = $_SERVER['HTTP_HOST'] ?? 'localhost';
$layoutContext = [
'app_base_url' => rtrim($GLOBALS['app_base_url'] ?? ($scheme . '://' . $host), '/'),
'app_api_base' => rtrim($GLOBALS['app_api_base'] ?? ($scheme . '://' . $host . '/api'), '/'),
'asset_version' => defined('ASSET_VERSION') ? ASSET_VERSION : time(),
'app_env' => $GLOBALS['app_env'] ?? (defined('APP_ENV') ? APP_ENV : 'prod'),
];
$layoutContext['asset_base'] = $layoutContext['app_base_url'] ?: '';
$layoutContext['debug_redirect'] = isset($_GET['debug_redirect']);
$GLOBALS['layoutContext'] = $layoutContext;
}
$GLOBALS['layoutContext'] = $layoutContext;
$appBaseUrl = $layoutContext['app_base_url'];
$appApiBase = $layoutContext['app_api_base'];
$assetBase = $layoutContext['asset_base'];
$assetVersion = $layoutContext['asset_version'];
$debugRedirect = $layoutContext['debug_redirect'];
if (!function_exists('app_asset_url')) {
function app_asset_url(string $path, ?int $version = null): string
{
$base = $GLOBALS['layoutContext']['asset_base'] ?? '';
$version = $version ?? ($GLOBALS['layoutContext']['asset_version'] ?? null);
$url = rtrim($base, '/') . $path;
if ($version !== null) {
$url .= (strpos($url, '?') === false ? '?' : '&') . 'v=' . rawurlencode((string)$version);
}
return $url;
}
}

View File

@@ -0,0 +1,15 @@
<?php
$version = $GLOBALS['app_version'] ?? null;
?>
<footer class="app-footer border-t border-slate-200 bg-white mt-8">
<div class="max-w-6xl mx-auto px-4 py-6 flex flex-wrap items-center gap-3 text-sm text-slate-500">
<span>© <?= date('Y') ?> Email Template System</span>
<span class="ms-auto inline-flex items-center gap-1 text-slate-600">
<span class="text-xs uppercase tracking-[0.2em] text-slate-400">Version</span>
<strong><?= htmlspecialchars($version ?? '—') ?></strong>
</span>
</div>
</footer>
<?php if ($version): ?>
<div class="app-version-fixed">v <?= htmlspecialchars($version) ?></div>
<?php endif; ?>

View File

@@ -0,0 +1,47 @@
<?php
/** @var array $layoutContext */
$appBaseUrl = $layoutContext['app_base_url'] ?? '';
$navLinks = $navLinks ?? [
['id' => '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'],
];
$navActive = $navActive ?? null;
?>
<header class="site-header sticky top-0 z-40 border-b border-slate-200 bg-white/90 backdrop-blur">
<div class="max-w-6xl mx-auto px-4 lg:px-6 flex items-center gap-4 py-3">
<a href="<?= htmlspecialchars($appBaseUrl . '/index.php') ?>" class="flex items-center gap-2 text-slate-800 font-semibold">
<span class="inline-flex h-9 w-9 items-center justify-center rounded-xl bg-sky-500 text-white font-bold">ET</span>
<span class="hidden sm:block">Email Template System</span>
</a>
<nav class="ms-auto hidden md:flex items-center gap-2 text-sm font-medium text-slate-500">
<?php foreach ($navLinks as $link): ?>
<?php
$isActive = $navActive && $navActive === ($link['id'] ?? null);
$classes = 'px-3 py-1.5 rounded-full transition-colors';
$classes .= $isActive ? ' bg-sky-100 text-sky-700' : ' hover:text-slate-900';
?>
<a href="<?= htmlspecialchars($link['href']) ?>" class="<?= $classes ?>">
<?= htmlspecialchars($link['label']) ?>
</a>
<?php endforeach; ?>
</nav>
<div class="relative ms-auto md:ms-4" id="userMenu">
<button id="btn-user" type="button" class="btn-avatar" aria-haspopup="true" aria-expanded="false">
<span id="userAvatar">U</span>
</button>
<div id="userMenuPanel" class="user-menu hidden" role="menu">
<a href="<?= htmlspecialchars($appBaseUrl . '/admin/profile.php') ?>" class="user-menu-item" data-menu="profile">Profil</a>
<a href="<?= htmlspecialchars($appBaseUrl . '/admin/dashboard.php') ?>" class="user-menu-item" data-role="admin">Dashboard</a>
<a href="<?= htmlspecialchars($appBaseUrl . '/admin/settings.php') ?>" class="user-menu-item" data-role="admin">Administration</a>
<a href="<?= htmlspecialchars($appBaseUrl . '/admin/bridge.php') ?>" class="user-menu-item" data-role="admin">Bridge Setup</a>
<button id="btn-logout" type="button" class="user-menu-item text-red-600">Logout</button>
</div>
</div>
</div>
</header>

View File

@@ -0,0 +1,27 @@
<?php
$layoutScripts = $layoutScripts ?? [];
if (!is_array($layoutScripts)) {
$layoutScripts = [$layoutScripts];
}
?>
<?php require __DIR__ . '/footer.php'; ?>
<?php foreach ($layoutScripts as $script): ?>
<?php if (is_string($script)): ?>
<?= $script . PHP_EOL ?>
<?php elseif (is_array($script) && isset($script['src'])): ?>
<?php
$attrs = [];
if (!empty($script['defer'])) {
$attrs[] = 'defer';
}
if (!empty($script['module'])) {
$attrs[] = 'type="module"';
} elseif (!empty($script['type'])) {
$attrs[] = 'type="' . htmlspecialchars($script['type'], ENT_QUOTES) . '"';
}
?>
<script src="<?= htmlspecialchars($script['src'], ENT_QUOTES) ?>" <?= implode(' ', $attrs) ?>></script>
<?php endif; ?>
<?php endforeach; ?>
</body>
</html>

View File

@@ -0,0 +1,57 @@
<?php
require_once __DIR__ . '/app_config.php';
$pageTitle = $pageTitle ?? 'Email Template System';
$bodyClass = trim(($bodyClass ?? 'bg-slate-50 text-slate-800') . ' page-shell');
$pageId = $pageId ?? null;
$navActive = $navActive ?? null;
$layoutExtraHead = $layoutExtraHead ?? [];
if (!is_array($layoutExtraHead)) {
$layoutExtraHead = [$layoutExtraHead];
}
$sharedCss = [
app_asset_url('/assets/css/admin.css'),
app_asset_url('/assets/css/toast.css'),
];
?>
<!doctype html>
<html lang="de">
<head>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<title><?= htmlspecialchars($pageTitle) ?></title>
<script>document.documentElement.classList.add('auth-pending');</script>
<style>html.auth-pending body{visibility:hidden;}</style>
<script>
window.APP_BASE_URL = <?= json_encode($appBaseUrl, JSON_UNESCAPED_SLASHES) ?>;
window.APP_API_BASE = <?= json_encode($appApiBase, JSON_UNESCAPED_SLASHES) ?>;
<?php if ($debugRedirect): ?>
window.DISABLE_AUTH_REDIRECT = true;
<?php endif; ?>
</script>
<script src="https://cdn.tailwindcss.com"></script>
<?php if ($debugRedirect): ?>
<script src="<?= app_asset_url('/assets/js/debug-location.js') ?>"></script>
<?php endif; ?>
<?php foreach ($sharedCss as $href): ?>
<link rel="stylesheet" href="<?= htmlspecialchars($href, ENT_QUOTES) ?>">
<?php endforeach; ?>
<style>
.btn{display:inline-flex;align-items:center;gap:.4rem;padding:.35rem .7rem;border-radius:.75rem;border:1px solid #e5e7eb;background:#fff;font-size:.9rem;cursor:pointer;}
.btn:hover{background:#f8fafc}
.btn-avatar{padding:.35rem;border-radius:999px;width:42px;height:42px;justify-content:center;font-weight:600;background:#0ea5e9;color:#fff;border:none}
.btn-avatar:hover{background:#0284c7}
.user-menu{position:absolute;top:calc(100% + .5rem);right:0;min-width:200px;background:#fff;border:1px solid #e2e8f0;border-radius:.9rem;box-shadow:0 20px 35px rgba(15,23,42,.15);padding:.4rem;z-index:40}
.user-menu-item{display:block;width:100%;text-align:left;padding:.45rem .75rem;border-radius:.65rem;font-size:.9rem;color:#0f172a}
.user-menu-item:hover{background:#f1f5f9}
.page-shell{min-height:100vh;display:flex;flex-direction:column;}
.app-version-fixed{position:fixed;right:12px;bottom:12px;z-index:2147483000;font-size:12px;font-family:system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;color:#0f172a;background:rgba(248,250,252,.85);border:1px solid rgba(148,163,184,.6);border-radius:999px;padding:4px 10px;box-shadow:0 8px 20px rgba(15,23,42,.15);backdrop-filter:blur(6px);}
@media print {.app-version-fixed{display:none}}
</style>
<?php foreach ($layoutExtraHead as $snippet): ?>
<?= $snippet . PHP_EOL ?>
<?php endforeach; ?>
</head>
<body class="<?= htmlspecialchars($bodyClass) ?>" <?= $pageId ? 'data-page="' . htmlspecialchars($pageId) . '"' : '' ?>>
<?php require __DIR__ . '/header.php'; ?>

View File

@@ -0,0 +1,39 @@
<?php if (!defined('MATOMO_SITE_ID')) return; ?>
<?php if (!defined('MATOMO_ENABLED') || !MATOMO_ENABLED) return; ?>
<?php
$matomoDomains = [];
$primaryDomain = app_primary_domain();
$fakecheckDomain = app_fakecheck_domain();
$matomoDomains[] = '*.' . $primaryDomain;
$matomoDomains[] = '*.' . $fakecheckDomain;
$matomoDomains[] = '*.' . $primaryDomain . '/fakecheck';
?>
<!-- Matomo -->
<script>
var _paq = window._paq = window._paq || [];
_paq.push(["setDomains", <?= json_encode($matomoDomains, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) ?>]);
_paq.push(['trackPageView']);
_paq.push(['enableLinkTracking']);
(function() {
var u = "<?= rtrim(MATOMO_URL, '/') ?>/";
_paq.push(['setTrackerUrl', u + 'matomo.php']);
_paq.push(['setSiteId', '<?= MATOMO_SITE_ID ?>']);
var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0];
g.async=true;
g.src=u + 'matomo.js';
s.parentNode.insertBefore(g,s);
})();
</script>
<noscript>
<p>
<img referrerpolicy="no-referrer-when-downgrade"
src="<?= rtrim(MATOMO_URL,'/') ?>/matomo.php?idsite=<?= MATOMO_SITE_ID ?>&amp;rec=1"
style="border:0;" alt="" />
</p>
</noscript>
<!-- End Matomo -->