Files
nexus/partials/landingpages/dashboard.php
2026-06-04 22:07:25 +02:00

348 lines
16 KiB
PHP

<?php
declare(strict_types=1);
require_auth();
$service = dashboards();
$ownerKey = auth_user_key();
$groups = auth_groups();
$notice = null;
$error = null;
if (!$service->available() || $ownerKey === '') {
echo '<div class="module-shell"><div class="module-page-bg"><div class="module-page-stack"><section class="section-box">Dashboard-System nicht verfügbar.</section></div></div></div>';
return;
}
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$action = trim((string) ($_POST['action'] ?? ''));
try {
if ($action === 'add_item') {
$dashboardId = (int) ($_POST['dashboard_id'] ?? 0);
$itemType = trim((string) ($_POST['item_type'] ?? 'link'));
$config = [];
if ($itemType === 'page_module') {
$config['page_module_id'] = (int) ($_POST['page_module_id'] ?? 0);
} elseif ($itemType === 'app') {
$config['app_id'] = (int) ($_POST['app_id'] ?? 0);
} elseif ($itemType === 'widget_template') {
$config['widget_template_id'] = (int) ($_POST['widget_template_id'] ?? 0);
} elseif ($itemType === 'bookmark_group') {
$config['bookmarks'] = trim((string) ($_POST['bookmarks'] ?? ''));
} else {
$config['url'] = trim((string) ($_POST['target_url'] ?? ''));
}
$service->createItem($dashboardId, $ownerKey, [
'item_type' => $itemType,
'title' => trim((string) ($_POST['title'] ?? '')),
'description' => trim((string) ($_POST['description'] ?? '')),
'grid_column' => trim((string) ($_POST['grid_column'] ?? '')),
'grid_row' => trim((string) ($_POST['grid_row'] ?? '')),
'column_span' => (int) ($_POST['column_span'] ?? 1),
'row_span' => (int) ($_POST['row_span'] ?? 1),
'config' => $config,
]);
$notice = 'Dashboard-Element hinzugefügt.';
} elseif ($action === 'delete_item') {
$service->deleteItem((int) ($_POST['item_id'] ?? 0), (int) ($_POST['dashboard_id'] ?? 0), $ownerKey);
$notice = 'Dashboard-Element entfernt.';
}
} catch (\Throwable $exception) {
$error = $exception->getMessage();
}
}
$accessibleDashboards = $service->listAccessibleDashboards($ownerKey, $groups);
$selectedDashboardId = (int) ($_GET['id'] ?? 0);
$currentDashboard = null;
foreach ($accessibleDashboards as $dashboard) {
if ((int) ($dashboard['id'] ?? 0) === $selectedDashboardId) {
$currentDashboard = $dashboard;
break;
}
}
if ($currentDashboard === null) {
$currentDashboard = $service->ensureDefaultDashboard($ownerKey, 'Mein Dashboard');
}
$currentDashboardId = (int) ($currentDashboard['id'] ?? 0);
$dashboardItems = $service->listItems($currentDashboardId);
$ownPageModules = $service->listPageModulesForOwner($ownerKey);
$availableApps = $service->listApps($ownerKey, true);
$availableWidgetTemplates = $service->listWidgetTemplates($ownerKey, true);
$pageModuleMap = [];
foreach ($ownPageModules as $pageModule) {
$pageModuleMap[(int) ($pageModule['id'] ?? 0)] = $pageModule;
}
$appMap = [];
foreach ($availableApps as $appEntry) {
$appMap[(int) ($appEntry['id'] ?? 0)] = $appEntry;
}
$widgetTemplateMap = [];
foreach ($availableWidgetTemplates as $template) {
$widgetTemplateMap[(int) ($template['id'] ?? 0)] = $template;
}
$GLOBALS['layout_header_base_title'] = 'Nexus';
$GLOBALS['layout_header_title'] = 'Nexus';
$GLOBALS['layout_header_context'] = 'Dashboard';
$GLOBALS['layout_header_text'] = 'Persönliche Arbeitsfläche mit frei platzierbaren Dashboard-Elementen.';
?>
<div class="module-shell"><div class="module-page-bg"><div class="module-page-stack">
<header class="module-hero submenu-box">
<div class="module-hero-top module-hero-top--compact">
<nav class="module-tabs" aria-label="Dashboard Navigation">
<a class="module-button module-button--tab-active" href="/dashboard">Dashboard</a>
<a class="module-button module-button--tab" href="/dashboards">Dashboards</a>
<a class="module-button module-button--tab" href="/integrations">Integrationen</a>
<a class="module-button module-button--tab" href="/page-modules">Seitenmodule</a>
<?php if (auth_is_admin()): ?>
<a class="module-button module-button--tab" href="/modules">Aktive Module</a>
<?php endif; ?>
</nav>
<div class="module-hero-actions module-submenu-actions">
<a class="module-button module-button--secondary module-button--small" href="/">Nexus Übersicht</a>
<a class="module-button module-button--secondary module-button--small" href="/settings">Nexus Einstellungen</a>
</div>
</div>
</header>
<?php if ($error !== null): ?>
<section class="section-box"><?= e($error) ?></section>
<?php elseif ($notice !== null): ?>
<section class="section-box"><?= e($notice) ?></section>
<?php endif; ?>
<section class="section-box">
<h2><?= e((string) ($currentDashboard['title'] ?? 'Dashboard')) ?></h2>
<p class="muted"><?= e((string) ($currentDashboard['description'] ?? 'Dein aktuelles Standard-Dashboard.')) ?></p>
<div class="setup-grid">
<label class="setup-field muted">
<span>Aktives Dashboard</span>
<select onchange="if (this.value) window.location.href='/dashboard?id=' + this.value;">
<?php foreach ($accessibleDashboards as $dashboard): ?>
<option value="<?= (int) ($dashboard['id'] ?? 0) ?>" <?= (int) ($dashboard['id'] ?? 0) === $currentDashboardId ? 'selected' : '' ?>>
<?= e((string) ($dashboard['title'] ?? 'Dashboard')) ?>
</option>
<?php endforeach; ?>
</select>
</label>
</div>
</section>
<section class="section-box">
<h2>Element hinzufügen</h2>
<p class="muted">Verfügbar sind direkte Links, iFrames, persönliche Linklisten, globale Apps, Seitenmodule und wiederverwendbare Widgets.</p>
<form method="post" class="setup-form">
<input type="hidden" name="action" value="add_item">
<input type="hidden" name="dashboard_id" value="<?= $currentDashboardId ?>">
<div class="setup-grid">
<label class="setup-field muted">
<span>Titel</span>
<input type="text" name="title" required>
</label>
<label class="setup-field muted">
<span>Typ</span>
<select name="item_type" data-dashboard-item-type>
<option value="link">Link</option>
<option value="iframe">iFrame</option>
<option value="bookmark_group">Linkliste</option>
<option value="app">App</option>
<option value="page_module">Seitenmodul</option>
<option value="widget_template">Widget-Vorlage</option>
</select>
</label>
</div>
<div class="setup-grid">
<label class="setup-field muted">
<span>Beschreibung</span>
<input type="text" name="description">
</label>
<label class="setup-field muted" data-dashboard-target-url>
<span>Ziel-URL</span>
<input type="url" name="target_url" placeholder="https://...">
</label>
<label class="setup-field muted" data-dashboard-bookmarks hidden>
<span>Linkliste</span>
<textarea name="bookmarks" rows="5" placeholder="Bezeichnung | https://ziel.example&#10;Nexus | /dashboard"></textarea>
</label>
<label class="setup-field muted" data-dashboard-app hidden>
<span>App</span>
<select name="app_id">
<option value="0">Bitte wählen</option>
<?php foreach ($availableApps as $appEntry): ?>
<option value="<?= (int) ($appEntry['id'] ?? 0) ?>"><?= e((string) ($appEntry['name'] ?? 'App')) ?></option>
<?php endforeach; ?>
</select>
</label>
<label class="setup-field muted" data-dashboard-page-module hidden>
<span>Seitenmodul</span>
<select name="page_module_id">
<option value="0">Bitte wählen</option>
<?php foreach ($ownPageModules as $pageModule): ?>
<option value="<?= (int) ($pageModule['id'] ?? 0) ?>"><?= e((string) ($pageModule['title'] ?? 'Seitenmodul')) ?></option>
<?php endforeach; ?>
</select>
</label>
<label class="setup-field muted" data-dashboard-widget-template hidden>
<span>Widget-Vorlage</span>
<select name="widget_template_id">
<option value="0">Bitte wählen</option>
<?php foreach ($availableWidgetTemplates as $template): ?>
<option value="<?= (int) ($template['id'] ?? 0) ?>"><?= e((string) ($template['name'] ?? 'Widget')) ?></option>
<?php endforeach; ?>
</select>
</label>
</div>
<div class="setup-grid">
<label class="setup-field muted">
<span>Spaltenbreite</span>
<select name="column_span">
<option value="1">1</option>
<option value="2" selected>2</option>
<option value="3">3</option>
<option value="4">4</option>
</select>
</label>
<label class="setup-field muted">
<span>Zeilenhöhe</span>
<select name="row_span">
<option value="1" selected>1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
</label>
<label class="setup-field muted">
<span>Grid-Spalte optional</span>
<input type="number" name="grid_column" min="1" max="4">
</label>
</div>
<div class="setup-actions setup-actions--footer">
<button class="cta-button" type="submit">Element speichern</button>
</div>
</form>
</section>
<?php if ($dashboardItems === []): ?>
<section class="section-box dashboard-empty">Noch keine Elemente vorhanden.</section>
<?php else: ?>
<div class="dashboard-grid">
<?php foreach ($dashboardItems as $item): ?>
<?php
$itemType = (string) ($item['item_type'] ?? 'link');
$config = is_array($item['config'] ?? null) ? $item['config'] : [];
if ($itemType === 'widget_template' && !empty($config['widget_template_id']) && isset($widgetTemplateMap[(int) $config['widget_template_id']])) {
$template = $widgetTemplateMap[(int) $config['widget_template_id']];
$itemType = (string) ($template['widget_type'] ?? $itemType);
$config = array_merge(is_array($template['config'] ?? null) ? $template['config'] : [], $config);
}
$columnSpan = max(1, min(4, (int) ($item['column_span'] ?? 1)));
$rowSpan = max(1, min(4, (int) ($item['row_span'] ?? 1)));
$gridStyles = 'grid-column: span ' . $columnSpan . '; grid-row: span ' . $rowSpan . ';';
if (!empty($item['grid_column'])) {
$gridStyles .= 'grid-column-start:' . (int) $item['grid_column'] . ';';
}
if (!empty($item['grid_row'])) {
$gridStyles .= 'grid-row-start:' . (int) $item['grid_row'] . ';';
}
$pageModule = null;
if ($itemType === 'page_module' && !empty($config['page_module_id'])) {
$pageModule = $pageModuleMap[(int) $config['page_module_id']] ?? null;
}
$appEntry = null;
if ($itemType === 'app' && !empty($config['app_id'])) {
$appEntry = $appMap[(int) $config['app_id']] ?? null;
}
$targetUrl = trim((string) ($config['url'] ?? ''));
if ($pageModule !== null) {
$targetUrl = trim((string) ($pageModule['target_url'] ?? $targetUrl));
}
if ($appEntry !== null) {
$targetUrl = trim((string) ($appEntry['app_url'] ?? $targetUrl));
}
$bookmarks = [];
if ($itemType === 'bookmark_group') {
foreach (preg_split('/\r\n|\r|\n/', trim((string) ($config['bookmarks'] ?? ''))) ?: [] as $line) {
$line = trim($line);
if ($line === '') {
continue;
}
[$label, $url] = array_pad(array_map('trim', explode('|', $line, 2)), 2, '');
if ($label !== '' && $url !== '') {
$bookmarks[] = ['label' => $label, 'url' => $url];
}
}
}
?>
<article class="card-box dashboard-widget" style="<?= e($gridStyles) ?>">
<div class="dashboard-widget__head">
<div>
<span class="module-admin-meta__label"><?= e(strtoupper($itemType)) ?></span>
<h2><?= e((string) ($item['title'] ?? 'Element')) ?></h2>
<?php if (!empty($item['description'])): ?>
<p><?= e((string) $item['description']) ?></p>
<?php endif; ?>
</div>
<form method="post">
<input type="hidden" name="action" value="delete_item">
<input type="hidden" name="dashboard_id" value="<?= $currentDashboardId ?>">
<input type="hidden" name="item_id" value="<?= (int) ($item['id'] ?? 0) ?>">
<button class="module-button module-button--secondary module-button--small" type="submit">Entfernen</button>
</form>
</div>
<?php if ($itemType === 'bookmark_group' && $bookmarks !== []): ?>
<div class="dashboard-links">
<?php foreach ($bookmarks as $bookmark): ?>
<a class="module-button module-button--secondary module-button--small" href="<?= e($bookmark['url']) ?>" target="_blank" rel="noreferrer"><?= e($bookmark['label']) ?></a>
<?php endforeach; ?>
</div>
<?php elseif (($itemType === 'iframe' || ($pageModule !== null && (string) ($pageModule['module_type'] ?? '') === 'iframe')) && $targetUrl !== ''): ?>
<iframe class="dashboard-widget__frame" src="<?= e($targetUrl) ?>" loading="lazy" referrerpolicy="no-referrer"></iframe>
<?php elseif ($appEntry !== null): ?>
<div class="dashboard-widget__meta">
<?php if (!empty($appEntry['icon_url'])): ?>
<img class="dashboard-app-icon" src="<?= e((string) $appEntry['icon_url']) ?>" alt="">
<?php endif; ?>
<p><?= e((string) ($appEntry['description'] ?? $targetUrl)) ?></p>
<a class="module-button module-button--secondary module-button--small" href="<?= e($targetUrl) ?>" target="_blank" rel="noreferrer">App öffnen</a>
</div>
<?php elseif ($pageModule !== null): ?>
<div class="dashboard-widget__meta">
<p><?= e((string) ($pageModule['description'] ?? 'Seitenmodul aus der globalen Nexus-Verwaltung.')) ?></p>
<a class="module-button module-button--secondary module-button--small" href="/page-modules/view/<?= (int) ($pageModule['id'] ?? 0) ?>">Öffnen</a>
</div>
<?php elseif ($targetUrl !== ''): ?>
<div class="dashboard-widget__meta">
<p><?= e($targetUrl) ?></p>
<a class="module-button module-button--secondary module-button--small" href="<?= e($targetUrl) ?>" target="_blank" rel="noreferrer">Öffnen</a>
</div>
<?php else: ?>
<div class="dashboard-widget__meta"><p>Für dieses Element ist noch kein Inhalt hinterlegt.</p></div>
<?php endif; ?>
</article>
<?php endforeach; ?>
</div>
<?php endif; ?>
</div></div></div>
<script>
(() => {
const typeSelect = document.querySelector('[data-dashboard-item-type]');
const urlField = document.querySelector('[data-dashboard-target-url]');
const pageModuleField = document.querySelector('[data-dashboard-page-module]');
const bookmarksField = document.querySelector('[data-dashboard-bookmarks]');
const appField = document.querySelector('[data-dashboard-app]');
const widgetTemplateField = document.querySelector('[data-dashboard-widget-template]');
if (!typeSelect || !urlField || !pageModuleField || !bookmarksField || !appField || !widgetTemplateField) return;
const sync = () => {
const value = typeSelect.value;
urlField.hidden = !['link', 'iframe'].includes(value);
pageModuleField.hidden = value !== 'page_module';
bookmarksField.hidden = value !== 'bookmark_group';
appField.hidden = value !== 'app';
widgetTemplateField.hidden = value !== 'widget_template';
};
typeSelect.addEventListener('change', sync);
sync();
})();
</script>