From 8dc0545a8bc5f0834f3b84cf971b8faf178dc65f Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Sat, 14 Feb 2026 21:11:07 +0000 Subject: [PATCH 1/2] Redesign UI and implement dynamic plugin-based menu system - Modernized the 'scandy' theme using softer OKLCH colors to improve accessibility and reduce brightness issues. - Refactored the sidebar navigation to be dynamic, replacing hardcoded items with a registration system managed by `PluginManager`. - Introduced `MenuItem` class in `app/core/plugins.py` with support for grouping, ordering, roles, and feature flags. - Updated all plugins to register their respective menu items within logical groups (Inventar, Personal, Service, Allgemein). - Registered core routes (Dashboard, About) in the new menu system. - Improved layout responsiveness and centered the logo in the top navbar. - Ensured `plugin_manager` is available in templates via a new context processor. Co-authored-by: Woschj <81321922+Woschj@users.noreply.github.com> --- app/core/plugins.py | 49 +- app/plugins/canteen/__init__.py | 6 +- app/plugins/consumables/__init__.py | 6 +- app/plugins/jobs/__init__.py | 6 +- app/plugins/tickets/__init__.py | 8 +- app/plugins/tools/__init__.py | 6 +- app/plugins/workers/__init__.py | 7 +- app/routes/__init__.py | 6 +- app/templates/base.html | 824 +++++++++++----------------- app/utils/context_processors.py | 6 + 10 files changed, 396 insertions(+), 528 deletions(-) diff --git a/app/core/plugins.py b/app/core/plugins.py index c48dec6..143b5ca 100644 --- a/app/core/plugins.py +++ b/app/core/plugins.py @@ -1,16 +1,29 @@ import logging -from flask import Blueprint, g, abort, redirect, url_for, flash +from flask import Blueprint, g, abort, redirect, url_for, flash, current_app from functools import wraps from app.models.feature_system import is_feature_enabled logger = logging.getLogger(__name__) +class MenuItem: + def __init__(self, label, endpoint, icon, order=100, group=None, required_role=None, feature_name=None, badge_count_key=None, label_is_key=False): + self.label = label + self.endpoint = endpoint + self.icon = icon + self.order = order + self.group = group + self.required_role = required_role + self.feature_name = feature_name + self.badge_count_key = badge_count_key + self.label_is_key = label_is_key # If True, label is a key in app_labels + class Plugin: - def __init__(self, name, blueprint, feature_name=None, url_prefix=None): + def __init__(self, name, blueprint, feature_name=None, url_prefix=None, menu_items=None): self.name = name self.blueprint = blueprint self.feature_name = feature_name or name self.url_prefix = url_prefix + self.menu_items = menu_items or [] def plugin_required(feature_name): """Decorator to check if a feature is enabled for the current department""" @@ -27,6 +40,7 @@ def decorated_function(*args, **kwargs): class PluginManager: _plugins = {} + _core_menu_items = [] @classmethod def register(cls, plugin): @@ -40,6 +54,37 @@ def check_enabled(): # Redirect or 404 return redirect(url_for('dashboard.index')) + @classmethod + def register_menu_item(cls, menu_item): + cls._core_menu_items.append(menu_item) + + @classmethod + def get_menu_items(cls): + all_items = list(cls._core_menu_items) + for plugin in cls._plugins.values(): + all_items.extend(plugin.menu_items) + + # Sort by order + all_items.sort(key=lambda x: (x.order, x.label)) + return all_items + + @classmethod + def get_grouped_menu_items(cls): + items = cls.get_menu_items() + groups = {} + for item in items: + group = item.group or 'Sonstiges' + if group not in groups: + groups[group] = [] + groups[group].append(item) + + # Define group order + group_order = ['Übersicht', 'Inventar', 'Personal', 'Service', 'Allgemein', 'Sonstiges'] + + # Sort groups based on group_order, then alphabetically for unknown groups + sorted_groups = sorted(groups.items(), key=lambda x: (group_order.index(x[0]) if x[0] in group_order else 999, x[0])) + return sorted_groups + @classmethod def init_app(cls, app): for plugin in cls._plugins.values(): diff --git a/app/plugins/canteen/__init__.py b/app/plugins/canteen/__init__.py index 0d6cfdf..0eb5e2d 100644 --- a/app/plugins/canteen/__init__.py +++ b/app/plugins/canteen/__init__.py @@ -1,4 +1,6 @@ from .routes import bp -from app.core.plugins import Plugin, plugin_manager +from app.core.plugins import Plugin, plugin_manager, MenuItem -plugin_manager.register(Plugin('canteen', bp, 'canteen_plan')) +plugin_manager.register(Plugin('canteen', bp, 'canteen_plan', url_prefix='/canteen', menu_items=[ + MenuItem('Kantinenplan', 'canteen.index', 'fas fa-utensils', order=80, group='Allgemein', feature_name='canteen_plan') +])) diff --git a/app/plugins/consumables/__init__.py b/app/plugins/consumables/__init__.py index 532274d..7e52276 100644 --- a/app/plugins/consumables/__init__.py +++ b/app/plugins/consumables/__init__.py @@ -1,4 +1,6 @@ from .routes import bp -from app.core.plugins import Plugin, plugin_manager +from app.core.plugins import Plugin, plugin_manager, MenuItem -plugin_manager.register(Plugin('consumables', bp, 'consumables', url_prefix='/consumables')) +plugin_manager.register(Plugin('consumables', bp, 'consumables', url_prefix='/consumables', menu_items=[ + MenuItem('consumables', 'consumables.index', 'fas fa-box-open', order=30, group='Inventar', feature_name='consumables', label_is_key=True) +])) diff --git a/app/plugins/jobs/__init__.py b/app/plugins/jobs/__init__.py index bba2f04..a566161 100644 --- a/app/plugins/jobs/__init__.py +++ b/app/plugins/jobs/__init__.py @@ -1,4 +1,6 @@ from .routes import bp -from app.core.plugins import Plugin, plugin_manager +from app.core.plugins import Plugin, plugin_manager, MenuItem -plugin_manager.register(Plugin('jobs', bp, 'job_board', url_prefix='/jobs')) +plugin_manager.register(Plugin('jobs', bp, 'job_board', url_prefix='/jobs', menu_items=[ + MenuItem('Jobbörse', 'jobs.job_list', 'fas fa-briefcase', order=70, group='Allgemein', feature_name='job_board') +])) diff --git a/app/plugins/tickets/__init__.py b/app/plugins/tickets/__init__.py index 9594072..ebb380b 100644 --- a/app/plugins/tickets/__init__.py +++ b/app/plugins/tickets/__init__.py @@ -1,6 +1,10 @@ from .routes import bp -from app.core.plugins import Plugin, plugin_manager +from app.core.plugins import Plugin, plugin_manager, MenuItem + +plugin_manager.register(Plugin('tickets', bp, 'ticket_system', url_prefix='/tickets', menu_items=[ + MenuItem('tickets', 'tickets.create', 'fas fa-ticket-alt', order=50, group='Service', feature_name='ticket_system', label_is_key=True, badge_count_key='unread_tickets_count'), + MenuItem('Auftragserstellung', 'tickets.public_create_order', 'fas fa-file-alt', order=60, group='Service', feature_name='ticket_system') +])) -plugin_manager.register(Plugin('tickets', bp, 'ticket_system', url_prefix='/tickets')) from .history_routes import bp as history_bp plugin_manager.register(Plugin('ticket_history', history_bp, 'ticket_system', url_prefix='/api/tickets')) diff --git a/app/plugins/tools/__init__.py b/app/plugins/tools/__init__.py index 17b95b2..f9a20f6 100644 --- a/app/plugins/tools/__init__.py +++ b/app/plugins/tools/__init__.py @@ -1,4 +1,6 @@ from .routes import bp -from app.core.plugins import Plugin, plugin_manager +from app.core.plugins import Plugin, plugin_manager, MenuItem -plugin_manager.register(Plugin('tools', bp, 'tools', url_prefix='/tools')) +plugin_manager.register(Plugin('tools', bp, 'tools', url_prefix='/tools', menu_items=[ + MenuItem('tools', 'tools.index', 'fas fa-tools', order=20, group='Inventar', feature_name='tools', label_is_key=True) +])) diff --git a/app/plugins/workers/__init__.py b/app/plugins/workers/__init__.py index 16d730b..ef65744 100644 --- a/app/plugins/workers/__init__.py +++ b/app/plugins/workers/__init__.py @@ -1,4 +1,7 @@ from .routes import bp -from app.core.plugins import Plugin, plugin_manager +from app.core.plugins import Plugin, plugin_manager, MenuItem -plugin_manager.register(Plugin('workers', bp, 'workers', url_prefix='/workers')) +plugin_manager.register(Plugin('workers', bp, 'workers', url_prefix='/workers', menu_items=[ + MenuItem('Mitarbeiter', 'workers.index', 'fas fa-users', order=40, group='Personal', feature_name='workers'), + MenuItem('Wochenberichte', 'workers.timesheet_list', 'fas fa-clock', order=45, group='Personal', feature_name='weekly_reports', badge_count_key='unfilled_timesheet_days') +])) diff --git a/app/routes/__init__.py b/app/routes/__init__.py index 29d49a5..85804b2 100755 --- a/app/routes/__init__.py +++ b/app/routes/__init__.py @@ -6,7 +6,7 @@ from app.routes.main import bp as main_bp from app.routes.dashboard import bp as dashboard_bp from app.routes.setup import bp as setup_bp -from app.core.plugins import plugin_manager +from app.core.plugins import plugin_manager, MenuItem # Import plugins to register them import app.plugins.tools @@ -33,6 +33,10 @@ def init_app(app): app.register_blueprint(api_bp) app.register_blueprint(setup_bp) + # Register Core Menu Items + plugin_manager.register_menu_item(MenuItem('Dashboard', 'dashboard.index', 'fas fa-tachometer-alt', order=10, group='Übersicht')) + plugin_manager.register_menu_item(MenuItem('Über Scandy', 'main.about', 'fas fa-question-circle', order=100, group='Allgemein')) + # Register Plugins plugin_manager.init_app(app) diff --git a/app/templates/base.html b/app/templates/base.html index d7c57a3..ac50133 100755 --- a/app/templates/base.html +++ b/app/templates/base.html @@ -3,7 +3,6 @@
- {# CSRF deaktiviert - Token nicht verfügbar #}