snapshot: preserve app registry and branding domain foundation
This commit is contained in:
255
backend/workflows/app_registry.py
Normal file
255
backend/workflows/app_registry.py
Normal file
@@ -0,0 +1,255 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
|
||||
from django.urls import reverse
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from .models import PortalAppConfig
|
||||
from .roles import user_has_capability
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class AppDefinition:
|
||||
key: str
|
||||
section: str
|
||||
route_name: str
|
||||
title: object
|
||||
description: object
|
||||
action_label: object
|
||||
capability: str | None = None
|
||||
accent: str = ''
|
||||
accent_label: str = 'APP'
|
||||
style_variant: str = ''
|
||||
tags: tuple[object, ...] = ()
|
||||
|
||||
|
||||
APP_DEFINITIONS: tuple[AppDefinition, ...] = (
|
||||
AppDefinition(
|
||||
key='onboarding',
|
||||
section=PortalAppConfig.SECTION_APP,
|
||||
route_name='onboarding_create',
|
||||
title=_('Onboarding'),
|
||||
description=_('Neue Mitarbeitende erfassen, PDF mit Briefkopf erstellen, Benachrichtigungen senden und in Nextcloud ablegen.'),
|
||||
action_label=_('Onboarding starten'),
|
||||
accent='ON',
|
||||
tags=(_('Mehrschritt-Formular'), 'PDF', _('E-Mail Routing')),
|
||||
style_variant='primary',
|
||||
),
|
||||
AppDefinition(
|
||||
key='offboarding',
|
||||
section=PortalAppConfig.SECTION_APP,
|
||||
route_name='offboarding_create',
|
||||
title=_('Offboarding'),
|
||||
description=_('Mitarbeitende suchen, Daten vorbefüllen, Offboarding-Dokumente erzeugen und Rückgabe-Prozess starten.'),
|
||||
action_label=_('Offboarding starten'),
|
||||
accent='OFF',
|
||||
tags=(_('Profile-Suche'), _('Hardware-Liste'), _('IT-Rückgabe')),
|
||||
style_variant='red',
|
||||
),
|
||||
AppDefinition(
|
||||
key='requests_dashboard',
|
||||
section=PortalAppConfig.SECTION_APP,
|
||||
route_name='requests_dashboard',
|
||||
title=_('Anfragen Dashboard'),
|
||||
description=_('Status, Suchfunktion, PDF-Links und Verlauf aller Onboarding-/Offboarding-Anfragen.'),
|
||||
action_label=_('Dashboard öffnen'),
|
||||
capability='access_requests_dashboard',
|
||||
accent='APP',
|
||||
tags=(_('Suche'), _('Status'), _('PDF Zugriff')),
|
||||
),
|
||||
AppDefinition(
|
||||
key='branding',
|
||||
section=PortalAppConfig.SECTION_PLATFORM,
|
||||
route_name='portal_branding_page',
|
||||
title=_('Branding'),
|
||||
description=_('Logo, Portalname, Farben und PDF-Briefkopf verwalten.'),
|
||||
action_label=_('Öffnen'),
|
||||
capability='manage_product_branding',
|
||||
),
|
||||
AppDefinition(
|
||||
key='app_registry',
|
||||
section=PortalAppConfig.SECTION_PLATFORM,
|
||||
route_name='portal_app_registry_page',
|
||||
title=_('App Registry'),
|
||||
description=_('Apps zentral aktivieren, sortieren und für Kundenauftritte vorbereiten.'),
|
||||
action_label=_('Öffnen'),
|
||||
capability='manage_app_registry',
|
||||
),
|
||||
AppDefinition(
|
||||
key='integrations',
|
||||
section=PortalAppConfig.SECTION_ADMIN,
|
||||
route_name='integrations_setup_page',
|
||||
title=_('Integrationen'),
|
||||
description=_('Nextcloud- und E-Mail-Setup.'),
|
||||
action_label=_('Öffnen'),
|
||||
capability='manage_integrations',
|
||||
),
|
||||
AppDefinition(
|
||||
key='users',
|
||||
section=PortalAppConfig.SECTION_ADMIN,
|
||||
route_name='user_management_page',
|
||||
title=_('Benutzer & Rollen'),
|
||||
description=_('Benutzer anlegen, Rollen zuweisen und Zugriffe steuern.'),
|
||||
action_label=_('Öffnen'),
|
||||
capability='manage_users',
|
||||
),
|
||||
AppDefinition(
|
||||
key='audit_log',
|
||||
section=PortalAppConfig.SECTION_ADMIN,
|
||||
route_name='audit_log_page',
|
||||
title=_('Audit Log'),
|
||||
description=_('Wichtige Admin-Aktionen nachvollziehen und prüfen.'),
|
||||
action_label=_('Öffnen'),
|
||||
capability='view_audit_log',
|
||||
),
|
||||
AppDefinition(
|
||||
key='backups',
|
||||
section=PortalAppConfig.SECTION_ADMIN,
|
||||
route_name='backup_recovery_page',
|
||||
title=_('Backup & Recovery'),
|
||||
description=_('Backups erstellen und sicher verifizieren.'),
|
||||
action_label=_('Öffnen'),
|
||||
capability='manage_backups',
|
||||
),
|
||||
AppDefinition(
|
||||
key='welcome_emails',
|
||||
section=PortalAppConfig.SECTION_ADMIN,
|
||||
route_name='welcome_emails_page',
|
||||
title=_('Welcome E-Mails'),
|
||||
description=_('Geplante Welcome Mails verwalten.'),
|
||||
action_label=_('Öffnen'),
|
||||
capability='manage_welcome_emails',
|
||||
),
|
||||
AppDefinition(
|
||||
key='form_builder',
|
||||
section=PortalAppConfig.SECTION_ADMIN,
|
||||
route_name='form_builder_page',
|
||||
title=_('Form Builder'),
|
||||
description=_('Felder, Schritte und Optionen verwalten.'),
|
||||
action_label=_('Öffnen'),
|
||||
capability='manage_builders',
|
||||
),
|
||||
AppDefinition(
|
||||
key='intro_builder',
|
||||
section=PortalAppConfig.SECTION_ADMIN,
|
||||
route_name='intro_builder_page',
|
||||
title=_('Einweisungs-Builder'),
|
||||
description=_('Checklistenpunkte für das Einweisungsprotokoll konfigurieren.'),
|
||||
action_label=_('Öffnen'),
|
||||
capability='manage_builders',
|
||||
),
|
||||
AppDefinition(
|
||||
key='handbook',
|
||||
section=PortalAppConfig.SECTION_ADMIN,
|
||||
route_name='handbook_page',
|
||||
title=_('Handbook'),
|
||||
description=_('Project wiki and developer documentation in one place.'),
|
||||
action_label=_('Öffnen'),
|
||||
capability='view_docs',
|
||||
),
|
||||
AppDefinition(
|
||||
key='django_admin',
|
||||
section=PortalAppConfig.SECTION_ADMIN,
|
||||
route_name='admin:index',
|
||||
title=_('Django Admin'),
|
||||
description=_('Vollständige Datenverwaltung.'),
|
||||
action_label=_('Öffnen'),
|
||||
capability='access_django_admin_link',
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
SECTION_META = {
|
||||
PortalAppConfig.SECTION_APP: {
|
||||
'title': _('Apps'),
|
||||
'subtitle': _('Wählen Sie den gewünschten Prozess.'),
|
||||
'css_class': 'section-head-primary',
|
||||
'grid_class': 'apps-grid',
|
||||
},
|
||||
PortalAppConfig.SECTION_PLATFORM: {
|
||||
'title': _('Platform Apps'),
|
||||
'subtitle': _('Produktweite Konfiguration und Produktsteuerung.'),
|
||||
'css_class': 'section-head-platform',
|
||||
'grid_class': 'admin-grid',
|
||||
},
|
||||
PortalAppConfig.SECTION_ADMIN: {
|
||||
'title': _('Admin Apps'),
|
||||
'subtitle': _('Konfiguration, Tests und Steuerung.'),
|
||||
'css_class': 'section-head-admin',
|
||||
'grid_class': 'admin-grid',
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def ensure_portal_app_configs() -> None:
|
||||
for index, definition in enumerate(APP_DEFINITIONS):
|
||||
PortalAppConfig.objects.get_or_create(
|
||||
key=definition.key,
|
||||
defaults={
|
||||
'section': definition.section,
|
||||
'sort_order': index,
|
||||
'is_enabled': True,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
def get_portal_app_registry_rows() -> list[dict[str, object]]:
|
||||
ensure_portal_app_configs()
|
||||
config_map = {config.key: config for config in PortalAppConfig.objects.all()}
|
||||
rows: list[dict[str, object]] = []
|
||||
for index, definition in enumerate(APP_DEFINITIONS):
|
||||
config = config_map[definition.key]
|
||||
rows.append(
|
||||
{
|
||||
'definition': definition,
|
||||
'config': config,
|
||||
'default_section': definition.section,
|
||||
'default_sort_order': index,
|
||||
}
|
||||
)
|
||||
return rows
|
||||
|
||||
|
||||
def build_portal_app_sections(user) -> list[dict[str, object]]:
|
||||
ensure_portal_app_configs()
|
||||
config_map = {config.key: config for config in PortalAppConfig.objects.all()}
|
||||
grouped: dict[str, list[dict[str, object]]] = {key: [] for key in SECTION_META}
|
||||
|
||||
for definition in APP_DEFINITIONS:
|
||||
config = config_map.get(definition.key)
|
||||
if not config or not config.is_enabled:
|
||||
continue
|
||||
if definition.capability and not user_has_capability(user, definition.capability):
|
||||
continue
|
||||
grouped[config.section].append(
|
||||
{
|
||||
'key': definition.key,
|
||||
'href': reverse(definition.route_name),
|
||||
'title': config.translated_title_override() or str(definition.title),
|
||||
'description': config.translated_description_override() or str(definition.description),
|
||||
'action_label': config.translated_action_label_override() or str(definition.action_label),
|
||||
'accent': definition.accent,
|
||||
'accent_label': definition.accent_label,
|
||||
'style_variant': definition.style_variant,
|
||||
'tags': [str(tag) for tag in definition.tags],
|
||||
'sort_order': config.sort_order,
|
||||
}
|
||||
)
|
||||
|
||||
sections: list[dict[str, object]] = []
|
||||
for section_key, meta in SECTION_META.items():
|
||||
apps = sorted(grouped.get(section_key, []), key=lambda item: (item['sort_order'], item['title']))
|
||||
if not apps:
|
||||
continue
|
||||
sections.append(
|
||||
{
|
||||
'key': section_key,
|
||||
'title': str(meta['title']),
|
||||
'subtitle': str(meta['subtitle']),
|
||||
'css_class': meta['css_class'],
|
||||
'grid_class': meta['grid_class'],
|
||||
'apps': apps,
|
||||
}
|
||||
)
|
||||
return sections
|
||||
Reference in New Issue
Block a user