From 9437aaa29a56a9d3d522f74960607fd4a1292f73 Mon Sep 17 00:00:00 2001 From: Md Bayazid Bostame Date: Thu, 26 Mar 2026 12:59:45 +0100 Subject: [PATCH] snapshot: preserve scalable app registry and landing visibility rules --- backend/locale/en/LC_MESSAGES/django.po | 527 ++++++++++++------ backend/workflows/admin.py | 16 +- backend/workflows/app_registry.py | 120 +++- ...rtalappconfig_visible_to_admin_and_more.py | 33 ++ backend/workflows/models.py | 4 + .../static/workflows/css/admin_tools.css | 30 + .../templates/workflows/app_registry.html | 266 +++++++-- .../workflows/templates/workflows/home.html | 2 + backend/workflows/views.py | 6 + 9 files changed, 762 insertions(+), 242 deletions(-) create mode 100644 backend/workflows/migrations/0041_portalappconfig_visible_to_admin_and_more.py diff --git a/backend/locale/en/LC_MESSAGES/django.po b/backend/locale/en/LC_MESSAGES/django.po index dc566a3..dfd613d 100644 --- a/backend/locale/en/LC_MESSAGES/django.po +++ b/backend/locale/en/LC_MESSAGES/django.po @@ -2,14 +2,14 @@ msgid "" msgstr "" "Project-Id-Version: tubco-portal\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-03-26 11:02+0000\n" +"POT-Creation-Date: 2026-03-26 11:56+0000\n" "PO-Revision-Date: 2026-03-24 00:00+0000\n" "Language: en\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -#: workflows/app_registry.py:32 workflows/models.py:273 workflows/models.py:354 +#: workflows/app_registry.py:32 workflows/models.py:277 workflows/models.py:358 #: workflows/templates/workflows/onboarding_form.html:25 #: workflows/templates/workflows/requests_dashboard.html:68 #: workflows/templates/workflows/requests_dashboard.html:131 @@ -36,7 +36,7 @@ msgstr "Multi-step form" msgid "E-Mail Routing" msgstr "Email routing" -#: workflows/app_registry.py:43 workflows/models.py:274 workflows/models.py:355 +#: workflows/app_registry.py:43 workflows/models.py:278 workflows/models.py:359 #: workflows/templates/workflows/requests_dashboard.html:78 #: workflows/templates/workflows/requests_dashboard.html:132 msgid "Offboarding" @@ -85,10 +85,12 @@ msgid "Dashboard öffnen" msgstr "Open dashboard" #: workflows/app_registry.py:59 +#: workflows/templates/workflows/app_registry.html:27 msgid "Suche" msgstr "Search" #: workflows/app_registry.py:59 +#: workflows/templates/workflows/app_registry.html:31 #: workflows/templates/workflows/backup_recovery.html:40 #: workflows/templates/workflows/onboarding_intro_session.html:37 #: workflows/templates/workflows/request_timeline.html:70 @@ -121,8 +123,8 @@ msgid "Öffnen" msgstr "Open" #: workflows/app_registry.py:74 -#: workflows/templates/workflows/app_registry.html:4 -#: workflows/templates/workflows/app_registry.html:12 +#: workflows/templates/workflows/app_registry.html:5 +#: workflows/templates/workflows/app_registry.html:13 msgid "App Registry" msgstr "" @@ -213,29 +215,44 @@ msgstr "Django Admin" msgid "Vollständige Datenverwaltung." msgstr "Full data management." -#: workflows/app_registry.py:165 workflows/models.py:80 +#: workflows/app_registry.py:259 +msgid "Nur Platform" +msgstr "" + +#: workflows/app_registry.py:261 +#: workflows/templates/workflows/app_registry.html:85 +msgid "Alle Firmenrollen" +msgstr "" + +#: workflows/app_registry.py:267 workflows/models.py:80 +#: workflows/templates/workflows/app_registry.html:43 +#: workflows/templates/workflows/app_registry.html:78 msgid "Apps" msgstr "Apps" -#: workflows/app_registry.py:166 +#: workflows/app_registry.py:268 msgid "Wählen Sie den gewünschten Prozess." msgstr "Choose the desired process." -#: workflows/app_registry.py:171 workflows/models.py:81 +#: workflows/app_registry.py:273 workflows/models.py:81 +#: workflows/templates/workflows/app_registry.html:44 +#: workflows/templates/workflows/app_registry.html:74 msgid "Platform Apps" msgstr "" -#: workflows/app_registry.py:172 +#: workflows/app_registry.py:274 #, fuzzy #| msgid "Konfiguration, Tests und Steuerung." msgid "Produktweite Konfiguration und Produktsteuerung." msgstr "Configuration, tests, and controls." -#: workflows/app_registry.py:177 workflows/models.py:82 +#: workflows/app_registry.py:279 workflows/models.py:82 +#: workflows/templates/workflows/app_registry.html:45 +#: workflows/templates/workflows/app_registry.html:76 msgid "Admin Apps" msgstr "Admin Apps" -#: workflows/app_registry.py:178 +#: workflows/app_registry.py:280 msgid "Konfiguration, Tests und Steuerung." msgstr "Configuration, tests, and controls." @@ -330,11 +347,11 @@ msgstr "Role:" msgid "Dieser Benutzername ist bereits vergeben." msgstr "This username is already taken." -#: workflows/forms.py:154 workflows/views.py:616 +#: workflows/forms.py:154 workflows/views.py:622 msgid "Ungültige Rolle." msgstr "Invalid role." -#: workflows/forms.py:156 workflows/views.py:619 +#: workflows/forms.py:156 workflows/views.py:625 msgid "Nur Platform Owner dürfen diese Rolle vergeben." msgstr "" @@ -397,10 +414,12 @@ msgid "Favicon" msgstr "" #: workflows/forms.py:209 +#: workflows/templates/workflows/branding_settings.html:94 msgid "Primärfarbe" msgstr "Primary color" #: workflows/forms.py:210 +#: workflows/templates/workflows/branding_settings.html:95 msgid "Sekundärfarbe" msgstr "Secondary color" @@ -433,244 +452,244 @@ msgid "" "(frühestens %(date)s)." msgstr "" -#: workflows/models.py:151 workflows/views.py:200 +#: workflows/models.py:155 workflows/views.py:200 msgid "Eingereicht" msgstr "Submitted" -#: workflows/models.py:152 workflows/views.py:201 +#: workflows/models.py:156 workflows/views.py:201 msgid "In Bearbeitung" msgstr "Processing" -#: workflows/models.py:153 workflows/models.py:468 workflows/views.py:202 +#: workflows/models.py:157 workflows/models.py:472 workflows/views.py:202 msgid "Abgeschlossen" msgstr "Completed" -#: workflows/models.py:154 workflows/models.py:408 +#: workflows/models.py:158 workflows/models.py:412 #: workflows/templates/workflows/backup_recovery.html:70 #: workflows/templates/workflows/requests_dashboard.html:222 #: workflows/templates/workflows/welcome_emails.html:108 workflows/views.py:203 msgid "Fehlgeschlagen" msgstr "Failed" -#: workflows/models.py:161 +#: workflows/models.py:165 msgid "Herr" msgstr "" -#: workflows/models.py:161 +#: workflows/models.py:165 msgid "Frau" msgstr "" -#: workflows/models.py:161 +#: workflows/models.py:165 msgid "Divers" msgstr "" -#: workflows/models.py:171 +#: workflows/models.py:175 msgid "befristet" msgstr "" -#: workflows/models.py:171 +#: workflows/models.py:175 msgid "unbefristet" msgstr "" -#: workflows/models.py:234 +#: workflows/models.py:238 #: workflows/templates/workflows/onboarding_intro_session.html:28 #: workflows/templates/workflows/requests_dashboard.html:145 msgid "Abteilung" msgstr "Department" -#: workflows/models.py:235 +#: workflows/models.py:239 msgid "Geräte" msgstr "" -#: workflows/models.py:236 +#: workflows/models.py:240 msgid "Software" msgstr "" -#: workflows/models.py:237 +#: workflows/models.py:241 #, fuzzy #| msgid "Vorgänge" msgid "Zugänge" msgstr "Requests" -#: workflows/models.py:238 +#: workflows/models.py:242 msgid "Workspace-Gruppen" msgstr "" -#: workflows/models.py:239 +#: workflows/models.py:243 msgid "Ressourcen" msgstr "" -#: workflows/models.py:240 +#: workflows/models.py:244 msgid "Telefonnummern" msgstr "" -#: workflows/models.py:266 +#: workflows/models.py:270 msgid "Automatisch" msgstr "" -#: workflows/models.py:267 workflows/views.py:95 +#: workflows/models.py:271 workflows/views.py:95 msgid "Stammdaten" msgstr "Master data" -#: workflows/models.py:268 workflows/views.py:96 +#: workflows/models.py:272 workflows/views.py:96 msgid "Vertrag" msgstr "Contract" -#: workflows/models.py:269 workflows/views.py:97 +#: workflows/models.py:273 workflows/views.py:97 msgid "IT-Setup" msgstr "IT setup" -#: workflows/models.py:270 workflows/views.py:98 +#: workflows/models.py:274 workflows/views.py:98 msgid "Abschluss" msgstr "Finish" -#: workflows/models.py:312 +#: workflows/models.py:316 #, fuzzy #| msgid "Onboarding" msgid "Onboarding: IT" msgstr "Onboarding" -#: workflows/models.py:313 +#: workflows/models.py:317 #, fuzzy #| msgid "Offboarding-Anfrage speichern" msgid "Onboarding: Allgemeine Info" msgstr "Save offboarding request" -#: workflows/models.py:314 +#: workflows/models.py:318 #, fuzzy #| msgid "Onboarding starten" msgid "Onboarding: Visitenkarte" msgstr "Start onboarding" -#: workflows/models.py:315 +#: workflows/models.py:319 #, fuzzy #| msgid "Onboarding" msgid "Onboarding: HR Works" msgstr "Onboarding" -#: workflows/models.py:316 +#: workflows/models.py:320 #, fuzzy #| msgid "Onboarding starten" msgid "Onboarding: Schlüssel" msgstr "Start onboarding" -#: workflows/models.py:317 +#: workflows/models.py:321 msgid "Onboarding: Referenz Anfordernde Person" msgstr "" -#: workflows/models.py:318 +#: workflows/models.py:322 #, fuzzy #| msgid "Welcome E-Mails" msgid "Onboarding: Welcome E-Mail" msgstr "Welcome Emails" -#: workflows/models.py:319 +#: workflows/models.py:323 #, fuzzy #| msgid "Offboarding" msgid "Offboarding: IT" msgstr "Offboarding" -#: workflows/models.py:320 +#: workflows/models.py:324 #, fuzzy #| msgid "Offboarding-Anfrage speichern" msgid "Offboarding: Allgemeine Info" msgstr "Save offboarding request" -#: workflows/models.py:321 +#: workflows/models.py:325 #, fuzzy #| msgid "Offboarding starten" msgid "Offboarding: HR Works Deaktivierung" msgstr "Start offboarding" -#: workflows/models.py:322 +#: workflows/models.py:326 msgid "Offboarding: Referenz Anfordernde Person" msgstr "" -#: workflows/models.py:358 +#: workflows/models.py:362 msgid "Immer" msgstr "" -#: workflows/models.py:359 workflows/models.py:437 +#: workflows/models.py:363 workflows/models.py:441 msgid "Enthält" msgstr "" -#: workflows/models.py:360 workflows/models.py:438 +#: workflows/models.py:364 workflows/models.py:442 msgid "Ist gleich" msgstr "" -#: workflows/models.py:361 +#: workflows/models.py:365 msgid "Ist aktiv/Ja" msgstr "" -#: workflows/models.py:362 +#: workflows/models.py:366 #, fuzzy #| msgid "inaktiv" msgid "Ist inaktiv/Nein" msgstr "inactive" -#: workflows/models.py:404 +#: workflows/models.py:408 #: workflows/templates/workflows/welcome_emails.html:100 msgid "Geplant" msgstr "Scheduled" -#: workflows/models.py:405 +#: workflows/models.py:409 #: workflows/templates/workflows/welcome_emails.html:102 msgid "Pausiert" msgstr "Paused" -#: workflows/models.py:406 +#: workflows/models.py:410 #: workflows/templates/workflows/welcome_emails.html:104 msgid "Abgebrochen" msgstr "Cancelled" -#: workflows/models.py:407 +#: workflows/models.py:411 #: workflows/templates/workflows/welcome_emails.html:106 msgid "Gesendet" msgstr "Sent" -#: workflows/models.py:430 workflows/tasks.py:576 +#: workflows/models.py:434 workflows/tasks.py:576 msgid "Geräte und Arbeitsplatz" msgstr "Devices and workplace" -#: workflows/models.py:431 workflows/tasks.py:577 +#: workflows/models.py:435 workflows/tasks.py:577 msgid "Konten und Berechtigungen" msgstr "Accounts and permissions" -#: workflows/models.py:432 workflows/tasks.py:578 +#: workflows/models.py:436 workflows/tasks.py:578 msgid "Software und Tools" msgstr "Software and tools" -#: workflows/models.py:433 workflows/tasks.py:579 +#: workflows/models.py:437 workflows/tasks.py:579 msgid "Prozesse und Hinweise" msgstr "Processes and notes" -#: workflows/models.py:436 +#: workflows/models.py:440 msgid "Immer anzeigen" msgstr "Always show" -#: workflows/models.py:439 +#: workflows/models.py:443 msgid "Ist Ja / aktiv" msgstr "Is yes / active" -#: workflows/models.py:440 +#: workflows/models.py:444 msgid "Ist Nein / inaktiv" msgstr "Is no / inactive" -#: workflows/models.py:467 +#: workflows/models.py:471 msgid "Entwurf" msgstr "Draft" -#: workflows/models.py:487 +#: workflows/models.py:491 #, fuzzy #| msgid "Nextcloud:" msgid "Nextcloud" msgstr "Nextcloud:" -#: workflows/models.py:488 +#: workflows/models.py:492 msgid "S3" msgstr "" -#: workflows/models.py:489 +#: workflows/models.py:493 msgid "NFS" msgstr "" @@ -678,15 +697,18 @@ msgstr "" msgid "Platform Owner" msgstr "" -#: workflows/roles.py:23 +#: workflows/roles.py:23 workflows/templates/workflows/app_registry.html:88 +#: workflows/templates/workflows/app_registry.html:114 msgid "Super Admin" msgstr "Super Admin" -#: workflows/roles.py:24 +#: workflows/roles.py:24 workflows/templates/workflows/app_registry.html:89 +#: workflows/templates/workflows/app_registry.html:118 msgid "Admin" msgstr "Admin" -#: workflows/roles.py:25 +#: workflows/roles.py:25 workflows/templates/workflows/app_registry.html:90 +#: workflows/templates/workflows/app_registry.html:122 msgid "IT Staff" msgstr "IT Staff" @@ -908,29 +930,47 @@ msgstr "" msgid "Link anfordern" msgstr "Request link" -#: workflows/templates/workflows/app_registry.html:13 +#: workflows/templates/workflows/app_registry.html:3 +#, fuzzy +#| msgid "Letzte Änderung" +msgid "Ungespeicherte Änderungen" +msgstr "Last updated" + +#: workflows/templates/workflows/app_registry.html:14 msgid "" "Apps zentral steuern, für Kunden vorbereiten und ohne Template-Eingriffe auf " "der Landing Page ausspielen." msgstr "" -#: workflows/templates/workflows/app_registry.html:19 +#: workflows/templates/workflows/app_registry.html:20 msgid "" "Sicherheit bleibt codebasiert: Sichtbarkeit und Reihenfolge sind hier " "steuerbar, Berechtigungen weiterhin über Rollen und Capabilities." msgstr "" -#: workflows/templates/workflows/app_registry.html:20 +#: workflows/templates/workflows/app_registry.html:21 #, fuzzy #| msgid "Produktion" msgid "Produktkern" msgstr "Production" #: workflows/templates/workflows/app_registry.html:28 -msgid "Key" +#, fuzzy +#| msgid "Nach Name oder E-Mail suchen" +msgid "Nach App-Name oder Key filtern" +msgstr "Search by name or email" + +#: workflows/templates/workflows/app_registry.html:33 +#: workflows/templates/workflows/app_registry.html:42 +#: workflows/templates/workflows/audit_log.html:25 +#: workflows/templates/workflows/requests_dashboard.html:130 +#: workflows/templates/workflows/requests_dashboard.html:138 +#: workflows/templates/workflows/requests_dashboard.html:147 +msgid "Alle" msgstr "" -#: workflows/templates/workflows/app_registry.html:29 +#: workflows/templates/workflows/app_registry.html:34 +#: workflows/templates/workflows/app_registry.html:62 #: workflows/templates/workflows/form_builder.html:91 #: workflows/templates/workflows/integrations_setup.html:263 #: workflows/templates/workflows/intro_builder.html:65 @@ -938,45 +978,138 @@ msgstr "" msgid "Aktiv" msgstr "Active" -#: workflows/templates/workflows/app_registry.html:30 +#: workflows/templates/workflows/app_registry.html:35 +#: workflows/templates/workflows/app_registry.html:64 +#: workflows/templates/workflows/backup_recovery.html:74 +#, fuzzy +#| msgid "Deaktivieren" +msgid "Deaktiviert" +msgstr "Disabled" + +#: workflows/templates/workflows/app_registry.html:36 +#: workflows/templates/workflows/app_registry.html:83 +msgid "Platform only" +msgstr "" + +#: workflows/templates/workflows/app_registry.html:40 +#: workflows/templates/workflows/app_registry.html:136 #, fuzzy #| msgid "Eingereicht" msgid "Bereich" msgstr "Submitted" -#: workflows/templates/workflows/app_registry.html:31 +#: workflows/templates/workflows/app_registry.html:69 +msgid "Empfohlener Standardzugriff:" +msgstr "" + +#: workflows/templates/workflows/app_registry.html:81 +#: workflows/templates/workflows/form_builder.html:87 +#: workflows/templates/workflows/intro_builder.html:58 +msgid "Sortierung" +msgstr "Sort order" + +#: workflows/templates/workflows/app_registry.html:91 +#: workflows/templates/workflows/app_registry.html:126 +#, fuzzy +#| msgid "IT Staff" +msgid "Staff" +msgstr "IT Staff" + +#: workflows/templates/workflows/app_registry.html:99 +#, fuzzy +#| msgid "Noch nicht verfügbar" +msgid "Verfügbarkeit" +msgstr "Not available yet" + +#: workflows/templates/workflows/app_registry.html:103 +#, fuzzy +#| msgid "Deaktivieren" +msgid "App aktiviert" +msgstr "Disabled" + +#: workflows/templates/workflows/app_registry.html:106 +msgid "" +"Deaktivierte Apps erscheinen nicht auf der Landing Page, selbst wenn Rollen " +"sie sehen dürften." +msgstr "" + +#: workflows/templates/workflows/app_registry.html:110 +msgid "Sichtbarkeit nach Rolle" +msgstr "" + +#: workflows/templates/workflows/app_registry.html:129 +msgid "" +"Wenn keine Firmenrolle aktiv ist, bleibt die App nur für die Platform " +"sichtbar." +msgstr "" + +#: workflows/templates/workflows/app_registry.html:133 +#, fuzzy +#| msgid "Sortierung" +msgid "Platzierung" +msgstr "Sort order" + +#: workflows/templates/workflows/app_registry.html:144 #, fuzzy #| msgid "Reihenfolge speichern" msgid "Reihenfolge" msgstr "Save order" -#: workflows/templates/workflows/app_registry.html:32 -msgid "Titel DE" +#: workflows/templates/workflows/app_registry.html:151 +msgid "Bezeichnungen & Texte" msgstr "" -#: workflows/templates/workflows/app_registry.html:33 -msgid "Titel EN" +#: workflows/templates/workflows/app_registry.html:154 +#: workflows/templates/workflows/branding_settings.html:141 +msgid "Deutsch" msgstr "" -#: workflows/templates/workflows/app_registry.html:34 -#, fuzzy -#| msgid "Aktion" -msgid "Aktion DE" -msgstr "Action" +#: workflows/templates/workflows/app_registry.html:156 +msgid "Titel" +msgstr "" -#: workflows/templates/workflows/app_registry.html:35 -#, fuzzy -#| msgid "Aktion" -msgid "Aktion EN" -msgstr "Action" +#: workflows/templates/workflows/app_registry.html:160 +msgid "Beschreibung" +msgstr "" -#: workflows/templates/workflows/app_registry.html:78 +#: workflows/templates/workflows/app_registry.html:164 +#, fuzzy +#| msgid "Aktionen" +msgid "Aktionslabel" +msgstr "Actions" + +#: workflows/templates/workflows/app_registry.html:169 +#: workflows/templates/workflows/branding_settings.html:152 +#, fuzzy +#| msgid "English label" +msgid "English" +msgstr "English label" + +#: workflows/templates/workflows/app_registry.html:171 +msgid "Title" +msgstr "" + +#: workflows/templates/workflows/app_registry.html:175 +msgid "Description" +msgstr "" + +#: workflows/templates/workflows/app_registry.html:179 +#, fuzzy +#| msgid "Aktionen" +msgid "Action label" +msgstr "Actions" + +#: workflows/templates/workflows/app_registry.html:191 msgid "" "Empfehlung: Produktweite Apps sparsam halten, kundenbezogene Prozesse unter " "Apps oder Admin Apps einordnen." msgstr "" -#: workflows/templates/workflows/app_registry.html:79 +#: workflows/templates/workflows/app_registry.html:192 +msgid "Keine ungespeicherten Änderungen" +msgstr "" + +#: workflows/templates/workflows/app_registry.html:194 #, fuzzy #| msgid "Regeln speichern" msgid "App Registry speichern" @@ -995,13 +1128,6 @@ msgstr "" msgid "Aktion" msgstr "Action" -#: workflows/templates/workflows/audit_log.html:25 -#: workflows/templates/workflows/requests_dashboard.html:130 -#: workflows/templates/workflows/requests_dashboard.html:138 -#: workflows/templates/workflows/requests_dashboard.html:147 -msgid "Alle" -msgstr "" - #: workflows/templates/workflows/audit_log.html:32 #: workflows/templates/workflows/audit_log.html:53 msgid "Nutzer" @@ -1147,12 +1273,6 @@ msgstr "Uploaded" msgid "Vorbereitet" msgstr "Prepared" -#: workflows/templates/workflows/backup_recovery.html:74 -#, fuzzy -#| msgid "Deaktivieren" -msgid "Deaktiviert" -msgstr "Disabled" - #: workflows/templates/workflows/backup_recovery.html:76 msgid "Lokal" msgstr "Local" @@ -1234,49 +1354,83 @@ msgid "Portalname, Firmenauftritt, Logo und PDF-Briefkopf zentral verwalten." msgstr "" "Manage portal name, company branding, logo, and PDF letterhead centrally." -#: workflows/templates/workflows/branding_settings.html:32 +#: workflows/templates/workflows/branding_settings.html:23 +msgid "Identität" +msgstr "" + +#: workflows/templates/workflows/branding_settings.html:24 +msgid "Titel, Firmenname und zentrale Spracheinstellungen." +msgstr "" + +#: workflows/templates/workflows/branding_settings.html:38 msgid "" "Wird für E-Mail-Vorschläge und Domain-bezogene Standardtexte verwendet, z. " "B. tub.co." msgstr "" -#: workflows/templates/workflows/branding_settings.html:41 -msgid "Wird für ausgehende System-E-Mails als Anzeigename verwendet." +#: workflows/templates/workflows/branding_settings.html:53 +msgid "Farben & Erscheinungsbild" msgstr "" -#: workflows/templates/workflows/branding_settings.html:78 +#: workflows/templates/workflows/branding_settings.html:54 +msgid "Zentrale visuelle Markenwerte und Browser-Icon." +msgstr "" + +#: workflows/templates/workflows/branding_settings.html:68 msgid "Erlaubte Formate: SVG, PNG, JPG, JPEG, WEBP. Maximal 5 MB." msgstr "" -#: workflows/templates/workflows/branding_settings.html:81 +#: workflows/templates/workflows/branding_settings.html:71 msgid "Aktuelles Logo:" msgstr "Current logo:" -#: workflows/templates/workflows/branding_settings.html:81 -#: workflows/templates/workflows/branding_settings.html:90 -#: workflows/templates/workflows/branding_settings.html:99 +#: workflows/templates/workflows/branding_settings.html:71 +#: workflows/templates/workflows/branding_settings.html:80 +#: workflows/templates/workflows/branding_settings.html:128 msgid "öffnen" msgstr "open" -#: workflows/templates/workflows/branding_settings.html:87 -msgid "Erlaubtes Format: PDF. Maximal 10 MB." -msgstr "" - -#: workflows/templates/workflows/branding_settings.html:90 -msgid "Aktueller Briefkopf:" -msgstr "Current letterhead:" - -#: workflows/templates/workflows/branding_settings.html:96 +#: workflows/templates/workflows/branding_settings.html:77 msgid "Erlaubte Formate: ICO, PNG, SVG, WEBP. Maximal 2 MB." msgstr "" -#: workflows/templates/workflows/branding_settings.html:99 +#: workflows/templates/workflows/branding_settings.html:80 #, fuzzy #| msgid "Aktuelles Logo:" msgid "Aktuelles Favicon:" msgstr "Current logo:" -#: workflows/templates/workflows/branding_settings.html:104 +#: workflows/templates/workflows/branding_settings.html:109 +#, fuzzy +#| msgid "Produktion" +msgid "Kommunikation" +msgstr "Production" + +#: workflows/templates/workflows/branding_settings.html:110 +msgid "Absender, Support und PDF-Branding für ausgehende Kommunikation." +msgstr "" + +#: workflows/templates/workflows/branding_settings.html:120 +msgid "Wird für ausgehende System-E-Mails als Anzeigename verwendet." +msgstr "" + +#: workflows/templates/workflows/branding_settings.html:125 +msgid "Erlaubtes Format: PDF. Maximal 10 MB." +msgstr "" + +#: workflows/templates/workflows/branding_settings.html:128 +msgid "Aktueller Briefkopf:" +msgstr "Current letterhead:" + +#: workflows/templates/workflows/branding_settings.html:136 +msgid "Footer & Rechtliches" +msgstr "" + +#: workflows/templates/workflows/branding_settings.html:137 +msgid "Gemeinsame Footer-Texte und rechtliche Hinweise für die Shell." +msgstr "" + +#: workflows/templates/workflows/branding_settings.html:166 msgid "" "TUBCO bleibt als Standard erhalten, bis hier Werte geändert oder Dateien " "hochgeladen werden." @@ -1284,7 +1438,7 @@ msgstr "" "TUBCO remains the default until values are changed or files are uploaded " "here." -#: workflows/templates/workflows/branding_settings.html:105 +#: workflows/templates/workflows/branding_settings.html:167 msgid "Branding speichern" msgstr "Save branding" @@ -1334,11 +1488,6 @@ msgstr "Technical value (optional)" msgid "Option hinzufügen" msgstr "Add option" -#: workflows/templates/workflows/form_builder.html:87 -#: workflows/templates/workflows/intro_builder.html:58 -msgid "Sortierung" -msgstr "Sort order" - #: workflows/templates/workflows/form_builder.html:89 #: workflows/templates/workflows/form_builder.html:134 msgid "Label (EN)" @@ -1534,47 +1683,47 @@ msgstr "" "Central workspace for requests, PDF generation, email workflows, and storage " "in Nextcloud." -#: workflows/templates/workflows/home.html:38 +#: workflows/templates/workflows/home.html:39 msgid "Rolle:" msgstr "Role:" -#: workflows/templates/workflows/home.html:40 +#: workflows/templates/workflows/home.html:41 msgid "Nextcloud:" msgstr "Nextcloud:" -#: workflows/templates/workflows/home.html:40 +#: workflows/templates/workflows/home.html:41 #: workflows/templates/workflows/integrations_setup.html:60 #: workflows/templates/workflows/user_management.html:103 msgid "aktiv" msgstr "active" -#: workflows/templates/workflows/home.html:40 +#: workflows/templates/workflows/home.html:41 #: workflows/templates/workflows/integrations_setup.html:60 #: workflows/templates/workflows/user_management.html:103 msgid "inaktiv" msgstr "inactive" -#: workflows/templates/workflows/home.html:43 +#: workflows/templates/workflows/home.html:44 #: workflows/templates/workflows/offboarding_success.html:20 #: workflows/templates/workflows/onboarding_success.html:20 msgid "E-Mail:" msgstr "Email:" -#: workflows/templates/workflows/home.html:43 +#: workflows/templates/workflows/home.html:44 #: workflows/templates/workflows/integrations_setup.html:122 msgid "Testmodus" msgstr "Test mode" -#: workflows/templates/workflows/home.html:43 +#: workflows/templates/workflows/home.html:44 #: workflows/templates/workflows/integrations_setup.html:122 msgid "Produktion" msgstr "Production" -#: workflows/templates/workflows/home.html:45 +#: workflows/templates/workflows/home.html:46 msgid "PDF + E-Mail Workflow bereit" msgstr "PDF + Email Workflow Ready" -#: workflows/templates/workflows/home.html:94 +#: workflows/templates/workflows/home.html:96 msgid "Tipp: Die letzten Vorgänge sehen Sie jederzeit im Anfragen Dashboard." msgstr "Tip: You can always see the latest requests in the Requests Dashboard." @@ -2141,7 +2290,7 @@ msgid "Dienstliche E-Mail" msgstr "Work email" #: workflows/templates/workflows/onboarding_intro_session.html:31 -#: workflows/views.py:869 +#: workflows/views.py:875 msgid "Vertragsbeginn" msgstr "Contract start" @@ -2806,7 +2955,7 @@ msgstr "Devices, software, and access" msgid "Notizen und Freigabe" msgstr "Notes and approval" -#: workflows/views.py:129 workflows/views.py:955 workflows/views.py:960 +#: workflows/views.py:129 workflows/views.py:961 workflows/views.py:966 msgid "Sie haben keine Berechtigung für diese Aktion." msgstr "You do not have permission for this action." @@ -3024,22 +3173,22 @@ msgstr "Save welcome settings" msgid "App-Registry gespeichert" msgstr "Request saved" -#: workflows/views.py:386 +#: workflows/views.py:392 #, fuzzy #| msgid "Anfrage gespeichert" msgid "App-Registry gespeichert." msgstr "Request saved" -#: workflows/views.py:485 +#: workflows/views.py:491 msgid "Für diesen Benutzer ist keine E-Mail-Adresse hinterlegt." msgstr "" -#: workflows/views.py:494 +#: workflows/views.py:500 #, python-format msgid "Zugangseinladung für %(username)s" msgstr "" -#: workflows/views.py:496 +#: workflows/views.py:502 #, python-format msgid "" "Hallo %(name)s,\n" @@ -3052,12 +3201,12 @@ msgid "" "Ihrem Administrator." msgstr "" -#: workflows/views.py:507 +#: workflows/views.py:513 #, python-format msgid "Passwort zurücksetzen für %(username)s" msgstr "" -#: workflows/views.py:509 +#: workflows/views.py:515 #, python-format msgid "" "Hallo %(name)s,\n" @@ -3070,7 +3219,7 @@ msgid "" "ignorieren." msgstr "" -#: workflows/views.py:547 +#: workflows/views.py:553 #, fuzzy #| msgid "" #| "Benutzer konnte nicht erstellt werden. Bitte prüfen Sie die Eingaben." @@ -3078,23 +3227,23 @@ msgid "" "Branding konnte nicht gespeichert werden. Bitte prüfen Sie die Eingaben." msgstr "User could not be created. Please check the input." -#: workflows/views.py:573 +#: workflows/views.py:579 #, fuzzy #| msgid "Offboarding-Anfrage speichern" msgid "Portal-Branding wurde gespeichert." msgstr "Save offboarding request" -#: workflows/views.py:589 +#: workflows/views.py:595 msgid "Benutzer konnte nicht erstellt werden. Bitte prüfen Sie die Eingaben." msgstr "User could not be created. Please check the input." -#: workflows/views.py:602 +#: workflows/views.py:608 #, fuzzy, python-format #| msgid "Benutzer wurde erstellt: %(username)s" msgid "Benutzer wurde erstellt und eingeladen: %(username)s" msgstr "User created: %(username)s" -#: workflows/views.py:624 +#: workflows/views.py:630 #, fuzzy #| msgid "" #| "Der aktuell angemeldete Super Admin kann sich hier nicht selbst sperren " @@ -3105,14 +3254,14 @@ msgid "" msgstr "" "The currently signed-in super admin cannot lock or downgrade themselves here." -#: workflows/views.py:627 +#: workflows/views.py:633 msgid "" "Der aktuell angemeldete Super Admin kann sich hier nicht selbst sperren oder " "herabstufen." msgstr "" "The currently signed-in super admin cannot lock or downgrade themselves here." -#: workflows/views.py:630 +#: workflows/views.py:636 #, fuzzy #| msgid "" #| "Der aktuell angemeldete Super Admin kann sich hier nicht selbst sperren " @@ -3123,7 +3272,7 @@ msgid "" msgstr "" "The currently signed-in super admin cannot lock or downgrade themselves here." -#: workflows/views.py:633 +#: workflows/views.py:639 #, fuzzy #| msgid "" #| "Der aktuell angemeldete Super Admin kann sich hier nicht selbst sperren " @@ -3134,18 +3283,18 @@ msgid "" msgstr "" "The currently signed-in super admin cannot lock or downgrade themselves here." -#: workflows/views.py:650 +#: workflows/views.py:656 #, python-format msgid "Benutzer wurde aktualisiert: %(username)s" msgstr "User updated: %(username)s" -#: workflows/views.py:672 +#: workflows/views.py:678 #, fuzzy, python-format #| msgid "Benutzer wurde erstellt: %(username)s" msgid "Passwort-Reset-Link wurde versendet: %(username)s" msgstr "User created: %(username)s" -#: workflows/views.py:684 +#: workflows/views.py:690 #, fuzzy #| msgid "" #| "Der aktuell angemeldete Super Admin kann sich hier nicht selbst sperren " @@ -3155,7 +3304,7 @@ msgid "" msgstr "" "The currently signed-in super admin cannot lock or downgrade themselves here." -#: workflows/views.py:687 +#: workflows/views.py:693 #, fuzzy #| msgid "" #| "Der aktuell angemeldete Super Admin kann sich hier nicht selbst sperren " @@ -3165,7 +3314,7 @@ msgid "" msgstr "" "The currently signed-in super admin cannot lock or downgrade themselves here." -#: workflows/views.py:690 +#: workflows/views.py:696 #, fuzzy #| msgid "" #| "Der aktuell angemeldete Super Admin kann sich hier nicht selbst sperren " @@ -3174,7 +3323,7 @@ msgid "Der letzte aktive Platform Owner kann nicht gelöscht werden." msgstr "" "The currently signed-in super admin cannot lock or downgrade themselves here." -#: workflows/views.py:693 +#: workflows/views.py:699 #, fuzzy #| msgid "" #| "Der aktuell angemeldete Super Admin kann sich hier nicht selbst sperren " @@ -3183,124 +3332,134 @@ msgid "Der letzte aktive Super Admin kann nicht gelöscht werden." msgstr "" "The currently signed-in super admin cannot lock or downgrade themselves here." -#: workflows/views.py:706 +#: workflows/views.py:712 #, fuzzy, python-format #| msgid "Benutzer wurde erstellt: %(username)s" msgid "Benutzer wurde gelöscht: %(username)s" msgstr "User created: %(username)s" -#: workflows/views.py:793 +#: workflows/views.py:799 #, python-format msgid "Backup wurde erstellt: %(name)s" msgstr "" -#: workflows/views.py:795 +#: workflows/views.py:801 #, python-format msgid "Backup konnte nicht erstellt werden: %(error)s" msgstr "" -#: workflows/views.py:811 +#: workflows/views.py:817 #, python-format msgid "Backup wurde verifiziert: %(name)s" msgstr "" -#: workflows/views.py:813 +#: workflows/views.py:819 #, python-format msgid "Backup-Verifikation fehlgeschlagen: %(error)s" msgstr "" -#: workflows/views.py:829 +#: workflows/views.py:835 #, python-format msgid "Backup wurde gelöscht: %(name)s" msgstr "" -#: workflows/views.py:831 +#: workflows/views.py:837 #, python-format msgid "Backup konnte nicht gelöscht werden: %(error)s" msgstr "" -#: workflows/views.py:857 +#: workflows/views.py:863 #, fuzzy #| msgid "Anfrage gespeichert" msgid "Anfrage erstellt" msgstr "Request saved" -#: workflows/views.py:859 +#: workflows/views.py:865 #, fuzzy, python-format #| msgid "Sitzungsstatus" msgid "Status: %(status)s" msgstr "Session status" -#: workflows/views.py:871 +#: workflows/views.py:877 #, fuzzy #| msgid "Geplant für" msgid "Geplanter Start" msgstr "Scheduled for" -#: workflows/views.py:881 +#: workflows/views.py:887 msgid "Geräteübergabe / Hardware-Abholung" msgstr "" -#: workflows/views.py:883 +#: workflows/views.py:889 msgid "Geplanter Hardware-Termin" msgstr "" -#: workflows/views.py:892 +#: workflows/views.py:898 #, fuzzy #| msgid "Noch nicht verfügbar" msgid "PDF verfügbar" msgstr "Not available yet" -#: workflows/views.py:918 +#: workflows/views.py:924 #, fuzzy #| msgid "Einweisung" msgid "Einweisungssitzung" msgstr "Introduction" -#: workflows/views.py:930 +#: workflows/views.py:936 #, fuzzy #| msgid "Welcome E-Mails" msgid "Welcome E-Mail" msgstr "Welcome Emails" -#: workflows/views.py:969 +#: workflows/views.py:975 msgid "Keine Einträge ausgewählt." msgstr "No entries selected." -#: workflows/views.py:1012 +#: workflows/views.py:1018 #, python-format msgid "%(count)s Eintrag/Einträge gelöscht." msgstr "%(count)s entry/entries deleted." -#: workflows/views.py:1014 +#: workflows/views.py:1020 #, python-format msgid "%(count)s Auswahl(en) konnten nicht verarbeitet werden." msgstr "%(count)s selection(s) could not be processed." -#: workflows/views.py:1016 +#: workflows/views.py:1022 msgid "Keine passenden Einträge gefunden." msgstr "No matching entries found." -#: workflows/views.py:1244 +#: workflows/views.py:1250 msgid "Einweisungs- und Übergabeprotokoll wurde erzeugt." msgstr "Introduction and handover protocol was generated." -#: workflows/views.py:1261 +#: workflows/views.py:1267 msgid "Einweisungsprotokoll aus Live-Status wurde erzeugt." msgstr "Introduction protocol from live status was generated." -#: workflows/views.py:1290 +#: workflows/views.py:1296 msgid "Einweisung wurde zurückgesetzt." msgstr "Introduction was reset." -#: workflows/views.py:1304 +#: workflows/views.py:1310 msgid "Einweisung wurde als abgeschlossen gespeichert." msgstr "Introduction was saved as completed." -#: workflows/views.py:1317 +#: workflows/views.py:1323 msgid "Einweisung wurde als Entwurf gespeichert." msgstr "Introduction was saved as draft." +#, fuzzy +#~| msgid "Aktion" +#~ msgid "Aktion DE" +#~ msgstr "Action" + +#, fuzzy +#~| msgid "Aktion" +#~ msgid "Aktion EN" +#~ msgstr "Action" + #, fuzzy #~| msgid "Produktion" #~ msgid "Product Owner" diff --git a/backend/workflows/admin.py b/backend/workflows/admin.py index 1064d62..e1e0660 100644 --- a/backend/workflows/admin.py +++ b/backend/workflows/admin.py @@ -27,11 +27,21 @@ class PortalBrandingAdmin(admin.ModelAdmin): @admin.register(PortalAppConfig) class PortalAppConfigAdmin(admin.ModelAdmin): - list_display = ('key', 'section', 'sort_order', 'is_enabled', 'updated_at') - list_filter = ('section', 'is_enabled') + list_display = ( + 'key', + 'section', + 'sort_order', + 'is_enabled', + 'visible_to_super_admin', + 'visible_to_admin', + 'visible_to_it_staff', + 'visible_to_staff', + 'updated_at', + ) + list_filter = ('section', 'is_enabled', 'visible_to_super_admin', 'visible_to_admin', 'visible_to_it_staff', 'visible_to_staff') search_fields = ('key', 'title_override', 'title_override_en') ordering = ('section', 'sort_order', 'key') - list_editable = ('section', 'sort_order', 'is_enabled') + list_editable = ('section', 'sort_order', 'is_enabled', 'visible_to_super_admin', 'visible_to_admin', 'visible_to_it_staff', 'visible_to_staff') @admin.register(OnboardingRequest) diff --git a/backend/workflows/app_registry.py b/backend/workflows/app_registry.py index 73290e9..781ad8b 100644 --- a/backend/workflows/app_registry.py +++ b/backend/workflows/app_registry.py @@ -6,7 +6,7 @@ from django.urls import reverse from django.utils.translation import gettext_lazy as _ from .models import PortalAppConfig -from .roles import user_has_capability +from .roles import ROLE_ADMIN, ROLE_IT_STAFF, ROLE_LABELS, ROLE_PLATFORM_OWNER, ROLE_STAFF, ROLE_SUPER_ADMIN, get_user_role_key, user_has_capability @dataclass(frozen=True) @@ -160,6 +160,108 @@ APP_DEFINITIONS: tuple[AppDefinition, ...] = ( ) +DEFAULT_ROLE_VISIBILITY = { + 'onboarding': { + ROLE_SUPER_ADMIN: True, + ROLE_ADMIN: True, + ROLE_IT_STAFF: True, + ROLE_STAFF: True, + }, + 'offboarding': { + ROLE_SUPER_ADMIN: True, + ROLE_ADMIN: True, + ROLE_IT_STAFF: True, + ROLE_STAFF: True, + }, + 'requests_dashboard': { + ROLE_SUPER_ADMIN: True, + ROLE_ADMIN: True, + ROLE_IT_STAFF: True, + ROLE_STAFF: False, + }, + 'branding': { + ROLE_SUPER_ADMIN: False, + ROLE_ADMIN: False, + ROLE_IT_STAFF: False, + ROLE_STAFF: False, + }, + 'app_registry': { + ROLE_SUPER_ADMIN: False, + ROLE_ADMIN: False, + ROLE_IT_STAFF: False, + ROLE_STAFF: False, + }, + 'integrations': { + ROLE_SUPER_ADMIN: True, + ROLE_ADMIN: True, + ROLE_IT_STAFF: False, + ROLE_STAFF: False, + }, + 'users': { + ROLE_SUPER_ADMIN: True, + ROLE_ADMIN: False, + ROLE_IT_STAFF: False, + ROLE_STAFF: False, + }, + 'audit_log': { + ROLE_SUPER_ADMIN: True, + ROLE_ADMIN: True, + ROLE_IT_STAFF: False, + ROLE_STAFF: False, + }, + 'backups': { + ROLE_SUPER_ADMIN: True, + ROLE_ADMIN: True, + ROLE_IT_STAFF: False, + ROLE_STAFF: False, + }, + 'welcome_emails': { + ROLE_SUPER_ADMIN: True, + ROLE_ADMIN: True, + ROLE_IT_STAFF: False, + ROLE_STAFF: False, + }, + 'form_builder': { + ROLE_SUPER_ADMIN: True, + ROLE_ADMIN: True, + ROLE_IT_STAFF: False, + ROLE_STAFF: False, + }, + 'intro_builder': { + ROLE_SUPER_ADMIN: True, + ROLE_ADMIN: True, + ROLE_IT_STAFF: False, + ROLE_STAFF: False, + }, + 'handbook': { + ROLE_SUPER_ADMIN: True, + ROLE_ADMIN: True, + ROLE_IT_STAFF: False, + ROLE_STAFF: False, + }, + 'django_admin': { + ROLE_SUPER_ADMIN: False, + ROLE_ADMIN: False, + ROLE_IT_STAFF: False, + ROLE_STAFF: False, + }, +} + + +def _default_visibility_summary(definition_key: str) -> str: + visibility = DEFAULT_ROLE_VISIBILITY.get(definition_key, {}) + enabled_roles = [ + role + for role in (ROLE_SUPER_ADMIN, ROLE_ADMIN, ROLE_IT_STAFF, ROLE_STAFF) + if visibility.get(role) + ] + if not enabled_roles: + return str(_('Nur Platform')) + if enabled_roles == [ROLE_SUPER_ADMIN, ROLE_ADMIN, ROLE_IT_STAFF, ROLE_STAFF]: + return str(_('Alle Firmenrollen')) + return ' + '.join(str(ROLE_LABELS[role]) for role in enabled_roles if role in ROLE_LABELS) + + SECTION_META = { PortalAppConfig.SECTION_APP: { 'title': _('Apps'), @@ -184,12 +286,17 @@ SECTION_META = { def ensure_portal_app_configs() -> None: for index, definition in enumerate(APP_DEFINITIONS): + visibility = DEFAULT_ROLE_VISIBILITY.get(definition.key, {}) PortalAppConfig.objects.get_or_create( key=definition.key, defaults={ 'section': definition.section, 'sort_order': index, 'is_enabled': True, + 'visible_to_super_admin': visibility.get(ROLE_SUPER_ADMIN, False), + 'visible_to_admin': visibility.get(ROLE_ADMIN, False), + 'visible_to_it_staff': visibility.get(ROLE_IT_STAFF, False), + 'visible_to_staff': visibility.get(ROLE_STAFF, False), }, ) @@ -206,6 +313,7 @@ def get_portal_app_registry_rows() -> list[dict[str, object]]: 'config': config, 'default_section': definition.section, 'default_sort_order': index, + 'default_visibility_summary': _default_visibility_summary(definition.key), } ) return rows @@ -215,6 +323,7 @@ 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} + role_key = get_user_role_key(user) for definition in APP_DEFINITIONS: config = config_map.get(definition.key) @@ -222,6 +331,15 @@ def build_portal_app_sections(user) -> list[dict[str, object]]: continue if definition.capability and not user_has_capability(user, definition.capability): continue + if role_key != ROLE_PLATFORM_OWNER: + if role_key == ROLE_SUPER_ADMIN and not config.visible_to_super_admin: + continue + if role_key == ROLE_ADMIN and not config.visible_to_admin: + continue + if role_key == ROLE_IT_STAFF and not config.visible_to_it_staff: + continue + if role_key == ROLE_STAFF and not config.visible_to_staff: + continue grouped[config.section].append( { 'key': definition.key, diff --git a/backend/workflows/migrations/0041_portalappconfig_visible_to_admin_and_more.py b/backend/workflows/migrations/0041_portalappconfig_visible_to_admin_and_more.py new file mode 100644 index 0000000..037e7f3 --- /dev/null +++ b/backend/workflows/migrations/0041_portalappconfig_visible_to_admin_and_more.py @@ -0,0 +1,33 @@ +# Generated by Django 5.1.5 on 2026-03-26 11:32 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('workflows', '0040_portalbranding_favicon_image_and_more'), + ] + + operations = [ + migrations.AddField( + model_name='portalappconfig', + name='visible_to_admin', + field=models.BooleanField(default=True), + ), + migrations.AddField( + model_name='portalappconfig', + name='visible_to_it_staff', + field=models.BooleanField(default=False), + ), + migrations.AddField( + model_name='portalappconfig', + name='visible_to_staff', + field=models.BooleanField(default=False), + ), + migrations.AddField( + model_name='portalappconfig', + name='visible_to_super_admin', + field=models.BooleanField(default=True), + ), + ] diff --git a/backend/workflows/models.py b/backend/workflows/models.py index 54e3091..5b0852c 100644 --- a/backend/workflows/models.py +++ b/backend/workflows/models.py @@ -86,6 +86,10 @@ class PortalAppConfig(models.Model): section = models.CharField(max_length=20, choices=SECTION_CHOICES, default=SECTION_APP) sort_order = models.PositiveIntegerField(default=0) is_enabled = models.BooleanField(default=True) + visible_to_super_admin = models.BooleanField(default=True) + visible_to_admin = models.BooleanField(default=True) + visible_to_it_staff = models.BooleanField(default=False) + visible_to_staff = models.BooleanField(default=False) title_override = models.CharField(max_length=255, blank=True) title_override_en = models.CharField(max_length=255, blank=True) description_override = models.TextField(blank=True) diff --git a/backend/workflows/static/workflows/css/admin_tools.css b/backend/workflows/static/workflows/css/admin_tools.css index 35878d6..f05f1f8 100644 --- a/backend/workflows/static/workflows/css/admin_tools.css +++ b/backend/workflows/static/workflows/css/admin_tools.css @@ -65,6 +65,30 @@ th { background: #f6f9ff; color: #334155; } .app-registry-table select, .app-registry-table textarea { min-width: 160px; } .app-registry-table td { background: rgba(255,255,255,0.9); } +.app-registry-cards { display: grid; gap: 14px; } +.app-registry-filters { display: grid; grid-template-columns: minmax(260px, 1.5fr) repeat(2, minmax(180px, 0.7fr)); gap: 12px; margin-bottom: 14px; } +.app-registry-card { border: 1px solid #d9e4f1; border-radius: 18px; background: linear-gradient(180deg, rgba(255,255,255,0.98), rgba(246,250,255,0.95)); padding: 16px; box-shadow: inset 0 1px 0 rgba(255,255,255,0.94); transition: border-color 180ms cubic-bezier(0.2, 0.8, 0.2, 1), box-shadow 220ms cubic-bezier(0.2, 0.8, 0.2, 1), transform 220ms cubic-bezier(0.2, 0.8, 0.2, 1), opacity 180ms cubic-bezier(0.2, 0.8, 0.2, 1); } +.app-registry-card:hover { transform: translateY(-1px); box-shadow: 0 12px 24px rgba(16, 32, 57, 0.06); border-color: #c9d8eb; } +.app-registry-card.is-disabled { opacity: 0.84; } +.app-registry-card[hidden] { display: none !important; } +.app-registry-card-head { display: flex; justify-content: space-between; align-items: start; gap: 14px; margin-bottom: 14px; } +.app-registry-card-title-row { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-bottom: 4px; } +.app-registry-card-title-row h2 { margin: 0; color: #17345e; font-size: 19px; } +.app-registry-card-copy { margin: 8px 0 0; color: #60738d; max-width: 760px; } +.app-registry-summary { display: grid; grid-template-columns: minmax(0, 1.5fr) minmax(260px, 0.9fr); gap: 16px; align-items: center; list-style: none; cursor: pointer; } +.app-registry-summary::-webkit-details-marker { display: none; } +.app-registry-summary::marker { display: none; } +.app-registry-summary-main { min-width: 0; } +.app-registry-summary-meta { display: flex; gap: 8px; flex-wrap: wrap; justify-content: flex-end; align-items: center; } +.app-registry-card-grid { display: grid; grid-template-columns: repeat(2, minmax(260px, 1fr)); gap: 12px; align-items: start; } +.app-registry-card .app-registry-card-grid { margin-top: 14px; padding-top: 14px; border-top: 1px solid #dce6f2; } +.app-registry-panel { border: 1px solid #dce6f2; border-radius: 14px; background: rgba(255,255,255,0.86); padding: 12px; } +.app-registry-panel h3 { margin: 0 0 10px; color: #213a61; font-size: 15px; } +.app-registry-panel h4 { margin: 0 0 10px; color: #223b63; font-size: 14px; } +.app-registry-checks { gap: 10px 14px; } +.app-registry-checks label { min-width: 130px; padding: 8px 10px; border: 1px solid #d7e2ef; border-radius: 12px; background: #f8fbff; } +.app-registry-copy-panel { grid-column: 1 / -1; } +.app-registry-savebar { position: sticky; bottom: 14px; z-index: 5; display: flex; justify-content: space-between; align-items: center; gap: 12px; margin-top: 16px; padding: 12px 14px; border: 1px solid #cad8ea; border-radius: 16px; background: rgba(255,255,255,0.95); box-shadow: 0 12px 24px rgba(16, 32, 57, 0.08); backdrop-filter: blur(8px); } .template-block { border: 1px solid #d8e3f0; border-radius: 10px; background: #fff; padding: 10px; margin-top: 10px; } .template-title, .rule-title { margin: 0 0 8px; color: #24344e; font-weight: 700; font-size: 14px; } .rule-card { margin-top: 12px; border: 1px solid #d8e3f0; border-radius: 12px; padding: 10px; background: #fff; } @@ -89,4 +113,10 @@ th { background: #f6f9ff; color: #334155; } .grid { grid-template-columns: 1fr; } .branding-preview-header { flex-direction: column; align-items: flex-start; } .branding-preview-band { flex-wrap: wrap; } + .app-registry-filters { grid-template-columns: 1fr; } + .app-registry-summary { grid-template-columns: 1fr; } + .app-registry-summary-meta { justify-content: flex-start; } + .app-registry-card-grid { grid-template-columns: 1fr; } + .app-registry-copy-panel { grid-column: auto; } + .app-registry-savebar { align-items: stretch; flex-direction: column; } } diff --git a/backend/workflows/templates/workflows/app_registry.html b/backend/workflows/templates/workflows/app_registry.html index dea7d93..d34155e 100644 --- a/backend/workflows/templates/workflows/app_registry.html +++ b/backend/workflows/templates/workflows/app_registry.html @@ -1,5 +1,6 @@ {% extends 'workflows/base_shell.html' %} {% load static i18n %} +{% trans "Ungespeicherte Änderungen" as dirty_state_label %} {% block title %}{% trans "App Registry" %}{% endblock %} @@ -21,63 +22,220 @@
{% csrf_token %} -
- - - - - - - - - - - - - - - {% for row in rows %} - - - - - - - - - - - {% endfor %} - -
{% trans "Key" %}{% trans "Aktiv" %}{% trans "Bereich" %}{% trans "Reihenfolge" %}{% trans "Titel DE" %}{% trans "Titel EN" %}{% trans "Aktion DE" %}{% trans "Aktion EN" %}
-
{{ row.config.key }}
-
{{ row.definition.title }}
-
- - - - - - - - - - - - - - - -
+
+
+ + +
+
+ + +
+
+ + +
+
+
+ {% for row in rows %} +
+ +
+
+

