Initialer Import des AssetManagers

This commit is contained in:
2026-08-02 21:28:50 +00:00
commit ed72ce8821
256 changed files with 26361 additions and 0 deletions
+126
View File
@@ -0,0 +1,126 @@
{% 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>