Files
Assetmanager/app/templates/base.html
T

127 lines
12 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{% set cfg = application_config() %}{% set appinfo = application_info() %}{% set general = cfg.get('general', {}) %}{% set colors = general.get('colors', {}) %}{% set auth = cfg.get('authentication', {}) %}{% set session_user = request.session.get('user') if request else None %}{% set current_language = (session_user.language_code if session_user else request.session.get('language_code', general.get('default_language', 'en'))) %}{% set theme_mode = session_user.theme_mode if session_user and session_user.theme_mode in ['light','dark'] else general.get('default_theme', 'light') %}
<!doctype html><html lang="{{ current_language }}" data-theme="{{ theme_mode }}"{% if request and request.url.path == '/assets' %} class="asset-page-booting"{% endif %}><head>
<style id="system-theme-colors">
:root{
--am-primary:{{ colors.get('primary_color', '#0878bd') }};
--am-primary-hover:{{ colors.get('primary_hover_color', '#075f94') }};
--am-header:{{ colors.get('header_color', '#063c66') }};
--am-navigation:{{ colors.get('navigation_color', '#ffffff') }};
--am-page-bg:{{ colors.get('page_background', '#f2f4f6') }};
--am-surface:{{ colors.get('surface_color', '#ffffff') }};
--am-text:{{ colors.get('text_color', '#1f2933') }};
--am-border:{{ colors.get('border_color', '#d7dde3') }};
--am-success:{{ colors.get('success_color', '#198754') }};
--am-warning:{{ colors.get('warning_color', '#d97706') }};
--am-danger:{{ colors.get('danger_color', '#b42318') }};
}
</style><script>(()=>{try{if(localStorage.getItem('assetmanager.sidebar.collapsed')==='true')document.documentElement.classList.add('sidebar-collapsed-preset')}catch(_){}})();</script><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>{{ title or general.get('title', 'AssetManager') }}</title>{% set favicon_path = general.get('favicon') or general.get('logo') %}{% if favicon_path %}<link rel="icon" href="{{ favicon_path }}">{% endif %}<link rel="stylesheet" href="/static/css/app.css?v={{ application_version() }}"><link rel="stylesheet" href="/custom.css"></head><body{% if request and request.url.path == '/assets' %} class="asset-list-scroll-page"{% endif %} data-record-count-template="{{ t('lists.record_count', 'Angezeigt: {visible} von {total} Datensätzen')|e }}" data-record-count-title="{{ t('lists.record_count_hint', 'Die Anzeige berücksichtigt die aktuell gesetzten Tabellenfilter.')|e }}" data-filter-active-label="{{ t('lists.filter_active', 'Filter aktiv')|e }}" data-filters-active-template="{{ t('lists.filters_active', '{count} Filter aktiv')|e }}" data-filter-status-template="{{ t('lists.filter_status', 'Gefiltert: {visible} von {total} Datensätzen')|e }}" data-clear-filters-label="{{ t('lists.clear_filters', 'Filter zurücksetzen')|e }}" data-persist-browser-state="{{ 'true' if session_user and session_user.persist_browser_state else 'false' }}" data-toast-position="{{ session_user.toast_position if session_user else 'top-right' }}" data-toast-duration="{{ session_user.toast_duration_seconds if session_user else 6 }}">
<header class="main-header">
<a class="brand" href="/">{% if general.get('logo') %}<img src="{{ general.get('logo') }}" alt="Logo">{% endif %}<span>{{ general.get('title', 'AssetManager') }}</span>{% if general.get('company_name') %}<span class="brand-company"> {{ general.get('company_name') }}</span>{% endif %}</a>
<div class="header-actions">
{% if session_user %}
<form method="post" action="/profile/theme" class="header-theme-form" id="header-theme-form">
<input type="hidden" name="theme_mode" value="{{ 'light' if theme_mode == 'dark' else 'dark' }}">
<input type="hidden" name="next_url" value="{{ request.url.path }}">
<button type="submit" class="header-theme-toggle" title="{{ t('appearance.switch_to_light') if theme_mode == 'dark' else t('appearance.switch_to_dark') }}" aria-label="{{ t('appearance.switch_to_light') if theme_mode == 'dark' else t('appearance.switch_to_dark') }}">
<span class="theme-icon-light" aria-hidden="true"></span>
<span class="theme-icon-dark" aria-hidden="true"></span>
</button>
</form>
{% endif %}
<button type="button" class="header-help-button" id="about-dialog-button" title="{{ t('about.open') }}" aria-label="{{ t('about.open') }}">?</button>
<details class="language-menu"><summary>🌐 {{ current_language|upper }} ▾</summary><div class="user-menu-panel">{% for language in available_languages() %}<form method="post" action="/profile/language"><input type="hidden" name="language_code" value="{{ language.code }}"><input type="hidden" name="next_url" value="{{ request.url.path }}"><button type="submit" class="menu-button{% if language.code == current_language %} active{% endif %}">{{ language.native_name }}</button></form>{% endfor %}</div></details>
<details class="user-menu"><summary>👤 {{ session_user.display_name if session_user else t('app.guest') }} ▾</summary><div class="user-menu-panel">{% if session_user %}<a href="/profile">{{ t('app.profile') }}</a><a href="/logout">{{ t('app.logout') }}</a>{% else %}{% if auth.get('mode', 'none') == 'none' %}<span class="muted">Authentication disabled</span>{% endif %}<a href="/login">{{ t('app.login') }}</a>{% endif %}</div></details></div>
</header>
<dialog id="about-dialog" class="about-dialog">
<form method="dialog">
<div class="dialog-heading">
<h2>{{ general.get('title', 'AssetManager') }}</h2>
<button type="submit" class="icon-button button-secondary" aria-label="{{ t('common.close') }}">×</button>
</div>
{% set fixed_manufacturer_logo = manufacturer_logo_path() %}
{% if fixed_manufacturer_logo %}<div class="about-manufacturer-logo"><img src="{{ fixed_manufacturer_logo }}?v={{ application_version() }}" alt=""></div>{% endif %}
<dl class="about-details">
<dt>{{ t('about.version') }}</dt><dd>{{ application_version() }}</dd>
{% if appinfo.author %}<dt>{{ t('about.author') }}</dt><dd>{{ appinfo.author }}</dd>{% endif %}
{% if appinfo.organization %}<dt>{{ t('about.organization') }}</dt><dd>{{ appinfo.organization }}</dd>{% endif %}
{% if appinfo.contact %}<dt>{{ t('about.contact') }}</dt><dd>{{ appinfo.contact }}</dd>{% endif %}
{% if appinfo.license %}<dt>{{ t('about.license') }}</dt><dd>{{ appinfo.license }}</dd>{% endif %}
{% if appinfo.website %}<dt>{{ t('about.website') }}</dt><dd><a href="{{ appinfo.website }}" target="_blank" rel="noopener">{{ appinfo.website }}</a></dd>{% endif %}
{% if appinfo.repository %}<dt>{{ t('about.repository') }}</dt><dd><a href="{{ appinfo.repository }}" target="_blank" rel="noopener">{{ appinfo.repository }}</a></dd>{% endif %}
{% if appinfo.copyright %}<dt>{{ t('about.copyright') }}</dt><dd>{{ appinfo.copyright }}</dd>{% endif %}
</dl>
<div class="form-actions"><button type="submit">{{ t('common.close') }}</button></div>
</form>
</dialog>
<script>
(() => {
const button = document.getElementById('about-dialog-button');
const dialog = document.getElementById('about-dialog');
button?.addEventListener('click', () => dialog?.showModal());
})();
</script>
<div class="app-shell" id="app-shell">
<aside class="side-nav" id="side-nav" aria-label="{{ t('navigation.main', 'Hauptnavigation') }}">
<button type="button" class="side-nav-toggle" id="side-nav-toggle" title="{{ t('navigation.toggle', 'Navigation ein-/ausklappen') }}" aria-label="{{ t('navigation.toggle', 'Navigation ein-/ausklappen') }}"></button>
<a href="/" title="{{ t('app.dashboard') }}"><span class="nav-icon"></span><span class="nav-label">{{ t('app.dashboard') }}</span></a>
<a href="/assets" title="{{ t('app.assets') }}"><span class="nav-icon"></span><span class="nav-label">{{ t('app.assets') }}</span></a>
<a href="/asset-tree" title="{{ t('app.asset_tree') }}"><span class="nav-icon"></span><span class="nav-label">{{ t('app.asset_tree') }}</span></a>
<a href="/charts" title="{{ t('app.charts') }}"><span class="nav-icon"></span><span class="nav-label">{{ t('app.charts') }}</span></a>
{% if session_user and session_user.is_admin %}
<a href="/software" title="{{ t('app.software') }}"><span class="nav-icon"></span><span class="nav-label">{{ t('app.software') }}</span></a>
<a href="/job-definitions" title="{{ t('jobdefs.title') }}"><span class="nav-icon"></span><span class="nav-label">{{ t('jobdefs.title') }}</span></a>
<div class="side-nav-settings">
<a href="/settings" class="settings-trigger" title="{{ t('app.settings') }}"><span class="nav-icon"></span><span class="nav-label">{{ t('app.settings') }}</span><span class="nav-caret"></span></a>
<div class="settings-hover-menu" role="menu">
<a href="/settings/appearance">{{ t('settings.appearance', 'Darstellung') }}</a>
<a href="/settings/fields">{{ t('settings.fields', 'Felder') }}</a>
<a href="/settings/import-export">{{ t('settings.import_export', 'Import / Export') }}</a>
<a href="/categories">{{ t('app.categories', 'Kategorien') }}</a>
<a href="/settings/authentication">{{ t('settings.ldap', 'LDAP') }}</a>
<a href="/sync/meshcentral">{{ t('settings.meshcentral', 'MeshCentral') }}</a>
<a href="/settings/software">{{ t('software.settings.menu') }}</a>
<a href="/settings/backup">{{ t('settings.backup', 'Sicherung') }}</a>
<a href="/status-options">{{ t('app.status_values', 'Statuswerte') }}</a>
<a href="/settings/system">{{ t('settings.system_information', 'Systeminformationen') }}</a>
<a href="/settings/translations">{{ t('settings.translations', 'Übersetzungen') }}</a>
<a href="/users">{{ t('users.management', 'Benutzerverwaltung') }}</a>
</div>
</div>
{% endif %}
</aside>
<main class="app-content">{% block content %}{% endblock %}</main>
</div><div id="toast-container" class="toast-container" aria-live="polite"></div><script src="/static/js/local-time.js"></script><script src="/static/js/browser-state.js?v={{ app_version }}"></script><script src="/static/js/table-tools.js?v={{ app_version }}"></script><script src="/static/js/list-tools.js"></script><script src="/static/js/asset-columns.js"></script><script src="/static/js/asset-duplicates.js"></script><script src="/static/js/toasts.js"></script><script>
(() => {
document.addEventListener('keydown', event => {
if (!(event.ctrlKey || event.metaKey) || event.key.toLowerCase() !== 's') return;
const openDialog = [...document.querySelectorAll('dialog[open]')].pop();
const scope = openDialog || document;
const buttons = [...scope.querySelectorAll('form button[type="submit"], form button:not([type]), form input[type="submit"]')];
const saveButton = buttons.find(button => !button.disabled && (
button.matches('.button-save,[data-save-shortcut]') ||
/speichern|save/i.test(button.textContent || button.value || '')
));
if (!saveButton) return;
event.preventDefault();
saveButton.click();
});
})();
</script><script>
(() => {
const shell = document.getElementById('app-shell');
const toggle = document.getElementById('side-nav-toggle');
if (!shell || !toggle) return;
const key = 'assetmanager.sidebar.collapsed';
if (document.documentElement.classList.contains('sidebar-collapsed-preset')) shell.classList.add('sidebar-collapsed');
shell.classList.add('sidebar-ready');
toggle.addEventListener('click', () => {
shell.classList.toggle('sidebar-collapsed');
const collapsed = shell.classList.contains('sidebar-collapsed');
localStorage.setItem(key, collapsed ? 'true' : 'false');
document.documentElement.classList.toggle('sidebar-collapsed-preset', collapsed);
});
})();
</script><script src="/static/js/asset-page-boot.js"></script><script src="/static/js/presence.js"></script><script src="/static/js/software-inventory-selection.js?v={{ app_version }}"></script>
</body></html>