{{ row.definition.title }}

+ {% if row.config.is_enabled %} + {% trans "Aktiv" %} + {% else %} + {% trans "Deaktiviert" %} + {% endif %} +
+
{{ row.config.key }}
+

{{ row.definition.description }}

+

{% trans "Empfohlener Standardzugriff:" %} {{ row.default_visibility_summary }}

+
+
+ + {% if row.config.section == 'platform_apps' %} + {% trans "Platform Apps" %} + {% elif row.config.section == 'admin_apps' %} + {% trans "Admin Apps" %} + {% else %} + {% trans "Apps" %} + {% endif %} + + {% trans "Sortierung" %}: {{ row.config.sort_order }} + {% if not row.config.visible_to_super_admin and not row.config.visible_to_admin and not row.config.visible_to_it_staff and not row.config.visible_to_staff %} + {% trans "Platform only" %} + {% elif row.config.visible_to_super_admin and row.config.visible_to_admin and row.config.visible_to_it_staff and row.config.visible_to_staff %} + {% trans "Alle Firmenrollen" %} + {% else %} + + {% if row.config.visible_to_super_admin %}{% trans "Super Admin" %}{% endif %} + {% if row.config.visible_to_admin %}{% if row.config.visible_to_super_admin %} + {% endif %}{% trans "Admin" %}{% endif %} + {% if row.config.visible_to_it_staff %}{% if row.config.visible_to_super_admin or row.config.visible_to_admin %} + {% endif %}{% trans "IT Staff" %}{% endif %} + {% if row.config.visible_to_staff %}{% if row.config.visible_to_super_admin or row.config.visible_to_admin or row.config.visible_to_it_staff %} + {% endif %}{% trans "Staff" %}{% endif %} + + {% endif %} +
+
+ +
+
+

