From 4549a867f91b4f7d5c67a6f7b59976a1e97976d1 Mon Sep 17 00:00:00 2001 From: Md Bayazid Bostame Date: Thu, 26 Mar 2026 00:33:02 +0100 Subject: [PATCH] snapshot: preserve shared confirmation modal standardization --- backend/locale/en/LC_MESSAGES/django.mo | Bin 23156 -> 23041 bytes backend/locale/en/LC_MESSAGES/django.po | 53 ++++++-- .../static/workflows/css/admin_tools.css | 7 +- .../static/workflows/css/app_chrome.css | 114 ++++++++++++++++++ .../static/workflows/css/buttons.css | 16 ++- .../workflows/static/workflows/css/home.css | 45 +++++++ .../workflows/static/workflows/css/login.css | 45 +++++-- .../workflows/css/requests_dashboard.css | 42 +++++++ .../static/workflows/js/confirm_dialog.js | 92 ++++++++++++++ .../static/workflows/js/welcome_emails.js | 33 +++-- .../templates/registration/login.html | 14 ++- .../templates/workflows/base_shell.html | 14 +++ .../templates/workflows/form_builder.html | 3 +- .../templates/workflows/intro_builder.html | 2 +- .../workflows/onboarding_intro_session.html | 3 +- .../workflows/requests_dashboard.html | 6 +- .../templates/workflows/welcome_emails.html | 3 +- 17 files changed, 445 insertions(+), 47 deletions(-) create mode 100644 backend/workflows/static/workflows/js/confirm_dialog.js diff --git a/backend/locale/en/LC_MESSAGES/django.mo b/backend/locale/en/LC_MESSAGES/django.mo index 5f4707a7ed088dcdf2be0b0227ae65c01d420818..46ff1edf790158f5b8babc0ed0162e4bcb225a0e 100644 GIT binary patch delta 6530 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 delta 6644 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 diff --git a/backend/locale/en/LC_MESSAGES/django.po b/backend/locale/en/LC_MESSAGES/django.po index 46fae62..705a0dc 100644 --- a/backend/locale/en/LC_MESSAGES/django.po +++ b/backend/locale/en/LC_MESSAGES/django.po @@ -2,7 +2,7 @@ msgid "" msgstr "" "Project-Id-Version: tubco-portal\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-03-25 23:12+0000\n" +"POT-Creation-Date: 2026-03-25 23:29+0000\n" "PO-Revision-Date: 2026-03-24 00:00+0000\n" "Language: en\n" "MIME-Version: 1.0\n" @@ -350,11 +350,18 @@ msgstr "Sign in" msgid "Bitte melden Sie sich mit Ihrem Benutzerkonto an." msgstr "Please sign in with your user account." -#: workflows/templates/registration/login.html:25 -msgid "Anmeldung fehlgeschlagen. Bitte Zugangsdaten prüfen." -msgstr "Login failed. Please check your credentials." - #: workflows/templates/registration/login.html:29 +#, fuzzy +#| msgid "Fehlgeschlagen" +msgid "Anmeldung fehlgeschlagen" +msgstr "Failed" + +#: workflows/templates/registration/login.html:30 +msgid "" +"Benutzername oder Passwort sind nicht korrekt. Bitte versuchen Sie es erneut." +msgstr "" + +#: workflows/templates/registration/login.html:35 msgid "Anmelden" msgstr "Sign in" @@ -453,6 +460,19 @@ msgstr "" msgid "Noch keine Audit-Einträge vorhanden." msgstr "No requests available yet." +#: workflows/templates/workflows/base_shell.html:24 +msgid "Bitte bestätigen" +msgstr "" + +#: workflows/templates/workflows/base_shell.html:28 +#: workflows/templates/workflows/welcome_emails.html:138 +msgid "Abbrechen" +msgstr "Cancel" + +#: workflows/templates/workflows/base_shell.html:29 +msgid "Bestätigen" +msgstr "" + #: workflows/templates/workflows/form_builder.html:4 #: workflows/templates/workflows/form_builder.html:14 #: workflows/templates/workflows/home.html:134 @@ -1795,6 +1815,12 @@ msgstr "" msgid "Bis" msgstr "" +#: workflows/templates/workflows/requests_dashboard.html:176 +#, fuzzy +#| msgid "Ausgewählte Welcome-Einträge wirklich löschen?" +msgid "Ausgewählte Einträge wirklich löschen?" +msgstr "Delete the selected welcome entries?" + #: workflows/templates/workflows/requests_dashboard.html:179 #: workflows/templates/workflows/welcome_emails.html:78 msgid "ausgewählt" @@ -1849,10 +1875,20 @@ msgstr "Not relevant" msgid "Timeline" msgstr "" +#: workflows/templates/workflows/requests_dashboard.html:276 +msgid "Eintrag erneut verarbeiten?" +msgstr "" + #: workflows/templates/workflows/requests_dashboard.html:278 msgid "Erneut versuchen" msgstr "" +#: workflows/templates/workflows/requests_dashboard.html:281 +#, fuzzy +#| msgid "Option wirklich löschen?" +msgid "Eintrag wirklich löschen?" +msgstr "Delete this option?" + #: workflows/templates/workflows/requests_dashboard.html:290 msgid "Noch keine Vorgänge vorhanden." msgstr "No requests available yet." @@ -1939,10 +1975,6 @@ msgstr "Sent at" msgid "Fortsetzen" msgstr "Resume" -#: workflows/templates/workflows/welcome_emails.html:138 -msgid "Abbrechen" -msgstr "Cancel" - #: workflows/templates/workflows/welcome_emails.html:145 msgid "Keine geplanten Welcome E-Mails vorhanden." msgstr "No scheduled welcome emails available." @@ -2215,6 +2247,9 @@ msgstr "Introduction was saved as completed." msgid "Einweisung wurde als Entwurf gespeichert." msgstr "Introduction was saved as draft." +#~ msgid "Anmeldung fehlgeschlagen. Bitte Zugangsdaten prüfen." +#~ msgstr "Login failed. Please check your credentials." + #, fuzzy #~| msgid "Nextcloud speichern" #~ msgid "Nextcloud deaktivieren" diff --git a/backend/workflows/static/workflows/css/admin_tools.css b/backend/workflows/static/workflows/css/admin_tools.css index 8d6bf8c..715150c 100644 --- a/backend/workflows/static/workflows/css/admin_tools.css +++ b/backend/workflows/static/workflows/css/admin_tools.css @@ -5,10 +5,10 @@ h1 { margin: 12px 0 6px; color: #000078; } .flash, .msg { border-radius: 10px; padding: 10px 12px; margin: 0 0 12px; border: 1px solid #d6e1ef; background: #f8fbff; color: #1f3a5f; } .flash.error, .msg.error { border-color: #fecaca; background: #fff1f2; color: #991b1b; } .flash.success { border-color: #bfe6c9; background: #edf9f1; color: #116634; } -.card { border: 1px solid #d8e3f0; border-radius: 12px; background: #fbfdff; padding: 12px; margin-bottom: 14px; } +.card { border: 1px solid #d8e3f0; border-radius: 12px; background: #fbfdff; padding: 12px; margin-bottom: 14px; 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); } .grid { display: grid; grid-template-columns: repeat(2, minmax(240px, 1fr)); gap: 10px; } label { display: block; margin-bottom: 4px; font-size: 12px; color: #334155; font-weight: 700; } -input, select, textarea { width: 100%; box-sizing: border-box; border: 1px solid #cbd5e1; border-radius: 8px; padding: 8px 9px; background: #fff; } +input, select, textarea { width: 100%; box-sizing: border-box; border: 1px solid #cbd5e1; border-radius: 8px; padding: 8px 9px; background: #fff; transition: border-color 180ms cubic-bezier(0.2, 0.8, 0.2, 1), box-shadow 180ms cubic-bezier(0.2, 0.8, 0.2, 1), background-color 180ms cubic-bezier(0.2, 0.8, 0.2, 1); } textarea { min-height: 120px; font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 12px; } .actions { margin-top: 10px; display: flex; gap: 8px; flex-wrap: wrap; } .toggle-row { margin-top: 10px; display: inline-flex; align-items: center; gap: 10px; padding: 8px 10px 8px 12px; border: 1px solid #d8e3f0; border-radius: 999px; background: linear-gradient(180deg, rgba(255,255,255,0.98), rgba(246,249,255,0.94)); box-shadow: inset 0 1px 0 rgba(255,255,255,0.9), 0 6px 14px rgba(16, 32, 57, 0.05); } @@ -24,8 +24,9 @@ textarea { min-height: 120px; font-family: ui-monospace, SFMono-Regular, Menlo, .hint { margin-top: 6px; color: #64748b; font-size: 12px; } .toolbar { display: flex; justify-content: space-between; align-items: center; gap: 10px; margin-bottom: 10px; flex-wrap: wrap; } .switch { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 12px; } -.switch .tab { border: 1px solid #c9d6e7; border-radius: 999px; padding: 8px 14px; text-decoration: none; color: #1f2f49; font-weight: 700; background: #f6f9ff; } +.switch .tab { border: 1px solid #c9d6e7; border-radius: 999px; padding: 8px 14px; text-decoration: none; color: #1f2f49; font-weight: 700; background: #f6f9ff; transition: background-color 180ms cubic-bezier(0.2, 0.8, 0.2, 1), border-color 180ms cubic-bezier(0.2, 0.8, 0.2, 1), color 180ms cubic-bezier(0.2, 0.8, 0.2, 1), transform 180ms cubic-bezier(0.2, 0.8, 0.2, 1), box-shadow 180ms cubic-bezier(0.2, 0.8, 0.2, 1); } .switch .tab.active { background: #000078; color: #fff; border-color: #000078; } +.switch .tab:hover { transform: translateY(-1px); box-shadow: 0 8px 16px rgba(16, 32, 57, 0.06); } .check-row { margin-top: 8px; display: flex; gap: 12px; flex-wrap: wrap; } .check-row label { display: inline-flex; align-items: center; gap: 6px; margin: 0; font-size: 13px; } .check-row input[type="checkbox"] { width: auto; } diff --git a/backend/workflows/static/workflows/css/app_chrome.css b/backend/workflows/static/workflows/css/app_chrome.css index fe96482..2ccb206 100644 --- a/backend/workflows/static/workflows/css/app_chrome.css +++ b/backend/workflows/static/workflows/css/app_chrome.css @@ -4,6 +4,10 @@ --app-brand-blue: #000078; --app-panel: rgba(255, 255, 255, 0.9); --app-shadow: 0 22px 48px rgba(18, 34, 56, 0.14); + --motion-fast: 160ms; + --motion-base: 200ms; + --motion-slow: 260ms; + --motion-ease: cubic-bezier(0.2, 0.8, 0.2, 1); } .app-header { @@ -19,6 +23,10 @@ border-radius: 28px; background: linear-gradient(180deg, rgba(255,255,255,0.95), rgba(248,251,255,0.84)); box-shadow: var(--app-shadow); + transition: + box-shadow var(--motion-base) var(--motion-ease), + border-color var(--motion-base) var(--motion-ease), + background-color var(--motion-base) var(--motion-ease); } .app-header-in-shell { @@ -38,6 +46,11 @@ min-width: 0; align-items: center; text-decoration: none; + transition: transform var(--motion-base) var(--motion-ease), opacity var(--motion-base) var(--motion-ease); +} + +.app-brand:hover { + transform: translateY(-1px); } .app-brand-logo { @@ -45,6 +58,12 @@ max-width: 100%; height: auto; display: block; + transition: transform var(--motion-base) var(--motion-ease), filter var(--motion-base) var(--motion-ease); +} + +.app-brand:hover .app-brand-logo { + transform: translateY(-1px); + filter: saturate(1.02); } .app-header-actions { @@ -71,6 +90,12 @@ font-size: 12px; font-weight: 700; cursor: pointer; + transition: + background-color var(--motion-fast) var(--motion-ease), + border-color var(--motion-fast) var(--motion-ease), + color var(--motion-fast) var(--motion-ease), + transform var(--motion-fast) var(--motion-ease), + box-shadow var(--motion-fast) var(--motion-ease); } .app-lang-btn.active { @@ -79,6 +104,15 @@ color: #fff; } +.app-lang-btn:hover { + transform: translateY(-1px); +} + +.app-lang-btn:focus-visible { + outline: none; + box-shadow: 0 0 0 4px rgba(0, 0, 120, 0.10); +} + .shell, .wrap, .top-wrap { @@ -99,3 +133,83 @@ justify-content: flex-start; } } + +.confirm-modal[hidden] { + display: none; +} + +.confirm-modal { + position: fixed; + inset: 0; + z-index: 1200; +} + +.confirm-backdrop { + position: absolute; + inset: 0; + background: rgba(16, 32, 57, 0.42); + backdrop-filter: blur(4px); + animation: confirmFadeIn var(--motion-base) var(--motion-ease); +} + +.confirm-dialog { + position: relative; + width: min(480px, calc(100% - 32px)); + margin: min(18vh, 120px) auto 0; + padding: 18px; + border: 1px solid rgba(217, 227, 238, 0.94); + border-radius: 22px; + background: linear-gradient(180deg, rgba(255,255,255,0.99), rgba(247,250,255,0.96)); + box-shadow: 0 24px 56px rgba(18, 34, 56, 0.20); + animation: confirmPopIn var(--motion-slow) var(--motion-ease); +} + +.confirm-dialog-head h2 { + margin: 0; + font-size: 20px; + color: #18345f; + letter-spacing: -0.02em; +} + +.confirm-message { + margin: 10px 0 0; + color: #52647a; + font-size: 14px; + line-height: 1.55; +} + +.confirm-actions { + margin-top: 18px; + display: flex; + justify-content: flex-end; + gap: 8px; + flex-wrap: wrap; +} + +body.confirm-open { + overflow: hidden; +} + +@keyframes confirmFadeIn { + from { opacity: 0; } + to { opacity: 1; } +} + +@keyframes confirmPopIn { + from { + opacity: 0; + transform: translateY(10px) scale(0.98); + } + to { + opacity: 1; + transform: translateY(0) scale(1); + } +} + +@media (prefers-reduced-motion: reduce) { + :root { + --motion-fast: 1ms; + --motion-base: 1ms; + --motion-slow: 1ms; + } +} diff --git a/backend/workflows/static/workflows/css/buttons.css b/backend/workflows/static/workflows/css/buttons.css index 28ea76c..be716fb 100644 --- a/backend/workflows/static/workflows/css/buttons.css +++ b/backend/workflows/static/workflows/css/buttons.css @@ -20,7 +20,13 @@ font-size: 14px; line-height: 1.2; cursor: pointer; - transition: background-color 0.15s ease, border-color 0.15s ease, color 0.15s ease, transform 0.08s ease; + transition: + background-color 180ms cubic-bezier(0.2, 0.8, 0.2, 1), + border-color 180ms cubic-bezier(0.2, 0.8, 0.2, 1), + color 180ms cubic-bezier(0.2, 0.8, 0.2, 1), + box-shadow 180ms cubic-bezier(0.2, 0.8, 0.2, 1), + transform 180ms cubic-bezier(0.2, 0.8, 0.2, 1), + filter 180ms cubic-bezier(0.2, 0.8, 0.2, 1); } .btn:focus-visible { @@ -34,7 +40,7 @@ } .btn:active:not(:disabled) { - transform: translateY(1px); + transform: translateY(0); } .btn-primary { @@ -44,7 +50,9 @@ } .btn-primary:hover:not(:disabled) { - filter: brightness(0.95); + filter: brightness(0.97); + transform: translateY(-1px); + box-shadow: 0 10px 20px rgba(0, 0, 120, 0.12); } .btn-secondary { @@ -55,4 +63,6 @@ .btn-secondary:hover:not(:disabled) { background: #f8fafc; + transform: translateY(-1px); + box-shadow: 0 8px 18px rgba(18, 34, 56, 0.08); } diff --git a/backend/workflows/static/workflows/css/home.css b/backend/workflows/static/workflows/css/home.css index fe7efa9..7b66da9 100644 --- a/backend/workflows/static/workflows/css/home.css +++ b/backend/workflows/static/workflows/css/home.css @@ -95,6 +95,10 @@ background: rgba(255,255,255,0.92); border-radius: 22px; box-shadow: inset 0 1px 0 rgba(255,255,255,0.92); + transition: + transform 220ms cubic-bezier(0.2, 0.8, 0.2, 1), + box-shadow 220ms cubic-bezier(0.2, 0.8, 0.2, 1), + border-color 220ms cubic-bezier(0.2, 0.8, 0.2, 1); } .hero-card { @@ -169,6 +173,12 @@ min-height: 38px; display: inline-flex; align-items: center; + transition: + background-color 180ms cubic-bezier(0.2, 0.8, 0.2, 1), + border-color 180ms cubic-bezier(0.2, 0.8, 0.2, 1), + color 180ms cubic-bezier(0.2, 0.8, 0.2, 1), + box-shadow 180ms cubic-bezier(0.2, 0.8, 0.2, 1), + transform 180ms cubic-bezier(0.2, 0.8, 0.2, 1); } .status-pill-neutral { @@ -385,6 +395,10 @@ flex-direction: column; justify-content: space-between; box-shadow: inset 0 1px 0 rgba(255,255,255,0.92); + transition: + transform 220ms cubic-bezier(0.2, 0.8, 0.2, 1), + box-shadow 220ms cubic-bezier(0.2, 0.8, 0.2, 1), + border-color 220ms cubic-bezier(0.2, 0.8, 0.2, 1); } .app-card::before { @@ -395,6 +409,17 @@ height: 118px; border-radius: 50%; background: rgba(31, 79, 214, 0.08); + transition: transform 260ms cubic-bezier(0.2, 0.8, 0.2, 1); + } + + .app-card:hover { + transform: translateY(-3px); + border-color: rgba(196, 208, 225, 1); + box-shadow: 0 14px 28px rgba(16, 32, 57, 0.10), inset 0 1px 0 rgba(255,255,255,0.92); + } + + .app-card:hover::before { + transform: scale(1.06); } .app-card.primary { @@ -468,6 +493,11 @@ font-weight: 800; letter-spacing: 0.03em; text-transform: uppercase; + transition: + background-color 180ms cubic-bezier(0.2, 0.8, 0.2, 1), + border-color 180ms cubic-bezier(0.2, 0.8, 0.2, 1), + color 180ms cubic-bezier(0.2, 0.8, 0.2, 1), + transform 180ms cubic-bezier(0.2, 0.8, 0.2, 1); } .card-actions { @@ -490,6 +520,10 @@ padding: 14px; background: linear-gradient(180deg, rgba(255,255,255,0.99), rgba(247,250,255,0.96)); box-shadow: inset 0 1px 0 rgba(255,255,255,0.92); + transition: + transform 220ms cubic-bezier(0.2, 0.8, 0.2, 1), + box-shadow 220ms cubic-bezier(0.2, 0.8, 0.2, 1), + border-color 220ms cubic-bezier(0.2, 0.8, 0.2, 1); } .admin-card::before { @@ -500,6 +534,17 @@ height: 84px; border-radius: 50%; background: rgba(31, 79, 214, 0.06); + transition: transform 260ms cubic-bezier(0.2, 0.8, 0.2, 1); + } + + .admin-card:hover { + transform: translateY(-2px); + border-color: rgba(196, 208, 225, 1); + box-shadow: 0 12px 24px rgba(16, 32, 57, 0.08), inset 0 1px 0 rgba(255,255,255,0.92); + } + + .admin-card:hover::before { + transform: scale(1.05); } .admin-card h3 { diff --git a/backend/workflows/static/workflows/css/login.css b/backend/workflows/static/workflows/css/login.css index 54cb0fd..5de49cb 100644 --- a/backend/workflows/static/workflows/css/login.css +++ b/backend/workflows/static/workflows/css/login.css @@ -51,6 +51,12 @@ body { margin-bottom: 12px; } +.field.has-error input { + border-color: #e3a3a3; + background: #fffafa; + box-shadow: 0 0 0 4px rgba(185, 28, 28, 0.06); +} + .field label { display: block; font-weight: 700; @@ -67,19 +73,44 @@ body { min-height: 44px; font: inherit; background: #fff; + transition: border-color 180ms cubic-bezier(0.2, 0.8, 0.2, 1), box-shadow 180ms cubic-bezier(0.2, 0.8, 0.2, 1), background-color 180ms cubic-bezier(0.2, 0.8, 0.2, 1); +} + +.field input:focus { + outline: none; + border-color: rgba(0, 0, 120, 0.3); + box-shadow: 0 0 0 4px rgba(0, 0, 120, 0.08); } .btn { width: 100%; } -.errorlist { - color: #b91c1c; - margin: 0 0 10px; - padding: 10px 12px; - border-radius: 10px; - border: 1px solid #f0c8c8; - background: #fff3f3; +.login-alert { + margin: 0 0 12px; + padding: 12px 14px; + border-radius: 12px; + border: 1px solid #d6e1ef; + background: #f8fbff; + display: grid; + gap: 4px; + font-size: 14px; +} + +.login-alert strong { + font-size: 14px; + line-height: 1.2; +} + +.login-alert span { + color: #5f6f85; + line-height: 1.45; +} + +.login-alert-error { + color: #991b1b; + border-color: #f0c8c8; + background: linear-gradient(180deg, #fff7f7, #fff1f1); } @media (max-width: 760px) { diff --git a/backend/workflows/static/workflows/css/requests_dashboard.css b/backend/workflows/static/workflows/css/requests_dashboard.css index 76adda7..9af2e0e 100644 --- a/backend/workflows/static/workflows/css/requests_dashboard.css +++ b/backend/workflows/static/workflows/css/requests_dashboard.css @@ -101,6 +101,10 @@ background: var(--panel); border-radius: 22px; box-shadow: inset 0 1px 0 rgba(255,255,255,0.92); + transition: + transform 220ms cubic-bezier(0.2, 0.8, 0.2, 1), + box-shadow 220ms cubic-bezier(0.2, 0.8, 0.2, 1), + border-color 220ms cubic-bezier(0.2, 0.8, 0.2, 1); } .hero-card { @@ -256,6 +260,17 @@ height: 110px; border-radius: 50%; background: rgba(31, 79, 214, 0.08); + transition: transform 260ms cubic-bezier(0.2, 0.8, 0.2, 1); + } + + .stat-card:hover { + transform: translateY(-2px); + border-color: rgba(196, 208, 225, 1); + box-shadow: 0 14px 30px rgba(18, 33, 56, 0.08), inset 0 1px 0 rgba(255,255,255,0.92); + } + + .stat-card:hover::before { + transform: scale(1.05); } .stat-card.red::before { background: rgba(163, 32, 32, 0.08); } @@ -461,6 +476,10 @@ color: var(--ink); background: rgba(255,255,255,0.98); box-shadow: inset 0 1px 0 rgba(255,255,255,0.98); + transition: + border-color 180ms cubic-bezier(0.2, 0.8, 0.2, 1), + box-shadow 180ms cubic-bezier(0.2, 0.8, 0.2, 1), + background-color 180ms cubic-bezier(0.2, 0.8, 0.2, 1); } .search-box input:focus, @@ -626,6 +645,11 @@ padding: 15px 14px; vertical-align: middle; box-shadow: 0 8px 22px rgba(26, 51, 89, 0.035); + transition: + background-color 180ms cubic-bezier(0.2, 0.8, 0.2, 1), + 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); } tbody td:first-child { @@ -686,6 +710,12 @@ border: 1px solid rgba(217, 227, 238, 0.96); background: linear-gradient(180deg, rgba(255,255,255,0.98), rgba(248,251,255,0.96)); box-shadow: inset 0 1px 0 rgba(255,255,255,0.94); + transition: + background-color 180ms cubic-bezier(0.2, 0.8, 0.2, 1), + border-color 180ms cubic-bezier(0.2, 0.8, 0.2, 1), + color 180ms cubic-bezier(0.2, 0.8, 0.2, 1), + box-shadow 180ms cubic-bezier(0.2, 0.8, 0.2, 1), + transform 180ms cubic-bezier(0.2, 0.8, 0.2, 1); } .kind-pill { @@ -725,6 +755,7 @@ text-decoration: none; font-weight: 600; word-break: break-word; + transition: color 180ms cubic-bezier(0.2, 0.8, 0.2, 1), opacity 180ms cubic-bezier(0.2, 0.8, 0.2, 1); } .mail-link:hover { text-decoration: underline; } @@ -750,6 +781,7 @@ .doc-link:hover { background: linear-gradient(180deg, rgba(248,251,255,0.99), rgba(238,244,255,0.98)); border-color: rgba(0, 0, 120, 0.18); + transform: translateY(-1px); } .actions-cell { white-space: normal; } @@ -781,6 +813,16 @@ font-weight: 800; color: #18345f; background: rgba(255,255,255,0.65); + transition: + background-color 180ms cubic-bezier(0.2, 0.8, 0.2, 1), + border-color 180ms cubic-bezier(0.2, 0.8, 0.2, 1), + box-shadow 180ms cubic-bezier(0.2, 0.8, 0.2, 1), + transform 180ms cubic-bezier(0.2, 0.8, 0.2, 1); + } + + .intro-toggle:hover { + transform: translateY(-1px); + box-shadow: 0 8px 18px rgba(18, 33, 56, 0.06); } .intro-toggle::after { diff --git a/backend/workflows/static/workflows/js/confirm_dialog.js b/backend/workflows/static/workflows/js/confirm_dialog.js new file mode 100644 index 0000000..3041f80 --- /dev/null +++ b/backend/workflows/static/workflows/js/confirm_dialog.js @@ -0,0 +1,92 @@ +(function () { + const modal = document.getElementById('app-confirm-modal'); + if (!modal) return; + + const messageNode = document.getElementById('app-confirm-message'); + const acceptBtn = document.getElementById('app-confirm-accept'); + const cancelBtn = document.getElementById('app-confirm-cancel'); + const closeNodes = modal.querySelectorAll('[data-confirm-close]'); + let resolver = null; + let lastActive = null; + + function close(result) { + modal.hidden = true; + modal.setAttribute('aria-hidden', 'true'); + document.body.classList.remove('confirm-open'); + if (resolver) { + resolver(result); + resolver = null; + } + if (lastActive && typeof lastActive.focus === 'function') { + lastActive.focus(); + } + } + + function open(message) { + lastActive = document.activeElement; + messageNode.textContent = message || ''; + modal.hidden = false; + modal.setAttribute('aria-hidden', 'false'); + document.body.classList.add('confirm-open'); + acceptBtn.focus(); + return new Promise(function (resolve) { + resolver = resolve; + }); + } + + window.AppConfirm = { open: open }; + + acceptBtn.addEventListener('click', function () { close(true); }); + cancelBtn.addEventListener('click', function () { close(false); }); + closeNodes.forEach(function (node) { + node.addEventListener('click', function () { close(false); }); + }); + + document.addEventListener('keydown', function (event) { + if (modal.hidden) return; + if (event.key === 'Escape') { + event.preventDefault(); + close(false); + } + }); + + document.addEventListener('submit', function (event) { + const form = event.target; + const message = form.dataset.confirm; + if (!message || form.dataset.confirmBypass === '1') return; + event.preventDefault(); + open(message).then(function (confirmed) { + if (!confirmed) return; + form.dataset.confirmBypass = '1'; + if (typeof form.requestSubmit === 'function') { + form.requestSubmit(); + } else { + form.submit(); + } + window.setTimeout(function () { + delete form.dataset.confirmBypass; + }, 0); + }); + }, true); + + document.addEventListener('click', function (event) { + const button = event.target.closest('button[data-confirm], input[type="submit"][data-confirm]'); + if (!button || button.dataset.confirmBypass === '1') return; + const form = button.form; + if (!form) return; + event.preventDefault(); + event.stopPropagation(); + open(button.dataset.confirm).then(function (confirmed) { + if (!confirmed) return; + button.dataset.confirmBypass = '1'; + if (typeof form.requestSubmit === 'function') { + form.requestSubmit(button); + } else { + button.click(); + } + window.setTimeout(function () { + delete button.dataset.confirmBypass; + }, 0); + }); + }, true); +})(); diff --git a/backend/workflows/static/workflows/js/welcome_emails.js b/backend/workflows/static/workflows/js/welcome_emails.js index 456d734..3bf90ae 100644 --- a/backend/workflows/static/workflows/js/welcome_emails.js +++ b/backend/workflows/static/workflows/js/welcome_emails.js @@ -29,24 +29,35 @@ }); rowChecks.forEach((c) => c.addEventListener('change', syncState)); - bulkForm.addEventListener('submit', syncState); - - window.confirmBulkAction = function () { + bulkForm.addEventListener('submit', function (event) { syncState(); const count = currentSelected().length; if (!count) { - alert(bulkForm.dataset.alertEmpty || 'Bitte mindestens einen Welcome-Eintrag auswählen.'); - return false; + event.preventDefault(); + const emptyMessage = bulkForm.dataset.alertEmpty || 'Bitte mindestens einen Welcome-Eintrag auswählen.'; + if (window.AppConfirm) { + window.AppConfirm.open(emptyMessage); + } else { + window.alert(emptyMessage); + } + return; } + if (bulkForm.dataset.confirmBypass === '1') return; + event.preventDefault(); const action = bulkAction.value; + let message = bulkForm.dataset.confirmSend || 'Ausgewählte Welcome-Einträge sofort senden?'; if (action === 'delete') { - return confirm(bulkForm.dataset.confirmDelete || 'Ausgewählte Welcome-Einträge wirklich löschen?'); + message = bulkForm.dataset.confirmDelete || 'Ausgewählte Welcome-Einträge wirklich löschen?'; + } else if (action === 'pause') { + message = bulkForm.dataset.confirmPause || 'Ausgewählte Welcome-Einträge pausieren?'; } - if (action === 'pause') { - return confirm(bulkForm.dataset.confirmPause || 'Ausgewählte Welcome-Einträge pausieren?'); - } - return confirm(bulkForm.dataset.confirmSend || 'Ausgewählte Welcome-Einträge sofort senden?'); - }; + window.AppConfirm.open(message).then(function (confirmed) { + if (!confirmed) return; + bulkForm.dataset.confirmBypass = '1'; + bulkForm.requestSubmit(); + window.setTimeout(function () { delete bulkForm.dataset.confirmBypass; }, 0); + }); + }); syncState(); })(); diff --git a/backend/workflows/templates/registration/login.html b/backend/workflows/templates/registration/login.html index 4ba811d..121a34e 100644 --- a/backend/workflows/templates/registration/login.html +++ b/backend/workflows/templates/registration/login.html @@ -21,11 +21,17 @@
{% csrf_token %} - {% if form.errors %} -
{% trans "Anmeldung fehlgeschlagen. Bitte Zugangsdaten prüfen." %}
+ {% if next %} + {% endif %} -
{{ form.username.label_tag }}{{ form.username }}
-
{{ form.password.label_tag }}{{ form.password }}
+ {% if form.errors %} + + {% endif %} +
{{ form.username.label_tag }}{{ form.username }}
+
{{ form.password.label_tag }}{{ form.password }}
diff --git a/backend/workflows/templates/workflows/base_shell.html b/backend/workflows/templates/workflows/base_shell.html index 96cf3b7..67c7159 100644 --- a/backend/workflows/templates/workflows/base_shell.html +++ b/backend/workflows/templates/workflows/base_shell.html @@ -17,6 +17,20 @@ {% block shell_header %}{% endblock %} {% block shell_body %}{% endblock %} + + {% block extra_scripts %}{% endblock %} diff --git a/backend/workflows/templates/workflows/form_builder.html b/backend/workflows/templates/workflows/form_builder.html index 94c54d3..7ef692f 100644 --- a/backend/workflows/templates/workflows/form_builder.html +++ b/backend/workflows/templates/workflows/form_builder.html @@ -108,7 +108,7 @@ - + {% empty %} @@ -164,4 +164,3 @@ {% endblock %} - diff --git a/backend/workflows/templates/workflows/intro_builder.html b/backend/workflows/templates/workflows/intro_builder.html index a7116ce..2f7acad 100644 --- a/backend/workflows/templates/workflows/intro_builder.html +++ b/backend/workflows/templates/workflows/intro_builder.html @@ -103,7 +103,7 @@ - + {% empty %} diff --git a/backend/workflows/templates/workflows/onboarding_intro_session.html b/backend/workflows/templates/workflows/onboarding_intro_session.html index b2388af..a36bcd3 100644 --- a/backend/workflows/templates/workflows/onboarding_intro_session.html +++ b/backend/workflows/templates/workflows/onboarding_intro_session.html @@ -81,7 +81,7 @@
- +
@@ -102,4 +102,3 @@ {% endblock %} - diff --git a/backend/workflows/templates/workflows/requests_dashboard.html b/backend/workflows/templates/workflows/requests_dashboard.html index cd2e62e..8f306e3 100644 --- a/backend/workflows/templates/workflows/requests_dashboard.html +++ b/backend/workflows/templates/workflows/requests_dashboard.html @@ -173,7 +173,7 @@ {% if request.user.is_staff %}
-
+ {% csrf_token %}
0 {% trans "ausgewählt" %} @@ -273,12 +273,12 @@ {% trans "Timeline" %} {% if row.status_key == 'failed' %} - + {% csrf_token %} {% endif %} -
+ {% csrf_token %}
diff --git a/backend/workflows/templates/workflows/welcome_emails.html b/backend/workflows/templates/workflows/welcome_emails.html index d3ff3c7..94eb5e9 100644 --- a/backend/workflows/templates/workflows/welcome_emails.html +++ b/backend/workflows/templates/workflows/welcome_emails.html @@ -62,7 +62,7 @@
-
+ {% csrf_token %}