diff --git a/backend/locale/en/LC_MESSAGES/django.mo b/backend/locale/en/LC_MESSAGES/django.mo index 46923b3..1f6f36c 100644 Binary files a/backend/locale/en/LC_MESSAGES/django.mo and b/backend/locale/en/LC_MESSAGES/django.mo differ diff --git a/backend/locale/en/LC_MESSAGES/django.po b/backend/locale/en/LC_MESSAGES/django.po index 8758ccb..8bc10ab 100644 --- a/backend/locale/en/LC_MESSAGES/django.po +++ b/backend/locale/en/LC_MESSAGES/django.po @@ -2,13 +2,243 @@ msgid "" msgstr "" "Project-Id-Version: tubco-portal\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-03-26 10:38+0000\n" +"POT-Creation-Date: 2026-03-26 10:55+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:261 workflows/models.py:342 +#: workflows/templates/workflows/onboarding_form.html:25 +#: workflows/templates/workflows/requests_dashboard.html:68 +#: workflows/templates/workflows/requests_dashboard.html:131 +msgid "Onboarding" +msgstr "Onboarding" + +#: workflows/app_registry.py:33 +msgid "" +"Neue Mitarbeitende erfassen, PDF mit Briefkopf erstellen, Benachrichtigungen " +"senden und in Nextcloud ablegen." +msgstr "" +"Capture new employees, generate a PDF with letterhead, send notifications, " +"and store it in Nextcloud." + +#: workflows/app_registry.py:34 +msgid "Onboarding starten" +msgstr "Start onboarding" + +#: workflows/app_registry.py:36 +msgid "Mehrschritt-Formular" +msgstr "Multi-step form" + +#: workflows/app_registry.py:36 +msgid "E-Mail Routing" +msgstr "Email routing" + +#: workflows/app_registry.py:43 workflows/models.py:262 workflows/models.py:343 +#: workflows/templates/workflows/requests_dashboard.html:78 +#: workflows/templates/workflows/requests_dashboard.html:132 +msgid "Offboarding" +msgstr "Offboarding" + +#: workflows/app_registry.py:44 +msgid "" +"Mitarbeitende suchen, Daten vorbefüllen, Offboarding-Dokumente erzeugen und " +"Rückgabe-Prozess starten." +msgstr "" +"Search employees, prefill data, generate offboarding documents, and start " +"the return process." + +#: workflows/app_registry.py:45 +msgid "Offboarding starten" +msgstr "Start offboarding" + +#: workflows/app_registry.py:47 +msgid "Profile-Suche" +msgstr "Profile search" + +#: workflows/app_registry.py:47 +msgid "Hardware-Liste" +msgstr "Hardware list" + +#: workflows/app_registry.py:47 +msgid "IT-Rückgabe" +msgstr "IT return" + +#: workflows/app_registry.py:54 +#: workflows/templates/workflows/requests_dashboard.html:4 +#: workflows/templates/workflows/requests_dashboard.html:33 +msgid "Anfragen Dashboard" +msgstr "Requests Dashboard" + +#: workflows/app_registry.py:55 +msgid "" +"Status, Suchfunktion, PDF-Links und Verlauf aller Onboarding-/Offboarding-" +"Anfragen." +msgstr "" +"Status, search, PDF links, and history of all onboarding/offboarding " +"requests." + +#: workflows/app_registry.py:56 +msgid "Dashboard öffnen" +msgstr "Open dashboard" + +#: workflows/app_registry.py:59 +msgid "Suche" +msgstr "Search" + +#: workflows/app_registry.py:59 +#: workflows/templates/workflows/backup_recovery.html:40 +#: workflows/templates/workflows/onboarding_intro_session.html:37 +#: workflows/templates/workflows/request_timeline.html:70 +#: workflows/templates/workflows/requests_dashboard.html:136 +#: workflows/templates/workflows/welcome_emails.html:85 +msgid "Status" +msgstr "Status" + +#: workflows/app_registry.py:59 +msgid "PDF Zugriff" +msgstr "PDF access" + +#: workflows/app_registry.py:65 +#: workflows/templates/workflows/branding_settings.html:4 +#: workflows/templates/workflows/branding_settings.html:12 +msgid "Branding" +msgstr "Branding" + +#: workflows/app_registry.py:66 +msgid "Logo, Portalname, Farben und PDF-Briefkopf verwalten." +msgstr "Manage logo, portal name, colors, and PDF letterhead." + +#: workflows/app_registry.py:67 workflows/app_registry.py:76 +#: workflows/app_registry.py:85 workflows/app_registry.py:94 +#: workflows/app_registry.py:103 workflows/app_registry.py:112 +#: workflows/app_registry.py:121 workflows/app_registry.py:130 +#: workflows/app_registry.py:139 workflows/app_registry.py:148 +#: workflows/app_registry.py:157 +msgid "Öffnen" +msgstr "Open" + +#: workflows/app_registry.py:74 +#: workflows/templates/workflows/app_registry.html:4 +#: workflows/templates/workflows/app_registry.html:12 +msgid "App Registry" +msgstr "" + +#: workflows/app_registry.py:75 +msgid "Apps zentral aktivieren, sortieren und für Kundenauftritte vorbereiten." +msgstr "" + +#: workflows/app_registry.py:83 +msgid "Integrationen" +msgstr "Integrations" + +#: workflows/app_registry.py:84 +msgid "Nextcloud- und E-Mail-Setup." +msgstr "Nextcloud and email setup." + +#: workflows/app_registry.py:92 +#: workflows/templates/workflows/user_management.html:4 +#: workflows/templates/workflows/user_management.html:14 +msgid "Benutzer & Rollen" +msgstr "Users & roles" + +#: workflows/app_registry.py:93 +msgid "Benutzer anlegen, Rollen zuweisen und Zugriffe steuern." +msgstr "Create users, assign roles, and control access." + +#: workflows/app_registry.py:101 workflows/templates/workflows/audit_log.html:4 +#: workflows/templates/workflows/audit_log.html:15 +msgid "Audit Log" +msgstr "" + +#: workflows/app_registry.py:102 +msgid "Wichtige Admin-Aktionen nachvollziehen und prüfen." +msgstr "" + +#: workflows/app_registry.py:110 +#: workflows/templates/workflows/backup_recovery.html:4 +#: workflows/templates/workflows/backup_recovery.html:12 +msgid "Backup & Recovery" +msgstr "Backup & Recovery" + +#: workflows/app_registry.py:111 +msgid "Backups erstellen und sicher verifizieren." +msgstr "" + +#: workflows/app_registry.py:119 +#: workflows/templates/workflows/welcome_emails.html:4 +msgid "Welcome E-Mails" +msgstr "Welcome Emails" + +#: workflows/app_registry.py:120 +msgid "Geplante Welcome Mails verwalten." +msgstr "Manage scheduled welcome emails." + +#: workflows/app_registry.py:128 +#: workflows/templates/workflows/form_builder.html:4 +#: workflows/templates/workflows/form_builder.html:14 +msgid "Form Builder" +msgstr "Form Builder" + +#: workflows/app_registry.py:129 +msgid "Felder, Schritte und Optionen verwalten." +msgstr "Manage fields, steps, and options." + +#: workflows/app_registry.py:137 +#: workflows/templates/workflows/intro_builder.html:4 +#: workflows/templates/workflows/intro_builder.html:17 +msgid "Einweisungs-Builder" +msgstr "Introduction Builder" + +#: workflows/app_registry.py:138 +msgid "Checklistenpunkte für das Einweisungsprotokoll konfigurieren." +msgstr "Configure checklist items for the introduction protocol." + +#: workflows/app_registry.py:146 workflows/templates/workflows/handbook.html:4 +#: workflows/templates/workflows/handbook.html:15 +msgid "Handbook" +msgstr "Handbook" + +#: workflows/app_registry.py:147 +msgid "Project wiki and developer documentation in one place." +msgstr "Project wiki and developer documentation in one place." + +#: workflows/app_registry.py:155 +msgid "Django Admin" +msgstr "Django Admin" + +#: workflows/app_registry.py:156 +msgid "Vollständige Datenverwaltung." +msgstr "Full data management." + +#: workflows/app_registry.py:165 workflows/models.py:68 +msgid "Apps" +msgstr "Apps" + +#: workflows/app_registry.py:166 +msgid "Wählen Sie den gewünschten Prozess." +msgstr "Choose the desired process." + +#: workflows/app_registry.py:171 workflows/models.py:69 +msgid "Platform Apps" +msgstr "" + +#: workflows/app_registry.py:172 +#, fuzzy +#| msgid "Konfiguration, Tests und Steuerung." +msgid "Produktweite Konfiguration und Produktsteuerung." +msgstr "Configuration, tests, and controls." + +#: workflows/app_registry.py:177 workflows/models.py:70 +msgid "Admin Apps" +msgstr "Admin Apps" + +#: workflows/app_registry.py:178 +msgid "Konfiguration, Tests und Steuerung." +msgstr "Configuration, tests, and controls." + #: workflows/backup_ops.py:141 msgid "Remote Backup ist deaktiviert." msgstr "" @@ -55,40 +285,40 @@ msgstr "" msgid "Remote Backup in Nextcloud konnte nicht gelöscht werden." msgstr "" -#: workflows/forms.py:103 workflows/forms.py:128 +#: workflows/forms.py:104 workflows/forms.py:129 #: workflows/templates/workflows/user_management.html:72 #: workflows/templates/workflows/user_management.html:170 msgid "Benutzername" msgstr "" -#: workflows/forms.py:104 +#: workflows/forms.py:105 msgid "Passwort" msgstr "Password" -#: workflows/forms.py:108 workflows/forms.py:129 +#: workflows/forms.py:109 workflows/forms.py:130 #, fuzzy #| msgid "E-Mail" msgid "E-Mail-Adresse" msgstr "Email" -#: workflows/forms.py:113 workflows/templates/workflows/user_management.html:77 +#: workflows/forms.py:114 workflows/templates/workflows/user_management.html:77 #: workflows/templates/workflows/user_management.html:108 msgid "Neues Passwort" msgstr "New password" -#: workflows/forms.py:119 +#: workflows/forms.py:120 msgid "Neues Passwort bestätigen" msgstr "Confirm new password" -#: workflows/forms.py:126 +#: workflows/forms.py:127 msgid "Vorname" msgstr "" -#: workflows/forms.py:127 +#: workflows/forms.py:128 msgid "Nachname" msgstr "" -#: workflows/forms.py:130 workflows/templates/workflows/user_management.html:74 +#: workflows/forms.py:131 workflows/templates/workflows/user_management.html:74 #: workflows/templates/workflows/user_management.html:93 #: workflows/templates/workflows/user_management.html:171 #, fuzzy @@ -96,318 +326,319 @@ msgstr "" msgid "Rolle" msgstr "Role:" -#: workflows/forms.py:144 +#: workflows/forms.py:145 msgid "Dieser Benutzername ist bereits vergeben." msgstr "This username is already taken." -#: workflows/forms.py:153 workflows/views.py:567 +#: workflows/forms.py:154 workflows/views.py:616 msgid "Ungültige Rolle." msgstr "Invalid role." -#: workflows/forms.py:155 workflows/views.py:570 +#: workflows/forms.py:156 workflows/views.py:619 msgid "Nur Platform Owner dürfen diese Rolle vergeben." msgstr "" -#: workflows/forms.py:186 +#: workflows/forms.py:188 msgid "Portal-Titel" msgstr "Portal title" -#: workflows/forms.py:187 +#: workflows/forms.py:189 msgid "Firmenname" msgstr "Company name" -#: workflows/forms.py:188 +#: workflows/forms.py:190 +#, fuzzy +#| msgid "Firmenname" +msgid "Firmen-Domain" +msgstr "Company name" + +#: workflows/forms.py:191 msgid "Support-E-Mail" msgstr "Support email" -#: workflows/forms.py:189 +#: workflows/forms.py:192 msgid "Standardsprache" msgstr "Default language" -#: workflows/forms.py:190 +#: workflows/forms.py:193 msgid "Logo" msgstr "Logo" -#: workflows/forms.py:191 +#: workflows/forms.py:194 msgid "PDF-Briefkopf" msgstr "PDF letterhead" -#: workflows/forms.py:192 +#: workflows/forms.py:195 msgid "Primärfarbe" msgstr "Primary color" -#: workflows/forms.py:193 +#: workflows/forms.py:196 msgid "Sekundärfarbe" msgstr "Secondary color" -#: workflows/forms.py:207 +#: workflows/forms.py:210 msgid "Das Logo darf maximal 5 MB groß sein." msgstr "" -#: workflows/forms.py:215 +#: workflows/forms.py:218 msgid "Der PDF-Briefkopf darf maximal 10 MB groß sein." msgstr "" -#: workflows/forms.py:458 +#: workflows/forms.py:357 workflows/forms.py:542 +#, python-format +msgid "Bitte nutzen Sie das Format name@%(domain)s." +msgstr "" + +#: workflows/forms.py:379 workflows/forms.py:556 +#, python-format +msgid "Bitte verwenden Sie eine @%(domain)s E-Mail-Adresse." +msgstr "" + +#: workflows/forms.py:464 #, python-format msgid "" "Das Übergabedatum muss mindestens %(days)s Tage in der Zukunft liegen " "(frühestens %(date)s)." msgstr "" -#: workflows/models.py:90 workflows/views.py:199 +#: workflows/models.py:139 workflows/views.py:200 msgid "Eingereicht" msgstr "Submitted" -#: workflows/models.py:91 workflows/views.py:200 +#: workflows/models.py:140 workflows/views.py:201 msgid "In Bearbeitung" msgstr "Processing" -#: workflows/models.py:92 workflows/models.py:407 workflows/views.py:201 +#: workflows/models.py:141 workflows/models.py:456 workflows/views.py:202 msgid "Abgeschlossen" msgstr "Completed" -#: workflows/models.py:93 workflows/models.py:347 +#: workflows/models.py:142 workflows/models.py:396 #: workflows/templates/workflows/backup_recovery.html:70 #: workflows/templates/workflows/requests_dashboard.html:222 -#: workflows/templates/workflows/welcome_emails.html:108 workflows/views.py:202 +#: workflows/templates/workflows/welcome_emails.html:108 workflows/views.py:203 msgid "Fehlgeschlagen" msgstr "Failed" -#: workflows/models.py:100 +#: workflows/models.py:149 msgid "Herr" msgstr "" -#: workflows/models.py:100 +#: workflows/models.py:149 msgid "Frau" msgstr "" -#: workflows/models.py:100 +#: workflows/models.py:149 msgid "Divers" msgstr "" -#: workflows/models.py:110 +#: workflows/models.py:159 msgid "befristet" msgstr "" -#: workflows/models.py:110 +#: workflows/models.py:159 msgid "unbefristet" msgstr "" -#: workflows/models.py:173 +#: workflows/models.py:222 #: workflows/templates/workflows/onboarding_intro_session.html:28 #: workflows/templates/workflows/requests_dashboard.html:145 msgid "Abteilung" msgstr "Department" -#: workflows/models.py:174 +#: workflows/models.py:223 msgid "Geräte" msgstr "" -#: workflows/models.py:175 +#: workflows/models.py:224 msgid "Software" msgstr "" -#: workflows/models.py:176 +#: workflows/models.py:225 #, fuzzy #| msgid "Vorgänge" msgid "Zugänge" msgstr "Requests" -#: workflows/models.py:177 +#: workflows/models.py:226 msgid "Workspace-Gruppen" msgstr "" -#: workflows/models.py:178 +#: workflows/models.py:227 msgid "Ressourcen" msgstr "" -#: workflows/models.py:179 +#: workflows/models.py:228 msgid "Telefonnummern" msgstr "" -#: workflows/models.py:205 +#: workflows/models.py:254 msgid "Automatisch" msgstr "" -#: workflows/models.py:206 workflows/views.py:94 +#: workflows/models.py:255 workflows/views.py:95 msgid "Stammdaten" msgstr "Master data" -#: workflows/models.py:207 workflows/views.py:95 +#: workflows/models.py:256 workflows/views.py:96 msgid "Vertrag" msgstr "Contract" -#: workflows/models.py:208 workflows/views.py:96 +#: workflows/models.py:257 workflows/views.py:97 msgid "IT-Setup" msgstr "IT setup" -#: workflows/models.py:209 workflows/views.py:97 +#: workflows/models.py:258 workflows/views.py:98 msgid "Abschluss" msgstr "Finish" -#: workflows/models.py:212 workflows/models.py:293 -#: workflows/templates/workflows/home.html:62 -#: workflows/templates/workflows/onboarding_form.html:25 -#: workflows/templates/workflows/requests_dashboard.html:68 -#: workflows/templates/workflows/requests_dashboard.html:131 -msgid "Onboarding" -msgstr "Onboarding" - -#: workflows/models.py:213 workflows/models.py:294 -#: workflows/templates/workflows/home.html:78 -#: workflows/templates/workflows/requests_dashboard.html:78 -#: workflows/templates/workflows/requests_dashboard.html:132 -msgid "Offboarding" -msgstr "Offboarding" - -#: workflows/models.py:251 +#: workflows/models.py:300 #, fuzzy #| msgid "Onboarding" msgid "Onboarding: IT" msgstr "Onboarding" -#: workflows/models.py:252 +#: workflows/models.py:301 #, fuzzy #| msgid "Offboarding-Anfrage speichern" msgid "Onboarding: Allgemeine Info" msgstr "Save offboarding request" -#: workflows/models.py:253 +#: workflows/models.py:302 #, fuzzy #| msgid "Onboarding starten" msgid "Onboarding: Visitenkarte" msgstr "Start onboarding" -#: workflows/models.py:254 +#: workflows/models.py:303 #, fuzzy #| msgid "Onboarding" msgid "Onboarding: HR Works" msgstr "Onboarding" -#: workflows/models.py:255 +#: workflows/models.py:304 #, fuzzy #| msgid "Onboarding starten" msgid "Onboarding: Schlüssel" msgstr "Start onboarding" -#: workflows/models.py:256 +#: workflows/models.py:305 msgid "Onboarding: Referenz Anfordernde Person" msgstr "" -#: workflows/models.py:257 +#: workflows/models.py:306 #, fuzzy #| msgid "Welcome E-Mails" msgid "Onboarding: Welcome E-Mail" msgstr "Welcome Emails" -#: workflows/models.py:258 +#: workflows/models.py:307 #, fuzzy #| msgid "Offboarding" msgid "Offboarding: IT" msgstr "Offboarding" -#: workflows/models.py:259 +#: workflows/models.py:308 #, fuzzy #| msgid "Offboarding-Anfrage speichern" msgid "Offboarding: Allgemeine Info" msgstr "Save offboarding request" -#: workflows/models.py:260 +#: workflows/models.py:309 #, fuzzy #| msgid "Offboarding starten" msgid "Offboarding: HR Works Deaktivierung" msgstr "Start offboarding" -#: workflows/models.py:261 +#: workflows/models.py:310 msgid "Offboarding: Referenz Anfordernde Person" msgstr "" -#: workflows/models.py:297 +#: workflows/models.py:346 msgid "Immer" msgstr "" -#: workflows/models.py:298 workflows/models.py:376 +#: workflows/models.py:347 workflows/models.py:425 msgid "Enthält" msgstr "" -#: workflows/models.py:299 workflows/models.py:377 +#: workflows/models.py:348 workflows/models.py:426 msgid "Ist gleich" msgstr "" -#: workflows/models.py:300 +#: workflows/models.py:349 msgid "Ist aktiv/Ja" msgstr "" -#: workflows/models.py:301 +#: workflows/models.py:350 #, fuzzy #| msgid "inaktiv" msgid "Ist inaktiv/Nein" msgstr "inactive" -#: workflows/models.py:343 +#: workflows/models.py:392 #: workflows/templates/workflows/welcome_emails.html:100 msgid "Geplant" msgstr "Scheduled" -#: workflows/models.py:344 +#: workflows/models.py:393 #: workflows/templates/workflows/welcome_emails.html:102 msgid "Pausiert" msgstr "Paused" -#: workflows/models.py:345 +#: workflows/models.py:394 #: workflows/templates/workflows/welcome_emails.html:104 msgid "Abgebrochen" msgstr "Cancelled" -#: workflows/models.py:346 +#: workflows/models.py:395 #: workflows/templates/workflows/welcome_emails.html:106 msgid "Gesendet" msgstr "Sent" -#: workflows/models.py:369 workflows/tasks.py:576 +#: workflows/models.py:418 workflows/tasks.py:576 msgid "Geräte und Arbeitsplatz" msgstr "Devices and workplace" -#: workflows/models.py:370 workflows/tasks.py:577 +#: workflows/models.py:419 workflows/tasks.py:577 msgid "Konten und Berechtigungen" msgstr "Accounts and permissions" -#: workflows/models.py:371 workflows/tasks.py:578 +#: workflows/models.py:420 workflows/tasks.py:578 msgid "Software und Tools" msgstr "Software and tools" -#: workflows/models.py:372 workflows/tasks.py:579 +#: workflows/models.py:421 workflows/tasks.py:579 msgid "Prozesse und Hinweise" msgstr "Processes and notes" -#: workflows/models.py:375 +#: workflows/models.py:424 msgid "Immer anzeigen" msgstr "Always show" -#: workflows/models.py:378 +#: workflows/models.py:427 msgid "Ist Ja / aktiv" msgstr "Is yes / active" -#: workflows/models.py:379 +#: workflows/models.py:428 msgid "Ist Nein / inaktiv" msgstr "Is no / inactive" -#: workflows/models.py:406 +#: workflows/models.py:455 msgid "Entwurf" msgstr "Draft" -#: workflows/models.py:426 +#: workflows/models.py:475 #, fuzzy #| msgid "Nextcloud:" msgid "Nextcloud" msgstr "Nextcloud:" -#: workflows/models.py:427 +#: workflows/models.py:476 msgid "S3" msgstr "" -#: workflows/models.py:428 +#: workflows/models.py:477 msgid "NFS" msgstr "" @@ -646,12 +877,80 @@ msgstr "" msgid "Link anfordern" msgstr "Request link" -#: workflows/templates/workflows/audit_log.html:4 -#: workflows/templates/workflows/audit_log.html:15 -#: workflows/templates/workflows/home.html:148 -msgid "Audit Log" +#: workflows/templates/workflows/app_registry.html:13 +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 +msgid "" +"Sicherheit bleibt codebasiert: Sichtbarkeit und Reihenfolge sind hier " +"steuerbar, Berechtigungen weiterhin über Rollen und Capabilities." +msgstr "" + +#: workflows/templates/workflows/app_registry.html:20 +#, fuzzy +#| msgid "Produktion" +msgid "Produktkern" +msgstr "Production" + +#: workflows/templates/workflows/app_registry.html:28 +msgid "Key" +msgstr "" + +#: workflows/templates/workflows/app_registry.html:29 +#: workflows/templates/workflows/form_builder.html:91 +#: workflows/templates/workflows/integrations_setup.html:263 +#: workflows/templates/workflows/intro_builder.html:65 +#: workflows/templates/workflows/user_management.html:75 +msgid "Aktiv" +msgstr "Active" + +#: workflows/templates/workflows/app_registry.html:30 +#, fuzzy +#| msgid "Eingereicht" +msgid "Bereich" +msgstr "Submitted" + +#: workflows/templates/workflows/app_registry.html:31 +#, fuzzy +#| msgid "Reihenfolge speichern" +msgid "Reihenfolge" +msgstr "Save order" + +#: workflows/templates/workflows/app_registry.html:32 +msgid "Titel DE" +msgstr "" + +#: workflows/templates/workflows/app_registry.html:33 +msgid "Titel EN" +msgstr "" + +#: workflows/templates/workflows/app_registry.html:34 +#, fuzzy +#| msgid "Aktion" +msgid "Aktion DE" +msgstr "Action" + +#: workflows/templates/workflows/app_registry.html:35 +#, fuzzy +#| msgid "Aktion" +msgid "Aktion EN" +msgstr "Action" + +#: workflows/templates/workflows/app_registry.html:78 +msgid "" +"Empfehlung: Produktweite Apps sparsam halten, kundenbezogene Prozesse unter " +"Apps oder Admin Apps einordnen." +msgstr "" + +#: workflows/templates/workflows/app_registry.html:79 +#, fuzzy +#| msgid "Regeln speichern" +msgid "App Registry speichern" +msgstr "Save rules" + #: workflows/templates/workflows/audit_log.html:16 msgid "Nachvollziehbarkeit aller wichtigen Admin-Aktionen im Portal." msgstr "" @@ -745,12 +1044,6 @@ msgstr "" msgid "Noch keine Audit-Einträge vorhanden." msgstr "No requests available yet." -#: workflows/templates/workflows/backup_recovery.html:4 -#: workflows/templates/workflows/backup_recovery.html:12 -#: workflows/templates/workflows/home.html:155 -msgid "Backup & Recovery" -msgstr "Backup & Recovery" - #: workflows/templates/workflows/backup_recovery.html:13 msgid "" "Datenbank- und Media-Backups erstellen und vorhandene Bundles sicher " @@ -803,15 +1096,6 @@ msgstr "Created" msgid "Verifiziert" msgstr "Verified" -#: workflows/templates/workflows/backup_recovery.html:40 -#: workflows/templates/workflows/home.html:99 -#: workflows/templates/workflows/onboarding_intro_session.html:37 -#: workflows/templates/workflows/request_timeline.html:70 -#: workflows/templates/workflows/requests_dashboard.html:136 -#: workflows/templates/workflows/welcome_emails.html:85 -msgid "Status" -msgstr "Status" - #: workflows/templates/workflows/backup_recovery.html:41 msgid "Inhalt" msgstr "Contents" @@ -914,39 +1198,39 @@ msgstr "Action in progress" msgid "Die Aktion wird im aktuellen Tab ausgeführt." msgstr "The action is running in the current tab." -#: workflows/templates/workflows/branding_settings.html:4 -#: workflows/templates/workflows/branding_settings.html:12 -#: workflows/templates/workflows/home.html:118 -msgid "Branding" -msgstr "Branding" - #: workflows/templates/workflows/branding_settings.html:13 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:48 +#: workflows/templates/workflows/branding_settings.html:32 +msgid "" +"Wird für E-Mail-Vorschläge und Domain-bezogene Standardtexte verwendet, z. " +"B. tub.co." +msgstr "" + +#: workflows/templates/workflows/branding_settings.html:53 msgid "Erlaubte Formate: SVG, PNG, JPG, JPEG, WEBP. Maximal 5 MB." msgstr "" -#: workflows/templates/workflows/branding_settings.html:51 +#: workflows/templates/workflows/branding_settings.html:56 msgid "Aktuelles Logo:" msgstr "Current logo:" -#: workflows/templates/workflows/branding_settings.html:51 -#: workflows/templates/workflows/branding_settings.html:60 +#: workflows/templates/workflows/branding_settings.html:56 +#: workflows/templates/workflows/branding_settings.html:65 msgid "öffnen" msgstr "open" -#: workflows/templates/workflows/branding_settings.html:57 +#: workflows/templates/workflows/branding_settings.html:62 msgid "Erlaubtes Format: PDF. Maximal 10 MB." msgstr "" -#: workflows/templates/workflows/branding_settings.html:60 +#: workflows/templates/workflows/branding_settings.html:65 msgid "Aktueller Briefkopf:" msgstr "Current letterhead:" -#: workflows/templates/workflows/branding_settings.html:65 +#: workflows/templates/workflows/branding_settings.html:70 msgid "" "TUBCO bleibt als Standard erhalten, bis hier Werte geändert oder Dateien " "hochgeladen werden." @@ -954,16 +1238,10 @@ msgstr "" "TUBCO remains the default until values are changed or files are uploaded " "here." -#: workflows/templates/workflows/branding_settings.html:66 +#: workflows/templates/workflows/branding_settings.html:71 msgid "Branding speichern" msgstr "Save branding" -#: workflows/templates/workflows/form_builder.html:4 -#: workflows/templates/workflows/form_builder.html:14 -#: workflows/templates/workflows/home.html:169 -msgid "Form Builder" -msgstr "Form Builder" - #: workflows/templates/workflows/form_builder.html:15 msgid "Felder per Drag-and-Drop sortieren und pro Schritt gruppieren." msgstr "Sort fields by drag and drop and group them by step." @@ -1020,13 +1298,6 @@ msgstr "Sort order" msgid "Label (EN)" msgstr "Label (EN)" -#: workflows/templates/workflows/form_builder.html:91 -#: workflows/templates/workflows/integrations_setup.html:263 -#: workflows/templates/workflows/intro_builder.html:65 -#: workflows/templates/workflows/user_management.html:75 -msgid "Aktiv" -msgstr "Active" - #: workflows/templates/workflows/form_builder.html:100 msgid "Ziehen zum Sortieren" msgstr "Drag to reorder" @@ -1083,12 +1354,6 @@ msgstr "No field configurations available." msgid "Feldtexte speichern" msgstr "Save field text" -#: workflows/templates/workflows/handbook.html:4 -#: workflows/templates/workflows/handbook.html:15 -#: workflows/templates/workflows/home.html:181 -msgid "Handbook" -msgstr "Handbook" - #: workflows/templates/workflows/handbook.html:17 msgid "" "Single documentation entry point for both operational knowledge and long-" @@ -1263,181 +1528,7 @@ msgstr "Production" msgid "PDF + E-Mail Workflow bereit" msgstr "PDF + Email Workflow Ready" -#: workflows/templates/workflows/home.html:55 -msgid "Apps" -msgstr "Apps" - -#: workflows/templates/workflows/home.html:56 -msgid "Wählen Sie den gewünschten Prozess." -msgstr "Choose the desired process." - -#: workflows/templates/workflows/home.html:63 -msgid "" -"Neue Mitarbeitende erfassen, PDF mit Briefkopf erstellen, Benachrichtigungen " -"senden und in Nextcloud ablegen." -msgstr "" -"Capture new employees, generate a PDF with letterhead, send notifications, " -"and store it in Nextcloud." - -#: workflows/templates/workflows/home.html:65 -msgid "Mehrschritt-Formular" -msgstr "Multi-step form" - -#: workflows/templates/workflows/home.html:67 -msgid "E-Mail Routing" -msgstr "Email routing" - -#: workflows/templates/workflows/home.html:71 -msgid "Onboarding starten" -msgstr "Start onboarding" - -#: workflows/templates/workflows/home.html:79 -msgid "" -"Mitarbeitende suchen, Daten vorbefüllen, Offboarding-Dokumente erzeugen und " -"Rückgabe-Prozess starten." -msgstr "" -"Search employees, prefill data, generate offboarding documents, and start " -"the return process." - -#: workflows/templates/workflows/home.html:81 -msgid "Profile-Suche" -msgstr "Profile search" - -#: workflows/templates/workflows/home.html:82 -msgid "Hardware-Liste" -msgstr "Hardware list" - -#: workflows/templates/workflows/home.html:83 -msgid "IT-Rückgabe" -msgstr "IT return" - -#: workflows/templates/workflows/home.html:87 -msgid "Offboarding starten" -msgstr "Start offboarding" - -#: workflows/templates/workflows/home.html:95 -#: workflows/templates/workflows/requests_dashboard.html:4 -#: workflows/templates/workflows/requests_dashboard.html:33 -msgid "Anfragen Dashboard" -msgstr "Requests Dashboard" - -#: workflows/templates/workflows/home.html:96 -msgid "" -"Status, Suchfunktion, PDF-Links und Verlauf aller Onboarding-/Offboarding-" -"Anfragen." -msgstr "" -"Status, search, PDF links, and history of all onboarding/offboarding " -"requests." - -#: workflows/templates/workflows/home.html:98 -msgid "Suche" -msgstr "Search" - -#: workflows/templates/workflows/home.html:100 -msgid "PDF Zugriff" -msgstr "PDF access" - -#: workflows/templates/workflows/home.html:104 -msgid "Dashboard öffnen" -msgstr "Open dashboard" - -#: workflows/templates/workflows/home.html:113 -msgid "Platform Apps" -msgstr "" - -#: workflows/templates/workflows/home.html:114 -#, fuzzy -#| msgid "Konfiguration, Tests und Steuerung." -msgid "Produktweite Konfiguration und Produktsteuerung." -msgstr "Configuration, tests, and controls." - -#: workflows/templates/workflows/home.html:119 -msgid "Logo, Portalname, Farben und PDF-Briefkopf verwalten." -msgstr "Manage logo, portal name, colors, and PDF letterhead." - -#: workflows/templates/workflows/home.html:120 -#: workflows/templates/workflows/home.html:136 -#: workflows/templates/workflows/home.html:143 -#: workflows/templates/workflows/home.html:150 -#: workflows/templates/workflows/home.html:157 -#: workflows/templates/workflows/home.html:164 -#: workflows/templates/workflows/home.html:171 -#: workflows/templates/workflows/home.html:176 -#: workflows/templates/workflows/home.html:183 -#: workflows/templates/workflows/home.html:190 -msgid "Öffnen" -msgstr "Open" - -#: workflows/templates/workflows/home.html:128 -msgid "Admin Apps" -msgstr "Admin Apps" - -#: workflows/templates/workflows/home.html:129 -msgid "Konfiguration, Tests und Steuerung." -msgstr "Configuration, tests, and controls." - -#: workflows/templates/workflows/home.html:134 -msgid "Integrationen" -msgstr "Integrations" - -#: workflows/templates/workflows/home.html:135 -msgid "Nextcloud- und E-Mail-Setup." -msgstr "Nextcloud and email setup." - -#: workflows/templates/workflows/home.html:141 -#: workflows/templates/workflows/user_management.html:4 -#: workflows/templates/workflows/user_management.html:14 -msgid "Benutzer & Rollen" -msgstr "Users & roles" - -#: workflows/templates/workflows/home.html:142 -msgid "Benutzer anlegen, Rollen zuweisen und Zugriffe steuern." -msgstr "Create users, assign roles, and control access." - -#: workflows/templates/workflows/home.html:149 -msgid "Wichtige Admin-Aktionen nachvollziehen und prüfen." -msgstr "" - -#: workflows/templates/workflows/home.html:156 -msgid "Backups erstellen und sicher verifizieren." -msgstr "" - -#: workflows/templates/workflows/home.html:162 -#: workflows/templates/workflows/welcome_emails.html:4 -msgid "Welcome E-Mails" -msgstr "Welcome Emails" - -#: workflows/templates/workflows/home.html:163 -msgid "Geplante Welcome Mails verwalten." -msgstr "Manage scheduled welcome emails." - -#: workflows/templates/workflows/home.html:170 -msgid "Felder, Schritte und Optionen verwalten." -msgstr "Manage fields, steps, and options." - -#: workflows/templates/workflows/home.html:174 -#: workflows/templates/workflows/intro_builder.html:4 -#: workflows/templates/workflows/intro_builder.html:17 -msgid "Einweisungs-Builder" -msgstr "Introduction Builder" - -#: workflows/templates/workflows/home.html:175 -msgid "Checklistenpunkte für das Einweisungsprotokoll konfigurieren." -msgstr "Configure checklist items for the introduction protocol." - -#: workflows/templates/workflows/home.html:182 -msgid "Project wiki and developer documentation in one place." -msgstr "Project wiki and developer documentation in one place." - -#: workflows/templates/workflows/home.html:188 -msgid "Django Admin" -msgstr "Django Admin" - -#: workflows/templates/workflows/home.html:189 -msgid "Vollständige Datenverwaltung." -msgstr "Full data management." - -#: workflows/templates/workflows/home.html:197 +#: workflows/templates/workflows/home.html:94 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." @@ -1842,7 +1933,9 @@ msgid "Mitarbeitende suchen (Name oder E-Mail)" msgstr "Search employees (name or email)" #: workflows/templates/workflows/offboarding_form.html:31 -msgid "z. B. max.mustermann@tub.co" +#, fuzzy, python-format +#| msgid "z. B. max.mustermann@tub.co" +msgid "z. B. max.mustermann@%(domain)s" msgstr "e.g. john.doe@tub.co" #: workflows/templates/workflows/offboarding_form.html:33 @@ -2002,7 +2095,7 @@ msgid "Dienstliche E-Mail" msgstr "Work email" #: workflows/templates/workflows/onboarding_intro_session.html:31 -#: workflows/views.py:820 +#: workflows/views.py:869 msgid "Vertragsbeginn" msgstr "Contract start" @@ -2651,244 +2744,256 @@ msgstr "Resume" msgid "Keine geplanten Welcome E-Mails vorhanden." msgstr "No scheduled welcome emails available." -#: workflows/views.py:94 +#: workflows/views.py:95 msgid "Person, Rolle, Abteilung" msgstr "Person, role, department" -#: workflows/views.py:95 +#: workflows/views.py:96 msgid "Beschäftigung und Termine" msgstr "Employment and dates" -#: workflows/views.py:96 +#: workflows/views.py:97 msgid "Geräte, Software und Zugänge" msgstr "Devices, software, and access" -#: workflows/views.py:97 +#: workflows/views.py:98 msgid "Notizen und Freigabe" msgstr "Notes and approval" -#: workflows/views.py:128 workflows/views.py:906 workflows/views.py:911 +#: workflows/views.py:129 workflows/views.py:955 workflows/views.py:960 msgid "Sie haben keine Berechtigung für diese Aktion." msgstr "You do not have permission for this action." -#: workflows/views.py:209 +#: workflows/views.py:210 #, fuzzy #| msgid "Vorgänge" msgid "Vorgänge gelöscht" msgstr "Requests" -#: workflows/views.py:210 +#: workflows/views.py:211 msgid "Vorgang gelöscht" msgstr "" -#: workflows/views.py:211 +#: workflows/views.py:212 msgid "Vorgang erneut angestoßen" msgstr "" -#: workflows/views.py:212 +#: workflows/views.py:213 #, fuzzy #| msgid "Einweisung" msgid "Einweisungs-PDF erzeugt" msgstr "Introduction" -#: workflows/views.py:213 +#: workflows/views.py:214 #, fuzzy #| msgid "Live-Protokoll erzeugen" msgid "Live-Protokoll erzeugt" msgstr "Generate live protocol" -#: workflows/views.py:214 +#: workflows/views.py:215 #, fuzzy #| msgid "Einweisung wurde zurückgesetzt." msgid "Einweisung zurückgesetzt" msgstr "Introduction was reset." -#: workflows/views.py:215 +#: workflows/views.py:216 #, fuzzy #| msgid "Einweisung wurde als Entwurf gespeichert." msgid "Einweisung als Entwurf gespeichert" msgstr "Introduction was saved as draft." -#: workflows/views.py:216 +#: workflows/views.py:217 #, fuzzy #| msgid "Einweisung wurde als abgeschlossen gespeichert." msgid "Einweisung abgeschlossen" msgstr "Introduction was saved as completed." -#: workflows/views.py:217 +#: workflows/views.py:218 msgid "Formularoption gelöscht" msgstr "" -#: workflows/views.py:218 +#: workflows/views.py:219 #, fuzzy #| msgid "Optionen speichern" msgid "Formularoptionen gespeichert" msgstr "Save options" -#: workflows/views.py:219 +#: workflows/views.py:220 #, fuzzy #| msgid "Feldtexte speichern" msgid "Feldtexte gespeichert" msgstr "Save field text" -#: workflows/views.py:220 +#: workflows/views.py:221 #, fuzzy #| msgid "Offboarding-Anfrage speichern" msgid "Formularlayout gespeichert" msgstr "Save offboarding request" -#: workflows/views.py:221 +#: workflows/views.py:222 msgid "Einweisungs-Checkpunkt gelöscht" msgstr "" -#: workflows/views.py:222 +#: workflows/views.py:223 msgid "Einweisungs-Checkpunkt hinzugefügt" msgstr "" -#: workflows/views.py:223 +#: workflows/views.py:224 #, fuzzy #| msgid "Checkliste speichern" msgid "Einweisungs-Checkliste gespeichert" msgstr "Save checklist" -#: workflows/views.py:224 +#: workflows/views.py:225 #, fuzzy #| msgid "Welcome E-Mails" msgid "Welcome E-Mail sofort ausgelöst" msgstr "Welcome Emails" -#: workflows/views.py:225 +#: workflows/views.py:226 #, fuzzy #| msgid "Welcome-Einstellungen speichern" msgid "Welcome E-Mail Einstellungen gespeichert" msgstr "Save welcome settings" -#: workflows/views.py:226 +#: workflows/views.py:227 msgid "Welcome E-Mail Sammelaktion ausgeführt" msgstr "" -#: workflows/views.py:227 +#: workflows/views.py:228 #, fuzzy #| msgid "Welcome E-Mails" msgid "Welcome E-Mail pausiert" msgstr "Welcome Emails" -#: workflows/views.py:228 +#: workflows/views.py:229 #, fuzzy #| msgid "Welcome E-Mails" msgid "Welcome E-Mail fortgesetzt" msgstr "Welcome Emails" -#: workflows/views.py:229 +#: workflows/views.py:230 #, fuzzy #| msgid "Welcome E-Mails" msgid "Welcome E-Mail abgebrochen" msgstr "Welcome Emails" -#: workflows/views.py:230 +#: workflows/views.py:231 #, fuzzy #| msgid "SMTP-Test" msgid "SMTP-Test gesendet" msgstr "SMTP test" -#: workflows/views.py:231 +#: workflows/views.py:232 #, fuzzy #| msgid "Nextcloud-Test" msgid "Nextcloud-Testupload ausgeführt" msgstr "Nextcloud test" -#: workflows/views.py:232 +#: workflows/views.py:233 #, fuzzy #| msgid "Nextcloud schalten" msgid "Nextcloud-Modus umgeschaltet" msgstr "Toggle Nextcloud" -#: workflows/views.py:233 +#: workflows/views.py:234 msgid "E-Mail-Modus umgeschaltet" msgstr "" -#: workflows/views.py:234 +#: workflows/views.py:235 #, fuzzy #| msgid "Integrationen Setup" msgid "Integrationen gespeichert" msgstr "Integrations Setup" -#: workflows/views.py:235 +#: workflows/views.py:236 #, fuzzy #| msgid "Welcome-Einstellungen speichern" msgid "Nextcloud-Einstellungen gespeichert" msgstr "Save welcome settings" -#: workflows/views.py:236 +#: workflows/views.py:237 #, fuzzy #| msgid "Welcome-Einstellungen speichern" msgid "Mail-Einstellungen gespeichert" msgstr "Save welcome settings" -#: workflows/views.py:237 +#: workflows/views.py:238 #, fuzzy #| msgid "E-Mail Routing & Vorlagen speichern" msgid "E-Mail-Routing gespeichert" msgstr "Save email routing & templates" -#: workflows/views.py:238 +#: workflows/views.py:239 #, fuzzy #| msgid "Offboarding-Anfrage speichern" msgid "Benachrichtigungsregeln gespeichert" msgstr "Save offboarding request" -#: workflows/views.py:239 +#: workflows/views.py:240 #, fuzzy #| msgid "Anfrage gespeichert" msgid "Benutzer erstellt" msgstr "Request saved" -#: workflows/views.py:240 +#: workflows/views.py:241 msgid "Benutzer aktualisiert" msgstr "" -#: workflows/views.py:241 +#: workflows/views.py:242 msgid "Passwort-Reset-Link versendet" msgstr "" -#: workflows/views.py:242 +#: workflows/views.py:243 #, fuzzy #| msgid "Benutzerübersicht" msgid "Benutzer gelöscht" msgstr "User overview" -#: workflows/views.py:243 +#: workflows/views.py:244 #, fuzzy #| msgid "Anfrage gespeichert" msgid "Backup erstellt" msgstr "Request saved" -#: workflows/views.py:244 +#: workflows/views.py:245 msgid "Backup verifiziert" msgstr "" -#: workflows/views.py:245 +#: workflows/views.py:246 #, fuzzy #| msgid "Anfrage gespeichert" msgid "Backup gelöscht" msgstr "Request saved" -#: workflows/views.py:246 +#: workflows/views.py:247 #, fuzzy #| msgid "Welcome-Einstellungen speichern" msgid "Backup-Einstellungen gespeichert" msgstr "Save welcome settings" -#: workflows/views.py:436 +#: workflows/views.py:248 +#, fuzzy +#| msgid "Anfrage gespeichert" +msgid "App-Registry gespeichert" +msgstr "Request saved" + +#: workflows/views.py:386 +#, fuzzy +#| msgid "Anfrage gespeichert" +msgid "App-Registry gespeichert." +msgstr "Request saved" + +#: workflows/views.py:485 msgid "Für diesen Benutzer ist keine E-Mail-Adresse hinterlegt." msgstr "" -#: workflows/views.py:445 +#: workflows/views.py:494 #, python-format msgid "Zugangseinladung für %(username)s" msgstr "" -#: workflows/views.py:447 +#: workflows/views.py:496 #, python-format msgid "" "Hallo %(name)s,\n" @@ -2901,12 +3006,12 @@ msgid "" "Ihrem Administrator." msgstr "" -#: workflows/views.py:458 +#: workflows/views.py:507 #, python-format msgid "Passwort zurücksetzen für %(username)s" msgstr "" -#: workflows/views.py:460 +#: workflows/views.py:509 #, python-format msgid "" "Hallo %(name)s,\n" @@ -2919,7 +3024,7 @@ msgid "" "ignorieren." msgstr "" -#: workflows/views.py:498 +#: workflows/views.py:547 #, fuzzy #| msgid "" #| "Benutzer konnte nicht erstellt werden. Bitte prüfen Sie die Eingaben." @@ -2927,23 +3032,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:524 +#: workflows/views.py:573 #, fuzzy #| msgid "Offboarding-Anfrage speichern" msgid "Portal-Branding wurde gespeichert." msgstr "Save offboarding request" -#: workflows/views.py:540 +#: workflows/views.py:589 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:553 +#: workflows/views.py:602 #, 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:575 +#: workflows/views.py:624 #, fuzzy #| msgid "" #| "Der aktuell angemeldete Super Admin kann sich hier nicht selbst sperren " @@ -2954,14 +3059,14 @@ msgid "" msgstr "" "The currently signed-in super admin cannot lock or downgrade themselves here." -#: workflows/views.py:578 +#: workflows/views.py:627 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:581 +#: workflows/views.py:630 #, fuzzy #| msgid "" #| "Der aktuell angemeldete Super Admin kann sich hier nicht selbst sperren " @@ -2972,7 +3077,7 @@ msgid "" msgstr "" "The currently signed-in super admin cannot lock or downgrade themselves here." -#: workflows/views.py:584 +#: workflows/views.py:633 #, fuzzy #| msgid "" #| "Der aktuell angemeldete Super Admin kann sich hier nicht selbst sperren " @@ -2983,18 +3088,18 @@ msgid "" msgstr "" "The currently signed-in super admin cannot lock or downgrade themselves here." -#: workflows/views.py:601 +#: workflows/views.py:650 #, python-format msgid "Benutzer wurde aktualisiert: %(username)s" msgstr "User updated: %(username)s" -#: workflows/views.py:623 +#: workflows/views.py:672 #, 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:635 +#: workflows/views.py:684 #, fuzzy #| msgid "" #| "Der aktuell angemeldete Super Admin kann sich hier nicht selbst sperren " @@ -3004,7 +3109,7 @@ msgid "" msgstr "" "The currently signed-in super admin cannot lock or downgrade themselves here." -#: workflows/views.py:638 +#: workflows/views.py:687 #, fuzzy #| msgid "" #| "Der aktuell angemeldete Super Admin kann sich hier nicht selbst sperren " @@ -3014,7 +3119,7 @@ msgid "" msgstr "" "The currently signed-in super admin cannot lock or downgrade themselves here." -#: workflows/views.py:641 +#: workflows/views.py:690 #, fuzzy #| msgid "" #| "Der aktuell angemeldete Super Admin kann sich hier nicht selbst sperren " @@ -3023,7 +3128,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:644 +#: workflows/views.py:693 #, fuzzy #| msgid "" #| "Der aktuell angemeldete Super Admin kann sich hier nicht selbst sperren " @@ -3032,121 +3137,121 @@ 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:657 +#: workflows/views.py:706 #, fuzzy, python-format #| msgid "Benutzer wurde erstellt: %(username)s" msgid "Benutzer wurde gelöscht: %(username)s" msgstr "User created: %(username)s" -#: workflows/views.py:744 +#: workflows/views.py:793 #, python-format msgid "Backup wurde erstellt: %(name)s" msgstr "" -#: workflows/views.py:746 +#: workflows/views.py:795 #, python-format msgid "Backup konnte nicht erstellt werden: %(error)s" msgstr "" -#: workflows/views.py:762 +#: workflows/views.py:811 #, python-format msgid "Backup wurde verifiziert: %(name)s" msgstr "" -#: workflows/views.py:764 +#: workflows/views.py:813 #, python-format msgid "Backup-Verifikation fehlgeschlagen: %(error)s" msgstr "" -#: workflows/views.py:780 +#: workflows/views.py:829 #, python-format msgid "Backup wurde gelöscht: %(name)s" msgstr "" -#: workflows/views.py:782 +#: workflows/views.py:831 #, python-format msgid "Backup konnte nicht gelöscht werden: %(error)s" msgstr "" -#: workflows/views.py:808 +#: workflows/views.py:857 #, fuzzy #| msgid "Anfrage gespeichert" msgid "Anfrage erstellt" msgstr "Request saved" -#: workflows/views.py:810 +#: workflows/views.py:859 #, fuzzy, python-format #| msgid "Sitzungsstatus" msgid "Status: %(status)s" msgstr "Session status" -#: workflows/views.py:822 +#: workflows/views.py:871 #, fuzzy #| msgid "Geplant für" msgid "Geplanter Start" msgstr "Scheduled for" -#: workflows/views.py:832 +#: workflows/views.py:881 msgid "Geräteübergabe / Hardware-Abholung" msgstr "" -#: workflows/views.py:834 +#: workflows/views.py:883 msgid "Geplanter Hardware-Termin" msgstr "" -#: workflows/views.py:843 +#: workflows/views.py:892 #, fuzzy #| msgid "Noch nicht verfügbar" msgid "PDF verfügbar" msgstr "Not available yet" -#: workflows/views.py:869 +#: workflows/views.py:918 #, fuzzy #| msgid "Einweisung" msgid "Einweisungssitzung" msgstr "Introduction" -#: workflows/views.py:881 +#: workflows/views.py:930 #, fuzzy #| msgid "Welcome E-Mails" msgid "Welcome E-Mail" msgstr "Welcome Emails" -#: workflows/views.py:920 +#: workflows/views.py:969 msgid "Keine Einträge ausgewählt." msgstr "No entries selected." -#: workflows/views.py:963 +#: workflows/views.py:1012 #, python-format msgid "%(count)s Eintrag/Einträge gelöscht." msgstr "%(count)s entry/entries deleted." -#: workflows/views.py:965 +#: workflows/views.py:1014 #, python-format msgid "%(count)s Auswahl(en) konnten nicht verarbeitet werden." msgstr "%(count)s selection(s) could not be processed." -#: workflows/views.py:967 +#: workflows/views.py:1016 msgid "Keine passenden Einträge gefunden." msgstr "No matching entries found." -#: workflows/views.py:1194 +#: workflows/views.py:1244 msgid "Einweisungs- und Übergabeprotokoll wurde erzeugt." msgstr "Introduction and handover protocol was generated." -#: workflows/views.py:1211 +#: workflows/views.py:1261 msgid "Einweisungsprotokoll aus Live-Status wurde erzeugt." msgstr "Introduction protocol from live status was generated." -#: workflows/views.py:1240 +#: workflows/views.py:1290 msgid "Einweisung wurde zurückgesetzt." msgstr "Introduction was reset." -#: workflows/views.py:1254 +#: workflows/views.py:1304 msgid "Einweisung wurde als abgeschlossen gespeichert." msgstr "Introduction was saved as completed." -#: workflows/views.py:1267 +#: workflows/views.py:1317 msgid "Einweisung wurde als Entwurf gespeichert." msgstr "Introduction was saved as draft." diff --git a/backend/workflows/admin.py b/backend/workflows/admin.py index d142cb2..31a851f 100644 --- a/backend/workflows/admin.py +++ b/backend/workflows/admin.py @@ -3,7 +3,7 @@ from django.conf import settings from django import forms from .emailing import send_system_email -from .models import AdminAuditLog, EmployeeProfile, FormFieldConfig, FormOption, IntroChecklistItem, NotificationRule, NotificationTemplate, OffboardingRequest, OnboardingIntroductionSession, OnboardingRequest, PortalBranding, ScheduledWelcomeEmail, SystemEmailConfig, WorkflowConfig +from .models import AdminAuditLog, EmployeeProfile, FormFieldConfig, FormOption, IntroChecklistItem, NotificationRule, NotificationTemplate, OffboardingRequest, OnboardingIntroductionSession, OnboardingRequest, PortalAppConfig, PortalBranding, ScheduledWelcomeEmail, SystemEmailConfig, WorkflowConfig @admin.register(EmployeeProfile) @@ -25,6 +25,15 @@ class PortalBrandingAdmin(admin.ModelAdmin): list_display = ('name', 'portal_title', 'company_name', 'support_email', 'default_language', 'updated_at') +@admin.register(PortalAppConfig) +class PortalAppConfigAdmin(admin.ModelAdmin): + list_display = ('key', 'section', 'sort_order', 'is_enabled', 'updated_at') + list_filter = ('section', 'is_enabled') + search_fields = ('key', 'title_override', 'title_override_en') + ordering = ('section', 'sort_order', 'key') + list_editable = ('section', 'sort_order', 'is_enabled') + + @admin.register(OnboardingRequest) class OnboardingRequestAdmin(admin.ModelAdmin): list_display = ('id', 'full_name', 'work_email', 'department', 'contract_start', 'created_at') diff --git a/backend/workflows/app_registry.py b/backend/workflows/app_registry.py new file mode 100644 index 0000000..73290e9 --- /dev/null +++ b/backend/workflows/app_registry.py @@ -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 diff --git a/backend/workflows/branding.py b/backend/workflows/branding.py index b25a984..46d4ae6 100644 --- a/backend/workflows/branding.py +++ b/backend/workflows/branding.py @@ -14,6 +14,7 @@ def get_portal_branding() -> PortalBranding: defaults={ 'portal_title': 'TUBCO Onboarding & Offboarding Portal', 'company_name': 'TUBCO', + 'company_domain': 'tub.co', 'support_email': 'info@tub.co', 'default_language': 'de', 'primary_color': '#000078', @@ -23,6 +24,12 @@ def get_portal_branding() -> PortalBranding: return branding +def get_company_email_domain() -> str: + branding = get_portal_branding() + domain = (branding.company_domain or '').strip().lower().lstrip('@') + return domain or 'tub.co' + + def get_portal_logo_url() -> str: branding = get_portal_branding() if branding.logo_image: @@ -51,6 +58,7 @@ def get_branding_context() -> dict[str, object]: 'portal_branding': branding, 'portal_title': branding.portal_title, 'portal_company_name': branding.company_name, + 'portal_email_domain': get_company_email_domain(), 'portal_support_email': branding.support_email, 'portal_default_language': branding.default_language, 'portal_primary_color': branding.primary_color, @@ -67,6 +75,7 @@ def get_branding_email_copy() -> dict[str, str]: portal_title = (branding.portal_title or f'{company_name} Portal').strip() return { 'company_name': company_name, + 'company_domain': get_company_email_domain(), 'portal_title': portal_title, 'support_email': (branding.support_email or '').strip(), } @@ -78,7 +87,9 @@ def get_default_notification_templates() -> dict[str, dict[str, str]]: from .tasks import DEFAULT_NOTIFICATION_TEMPLATES templates = deepcopy(DEFAULT_NOTIFICATION_TEMPLATES) - company_name = get_branding_email_copy()['company_name'] + branding_copy = get_branding_email_copy() + company_name = branding_copy['company_name'] + support_email = branding_copy['support_email'] or f"it@{branding_copy['company_domain']}" welcome = templates.get('onboarding_welcome') if welcome: welcome['subject'] = f'Willkommen bei {company_name}, {{ VORNAME }}' @@ -89,7 +100,7 @@ def get_default_notification_templates() -> dict[str, dict[str, str]]: 'Wir freuen uns sehr, dass du ab dem {{ CONTRACT_START }} unser Team in der Abteilung {{ DEPARTMENT }} verstärkst.\n\n' 'Deine dienstliche E-Mail-Adresse lautet: {{ EMAIL }}.\n' 'Im Anhang findest du deine Onboarding-Unterlagen als PDF.\n\n' - 'Wenn du Fragen hast, melde dich gerne jederzeit.\n\n' + f'Wenn du Fragen hast, melde dich gerne jederzeit unter {support_email}.\n\n' 'Viele Grüße\n' f'{company_name} IT' ) @@ -99,7 +110,7 @@ def get_default_notification_templates() -> dict[str, dict[str, str]]: 'We are very happy that you will join our {{ DEPARTMENT }} team starting on {{ CONTRACT_START }}.\n\n' 'Your work email address is: {{ EMAIL }}.\n' 'You will find your onboarding documents attached as a PDF.\n\n' - 'If you have any questions, feel free to contact us anytime.\n\n' + f'If you have any questions, feel free to contact {support_email}.\n\n' 'Best regards,\n' f'{company_name} IT' ) diff --git a/backend/workflows/forms.py b/backend/workflows/forms.py index d5ca11b..acd6060 100644 --- a/backend/workflows/forms.py +++ b/backend/workflows/forms.py @@ -6,6 +6,7 @@ from django.contrib.auth.forms import AuthenticationForm, PasswordResetForm, Set from django.utils import timezone from django.utils.translation import get_language, gettext as _, gettext_lazy +from .branding import get_company_email_domain from .form_builder import apply_form_field_config from .models import EmployeeProfile, FormOption, OffboardingRequest, OnboardingRequest, PortalBranding, WorkflowConfig from .roles import ROLE_ADMIN, ROLE_GROUP_NAMES, ROLE_IT_STAFF, ROLE_LABELS, ROLE_PLATFORM_OWNER, ROLE_STAFF, ROLE_SUPER_ADMIN, assign_user_role @@ -175,6 +176,7 @@ class PortalBrandingForm(forms.ModelForm): fields = [ 'portal_title', 'company_name', + 'company_domain', 'support_email', 'default_language', 'logo_image', @@ -185,6 +187,7 @@ class PortalBrandingForm(forms.ModelForm): labels = { 'portal_title': gettext_lazy('Portal-Titel'), 'company_name': gettext_lazy('Firmenname'), + 'company_domain': gettext_lazy('Firmen-Domain'), 'support_email': gettext_lazy('Support-E-Mail'), 'default_language': gettext_lazy('Standardsprache'), 'logo_image': gettext_lazy('Logo'), @@ -343,6 +346,7 @@ class OnboardingRequestForm(forms.ModelForm): def __init__(self, *args, **kwargs): self.requester_email = (kwargs.pop('requester_email', '') or '').strip().lower() super().__init__(*args, **kwargs) + self.email_domain = get_company_email_domain() config = WorkflowConfig.objects.order_by('id').first() self.handover_lead_days = max(0, int(getattr(config, 'device_handover_lead_days', 5) or 5)) @@ -350,6 +354,7 @@ class OnboardingRequestForm(forms.ModelForm): self.fields['handover_date'].widget.attrs['min'] = minimum_handover_date.isoformat() self.fields['full_name'].label = 'Name' + self.fields['work_email'].help_text = _('Bitte nutzen Sie das Format name@%(domain)s.') % {'domain': self.email_domain} full_name_initial = (self.initial.get('full_name') or '').strip() if full_name_initial and not self.initial.get('first_name') and not self.initial.get('last_name'): name_parts = full_name_initial.split() @@ -369,8 +374,9 @@ class OnboardingRequestForm(forms.ModelForm): value = (self.cleaned_data.get('work_email') or '').strip().lower() if not value: return value - if not value.endswith('@tub.co'): - raise forms.ValidationError('Bitte verwenden Sie eine @tub.co E-Mail-Adresse.') + expected_suffix = f'@{self.email_domain}' + if self.email_domain and not value.endswith(expected_suffix): + raise forms.ValidationError(_('Bitte verwenden Sie eine @%(domain)s E-Mail-Adresse.') % {'domain': self.email_domain}) return value def clean_signature_image(self): @@ -531,11 +537,21 @@ class OffboardingRequestForm(forms.ModelForm): def __init__(self, *args, **kwargs): prefill_profile = kwargs.pop('prefill_profile', None) super().__init__(*args, **kwargs) + self.email_domain = get_company_email_domain() self.fields['full_name'].label = 'Vorname und Nachname' - self.fields['work_email'].help_text = '' + self.fields['work_email'].help_text = _('Bitte nutzen Sie das Format name@%(domain)s.') % {'domain': self.email_domain} if prefill_profile: self.fields['full_name'].initial = prefill_profile.full_name self.fields['work_email'].initial = prefill_profile.work_email self.fields['department'].initial = prefill_profile.department self.fields['job_title'].initial = prefill_profile.job_title apply_form_field_config('offboarding', self) + + def clean_work_email(self): + value = (self.cleaned_data.get('work_email') or '').strip().lower() + if not value: + return value + expected_suffix = f'@{self.email_domain}' + if self.email_domain and not value.endswith(expected_suffix): + raise forms.ValidationError(_('Bitte verwenden Sie eine @%(domain)s E-Mail-Adresse.') % {'domain': self.email_domain}) + return value diff --git a/backend/workflows/migrations/0038_portalappconfig.py b/backend/workflows/migrations/0038_portalappconfig.py new file mode 100644 index 0000000..a2e0875 --- /dev/null +++ b/backend/workflows/migrations/0038_portalappconfig.py @@ -0,0 +1,35 @@ +# Generated by Django 5.1.5 on 2026-03-26 10:48 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('workflows', '0037_alter_portalbranding_logo_image_and_more'), + ] + + operations = [ + migrations.CreateModel( + name='PortalAppConfig', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('key', models.CharField(max_length=80, unique=True)), + ('section', models.CharField(choices=[('app', 'Apps'), ('platform', 'Platform Apps'), ('admin', 'Admin Apps')], default='app', max_length=20)), + ('sort_order', models.PositiveIntegerField(default=0)), + ('is_enabled', models.BooleanField(default=True)), + ('title_override', models.CharField(blank=True, max_length=255)), + ('title_override_en', models.CharField(blank=True, max_length=255)), + ('description_override', models.TextField(blank=True)), + ('description_override_en', models.TextField(blank=True)), + ('action_label_override', models.CharField(blank=True, max_length=255)), + ('action_label_override_en', models.CharField(blank=True, max_length=255)), + ('updated_at', models.DateTimeField(auto_now=True)), + ], + options={ + 'verbose_name': 'Portal App', + 'verbose_name_plural': 'Portal Apps', + 'ordering': ['section', 'sort_order', 'key'], + }, + ), + ] diff --git a/backend/workflows/migrations/0039_portalbranding_company_domain.py b/backend/workflows/migrations/0039_portalbranding_company_domain.py new file mode 100644 index 0000000..de83381 --- /dev/null +++ b/backend/workflows/migrations/0039_portalbranding_company_domain.py @@ -0,0 +1,18 @@ +# Generated by Django 5.1.5 on 2026-03-26 10:55 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('workflows', '0038_portalappconfig'), + ] + + operations = [ + migrations.AddField( + model_name='portalbranding', + name='company_domain', + field=models.CharField(blank=True, default='tub.co', max_length=120), + ), + ] diff --git a/backend/workflows/models.py b/backend/workflows/models.py index f986b51..7108549 100644 --- a/backend/workflows/models.py +++ b/backend/workflows/models.py @@ -29,6 +29,7 @@ class PortalBranding(models.Model): name = models.CharField(max_length=80, default='Default', unique=True) portal_title = models.CharField(max_length=255, default='TUBCO Onboarding & Offboarding Portal') company_name = models.CharField(max_length=255, default='TUBCO') + company_domain = models.CharField(max_length=120, blank=True, default='tub.co') support_email = models.EmailField(blank=True, default='info@tub.co') default_language = models.CharField( max_length=10, @@ -59,6 +60,54 @@ class PortalBranding(models.Model): return self.portal_title or self.company_name or self.name +class PortalAppConfig(models.Model): + SECTION_APP = 'app' + SECTION_PLATFORM = 'platform' + SECTION_ADMIN = 'admin' + SECTION_CHOICES = [ + (SECTION_APP, _('Apps')), + (SECTION_PLATFORM, _('Platform Apps')), + (SECTION_ADMIN, _('Admin Apps')), + ] + + key = models.CharField(max_length=80, unique=True) + section = models.CharField(max_length=20, choices=SECTION_CHOICES, default=SECTION_APP) + sort_order = models.PositiveIntegerField(default=0) + is_enabled = models.BooleanField(default=True) + 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) + description_override_en = models.TextField(blank=True) + action_label_override = models.CharField(max_length=255, blank=True) + action_label_override_en = models.CharField(max_length=255, blank=True) + updated_at = models.DateTimeField(auto_now=True) + + class Meta: + ordering = ['section', 'sort_order', 'key'] + verbose_name = 'Portal App' + verbose_name_plural = 'Portal Apps' + + def __str__(self) -> str: + return self.key + + def _translated_value(self, field_name: str, language_code: str | None = None) -> str: + lang = (language_code or get_language() or 'de').split('-')[0] + if lang == 'en': + english_value = (getattr(self, f'{field_name}_en', '') or '').strip() + if english_value: + return english_value + return (getattr(self, field_name, '') or '').strip() + + def translated_title_override(self, language_code: str | None = None) -> str: + return self._translated_value('title_override', language_code) + + def translated_description_override(self, language_code: str | None = None) -> str: + return self._translated_value('description_override', language_code) + + def translated_action_label_override(self, language_code: str | None = None) -> str: + return self._translated_value('action_label_override', language_code) + + class AdminAuditLog(models.Model): actor = models.ForeignKey( settings.AUTH_USER_MODEL, diff --git a/backend/workflows/roles.py b/backend/workflows/roles.py index 890d2d0..e8841e5 100644 --- a/backend/workflows/roles.py +++ b/backend/workflows/roles.py @@ -29,6 +29,7 @@ ROLE_LABELS = { CAPABILITIES = { 'manage_users': {ROLE_PLATFORM_OWNER, ROLE_SUPER_ADMIN}, 'manage_product_branding': {ROLE_PLATFORM_OWNER}, + 'manage_app_registry': {ROLE_PLATFORM_OWNER}, 'access_requests_dashboard': {ROLE_PLATFORM_OWNER, ROLE_SUPER_ADMIN, ROLE_ADMIN, ROLE_IT_STAFF}, 'run_intro_session': {ROLE_PLATFORM_OWNER, ROLE_SUPER_ADMIN, ROLE_ADMIN, ROLE_IT_STAFF}, 'generate_intro_pdfs': {ROLE_PLATFORM_OWNER, ROLE_SUPER_ADMIN, ROLE_ADMIN, ROLE_IT_STAFF}, @@ -123,6 +124,7 @@ def template_role_context(user) -> dict[str, object]: 'role_key': role_key, 'role_label': str(ROLE_LABELS[role_key]), 'can_manage_product_branding': user_has_capability(user, 'manage_product_branding'), + 'can_manage_app_registry': user_has_capability(user, 'manage_app_registry'), 'can_manage_users': user_has_capability(user, 'manage_users'), 'can_access_requests_dashboard': user_has_capability(user, 'access_requests_dashboard'), 'can_run_intro_session': user_has_capability(user, 'run_intro_session'), diff --git a/backend/workflows/static/workflows/css/admin_tools.css b/backend/workflows/static/workflows/css/admin_tools.css index 014198d..ada691d 100644 --- a/backend/workflows/static/workflows/css/admin_tools.css +++ b/backend/workflows/static/workflows/css/admin_tools.css @@ -35,6 +35,12 @@ textarea { min-height: 120px; font-family: ui-monospace, SFMono-Regular, Menlo, table { width: 100%; border-collapse: collapse; font-size: 14px; } th, td { border: 1px solid #dce5f1; padding: 8px; text-align: left; vertical-align: top; } th { background: #f6f9ff; color: #334155; } +.app-registry-wrap textarea { min-height: 86px; } +.app-registry-table input[type="text"], +.app-registry-table input[type="number"], +.app-registry-table select, +.app-registry-table textarea { min-width: 160px; } +.app-registry-table td { background: rgba(255,255,255,0.9); } .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; } diff --git a/backend/workflows/static/workflows/js/offboarding_form.js b/backend/workflows/static/workflows/js/offboarding_form.js index c7dec47..bccbae1 100644 --- a/backend/workflows/static/workflows/js/offboarding_form.js +++ b/backend/workflows/static/workflows/js/offboarding_form.js @@ -22,6 +22,8 @@ const fullName = byName('full_name'); const workEmail = byName('work_email'); + const form = fullName ? fullName.closest('form') : null; + const emailDomain = (((form && form.dataset.emailDomain) || 'tub.co') + '').replace(/^@+/, '').trim(); if (!fullName || !workEmail) return; let lastSuggested = ''; @@ -31,7 +33,7 @@ const lastName = extractLastName(fullName.value); const slug = slugifyForEmail(lastName); if (!slug) return; - const suggestion = slug + '@tub.co'; + const suggestion = slug + '@' + emailDomain; const current = (workEmail.value || '').trim(); if (!userEditedEmail || current === '' || current === lastSuggested) { workEmail.value = suggestion; diff --git a/backend/workflows/static/workflows/js/onboarding_form.js b/backend/workflows/static/workflows/js/onboarding_form.js index 8ae6960..8a0ce0b 100644 --- a/backend/workflows/static/workflows/js/onboarding_form.js +++ b/backend/workflows/static/workflows/js/onboarding_form.js @@ -5,6 +5,7 @@ const btnNext = document.getElementById('btn-next'); const btnSubmit = document.getElementById('btn-submit'); const form = document.getElementById('onboarding-form'); + const emailDomain = ((form && form.dataset.emailDomain) || 'tub.co').replace(/^@+/, '').trim(); let current = 0; form.setAttribute('novalidate', 'novalidate'); @@ -82,7 +83,7 @@ function suggestEmail() { const slug = slugifyForEmail(lastName.value); if (!slug) return; - const suggestion = slug + '@tub.co'; + const suggestion = slug + '@' + emailDomain; if (!userEditedEmail || workEmail.value === '' || workEmail.value === lastSuggested) { workEmail.value = suggestion; lastSuggested = suggestion; diff --git a/backend/workflows/tasks.py b/backend/workflows/tasks.py index 9e804b8..9adfd4b 100644 --- a/backend/workflows/tasks.py +++ b/backend/workflows/tasks.py @@ -101,7 +101,7 @@ DEFAULT_NOTIFICATION_TEMPLATES = { 'Vertragsbeginn: {{ CONTRACT_START }}\n' 'E-Mail-Adresse: {{ EMAIL }}\n\n' '{% if PDF_LINK %}In 2 Minuten findest du alle Infos über den Mitarbeiter als PDF unter diesem Link: {{ PDF_LINK }}\n\n{% endif %}' - 'Falls du noch irgendwelche anderen Informationen benötigen solltest, kannst du dich bei der it@tub.co melden!\n\n' + 'Falls du noch irgendwelche anderen Informationen benötigen solltest, kannst du dich bei {{ SUPPORT_EMAIL }} melden!\n\n' 'Vielen Dank und schöne Grüße,\n' 'Die IT.' ), @@ -114,7 +114,7 @@ DEFAULT_NOTIFICATION_TEMPLATES = { 'Contract start: {{ CONTRACT_START }}\n' 'Email address: {{ EMAIL }}\n\n' '{% if PDF_LINK %}You will find the employee PDF here in about 2 minutes: {{ PDF_LINK }}\n\n{% endif %}' - 'If you need any other information, please contact it@tub.co.\n\n' + 'If you need any other information, please contact {{ SUPPORT_EMAIL }}.\n\n' 'Thank you and best regards,\n' 'IT' ), @@ -1176,6 +1176,7 @@ def process_onboarding_request(onboarding_request_id: int) -> None: request_obj.last_error = '' request_obj.save(update_fields=['processing_status', 'last_error']) try: + branding_copy = get_branding_email_copy() it_email, general_info_email, business_card_email, hr_works_email, key_email = _resolve_workflow_emails() salutation = (request_obj.get_gender_display() or '').strip() display_name = f"{salutation} {request_obj.full_name}".strip() @@ -1204,6 +1205,7 @@ def process_onboarding_request(onboarding_request_id: int) -> None: 'CONTRACT_START': request_obj.contract_start, 'EMAIL': request_obj.work_email, 'REQUESTED_BY': request_obj.onboarded_by_email or '-', + 'SUPPORT_EMAIL': branding_copy['support_email'] or f"it@{branding_copy['company_domain']}", 'BUSINESS_CARD_NAME': request_obj.business_card_name or display_name, 'BUSINESS_CARD_TITLE': request_obj.business_card_title or '-', 'BUSINESS_CARD_EMAIL': request_obj.business_card_email or request_obj.work_email, @@ -1285,6 +1287,7 @@ def process_offboarding_request(offboarding_request_id: int) -> None: request_obj.last_error = '' request_obj.save(update_fields=['processing_status', 'last_error']) try: + branding_copy = get_branding_email_copy() it_email, general_info_email, _, hr_works_email, _ = _resolve_workflow_emails() pdf_path = _generate_offboarding_pdf(request_obj) @@ -1297,6 +1300,7 @@ def process_offboarding_request(offboarding_request_id: int) -> None: 'LAST_WORKING_DAY': request_obj.last_working_day, 'REQUESTED_BY': request_obj.requested_by_email, 'EMAIL': request_obj.work_email, + 'SUPPORT_EMAIL': branding_copy['support_email'] or f"it@{branding_copy['company_domain']}", } _send_templated_email( diff --git a/backend/workflows/templates/workflows/app_registry.html b/backend/workflows/templates/workflows/app_registry.html new file mode 100644 index 0000000..dea7d93 --- /dev/null +++ b/backend/workflows/templates/workflows/app_registry.html @@ -0,0 +1,83 @@ +{% extends 'workflows/base_shell.html' %} +{% load static i18n %} + +{% block title %}{% trans "App Registry" %}{% endblock %} + +{% block extra_css %} + +{% endblock %} + +{% block shell_body %} +{% include 'workflows/includes/app_header.html' with header_show_home=1 header_show_lang=1 header_inside_shell=1 %} +

{% trans "App Registry" %}

+

{% trans "Apps zentral steuern, für Kunden vorbereiten und ohne Template-Eingriffe auf der Landing Page ausspielen." %}

+ +{% include 'workflows/includes/messages.html' %} + +
+
+
{% trans "Sicherheit bleibt codebasiert: Sichtbarkeit und Reihenfolge sind hier steuerbar, Berechtigungen weiterhin über Rollen und Capabilities." %}
+ {% trans "Produktkern" %} +
+
+ {% 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 }}
+
+ + + + + + + + + + + + + + + +
+
+
+
{% trans "Empfehlung: Produktweite Apps sparsam halten, kundenbezogene Prozesse unter Apps oder Admin Apps einordnen." %}
+ +
+
+
+{% endblock %} diff --git a/backend/workflows/templates/workflows/branding_settings.html b/backend/workflows/templates/workflows/branding_settings.html index 4f9138e..c7380d4 100644 --- a/backend/workflows/templates/workflows/branding_settings.html +++ b/backend/workflows/templates/workflows/branding_settings.html @@ -26,6 +26,11 @@ {{ form.company_name }} +
+ + {{ form.company_domain }} +
{% trans "Wird für E-Mail-Vorschläge und Domain-bezogene Standardtexte verwendet, z. B. tub.co." %}
+
{{ form.support_email }} diff --git a/backend/workflows/templates/workflows/developer_handbook.html b/backend/workflows/templates/workflows/developer_handbook.html index 76e4552..5066ee8 100644 --- a/backend/workflows/templates/workflows/developer_handbook.html +++ b/backend/workflows/templates/workflows/developer_handbook.html @@ -102,12 +102,13 @@ docker compose exec -T web python manage.py check

Role and Permission Model

+

10b) App Registry

+ +

11) Builder Architecture

Form Builder