{% trans "Verfügbarkeit" %}

+
+ +
+

{% trans "Deaktivierte Apps erscheinen nicht auf der Landing Page, selbst wenn Rollen sie sehen dürften." %}

+
+ +
+

{% trans "Sichtbarkeit nach Rolle" %}

+
+ + + + +
+

{% trans "Wenn keine Firmenrolle aktiv ist, bleibt die App nur für die Platform sichtbar." %}

+
+ +
+

{% trans "Platzierung" %}

+
+
+ + +
+
+ + +
+
+
+ +
+

{% trans "Bezeichnungen & Texte" %}

+
+
+

{% trans "Deutsch" %}

+
+ + +
+
+ + +
+
+ + +
+
+
+

{% trans "English" %}

+
+ + +
+
+ + +
+
+ + +
+
+
+
+
+
+ {% endfor %}
-
-
{% trans "Empfehlung: Produktweite Apps sparsam halten, kundenbezogene Prozesse unter Apps oder Admin Apps einordnen." %}
+
+
+
{% trans "Empfehlung: Produktweite Apps sparsam halten, kundenbezogene Prozesse unter Apps oder Admin Apps einordnen." %}
+
{% trans "Keine ungespeicherten Änderungen" %}
+
{% endblock %} + +{% block extra_js %} + +{% endblock %} diff --git a/backend/workflows/templates/workflows/home.html b/backend/workflows/templates/workflows/home.html index c9387e0..7472c20 100644 --- a/backend/workflows/templates/workflows/home.html +++ b/backend/workflows/templates/workflows/home.html @@ -34,6 +34,7 @@ {% trans "Operations Console" %}

{{ portal_title }}

{% trans "Zentrale Arbeitsfläche für Anfragen, PDF-Generierung, E-Mail-Workflows und Ablage in Nextcloud." %}

+ {% if can_manage_integrations %}
{% trans "Rolle:" %} {{ role_label }} @@ -44,6 +45,7 @@ {% trans "PDF + E-Mail Workflow bereit" %}
+ {% endif %}
diff --git a/backend/workflows/views.py b/backend/workflows/views.py index f4330f2..adff632 100644 --- a/backend/workflows/views.py +++ b/backend/workflows/views.py @@ -328,6 +328,7 @@ def _build_onboarding_sections(blocks: list[dict], field_pages: dict[str, str]) @login_required def home(request): config, _ = WorkflowConfig.objects.get_or_create(name='Default') + role_key = get_user_role_key(request.user) return render( request, 'workflows/home.html', @@ -336,6 +337,7 @@ def home(request): 'email_test_mode': is_email_test_mode(), 'workflow_config': config, 'role_label': get_user_role_label(request.user), + 'role_key': role_key, 'portal_app_sections': build_portal_app_sections(request.user), }, ) @@ -364,6 +366,10 @@ def save_portal_app_registry(request): if config.section not in dict(PortalAppConfig.SECTION_CHOICES): config.section = row['default_section'] config.is_enabled = request.POST.get(f'is_enabled__{key}') == 'on' + config.visible_to_super_admin = request.POST.get(f'visible_to_super_admin__{key}') == 'on' + config.visible_to_admin = request.POST.get(f'visible_to_admin__{key}') == 'on' + config.visible_to_it_staff = request.POST.get(f'visible_to_it_staff__{key}') == 'on' + config.visible_to_staff = request.POST.get(f'visible_to_staff__{key}') == 'on' try: config.sort_order = int((request.POST.get(f'sort_order__{key}') or '').strip() or row['default_sort_order']) except ValueError: