From 719d122c26a49097c155fd36f7e1428805900f0d Mon Sep 17 00:00:00 2001 From: Md Bayazid Bostame Date: Tue, 24 Mar 2026 18:19:12 +0100 Subject: [PATCH] snapshot: preserve shell cleanup and login/dashboard fixes --- .../static/workflows/css/admin_tools.css | 45 + .../static/workflows/css/docs_pages.css | 22 + .../workflows/static/workflows/css/home.css | 432 +++++++++ .../workflows/static/workflows/css/login.css | 99 ++ .../css/onboarding_intro_session.css | 46 + .../workflows/css/release_checklist.css | 20 + .../workflows/css/requests_dashboard.css | 863 +++++++++++++++++ .../static/workflows/css/success_pages.css | 6 + .../static/workflows/js/offboarding_form.js | 51 + .../static/workflows/js/onboarding_form.js | 253 +++++ .../static/workflows/js/requests_dashboard.js | 20 + .../static/workflows/js/welcome_emails.js | 52 + .../templates/registration/login.html | 27 +- .../workflows/developer_handbook.html | 23 +- .../templates/workflows/handbook.html | 22 +- .../workflows/templates/workflows/home.html | 435 +-------- .../workflows/integrations_setup.html | 68 +- .../templates/workflows/intro_builder.html | 30 +- .../templates/workflows/offboarding_form.html | 53 +- .../workflows/offboarding_success.html | 15 +- .../templates/workflows/onboarding_form.html | 257 +---- .../workflows/onboarding_intro_session.html | 53 +- .../workflows/onboarding_success.html | 15 +- .../templates/workflows/project_wiki.html | 22 +- .../workflows/release_checklist.html | 27 +- .../workflows/requests_dashboard.html | 901 +----------------- .../templates/workflows/welcome_emails.html | 95 +- 27 files changed, 1976 insertions(+), 1976 deletions(-) create mode 100644 backend/workflows/static/workflows/css/admin_tools.css create mode 100644 backend/workflows/static/workflows/css/docs_pages.css create mode 100644 backend/workflows/static/workflows/css/home.css create mode 100644 backend/workflows/static/workflows/css/login.css create mode 100644 backend/workflows/static/workflows/css/onboarding_intro_session.css create mode 100644 backend/workflows/static/workflows/css/release_checklist.css create mode 100644 backend/workflows/static/workflows/css/requests_dashboard.css create mode 100644 backend/workflows/static/workflows/css/success_pages.css create mode 100644 backend/workflows/static/workflows/js/offboarding_form.js create mode 100644 backend/workflows/static/workflows/js/onboarding_form.js create mode 100644 backend/workflows/static/workflows/js/requests_dashboard.js create mode 100644 backend/workflows/static/workflows/js/welcome_emails.js diff --git a/backend/workflows/static/workflows/css/admin_tools.css b/backend/workflows/static/workflows/css/admin_tools.css new file mode 100644 index 0000000..876f90b --- /dev/null +++ b/backend/workflows/static/workflows/css/admin_tools.css @@ -0,0 +1,45 @@ +body { margin: 0; font-family: Arial, sans-serif; background: #f4f8ff; color: #0f172a; padding: 20px; } +.shell { max-width: 1100px; margin: 0 auto; background: #fff; border: 1px solid #d8e3f0; border-radius: 14px; padding: 16px; } +h1 { margin: 12px 0 6px; color: #000078; } +.sub { margin: 0 0 12px; color: #54657c; } +.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; } +.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; } +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; } +.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.active { background: #000078; color: #fff; border-color: #000078; } +.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; } +.table-wrap, .option-table-wrap { overflow-x: auto; } +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; } +.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; } +.badge { display: inline-block; padding: 2px 8px; border-radius: 999px; font-size: 12px; font-weight: 700; } +.scheduled { background: #eff6ff; color: #1d4ed8; } +.paused { background: #fef9c3; color: #854d0e; } +.cancelled { background: #f1f5f9; color: #334155; } +.sent { background: #ecfdf3; color: #166534; } +.failed { background: #fff1f2; color: #991b1b; } +.bulk-bar { margin: 0 0 10px; display: flex; gap: 8px; align-items: center; flex-wrap: wrap; } +.bulk-bar select { width: auto; min-width: 180px; } +.select-col { width: 42px; text-align: center; } +.bulk-note { color: #64748b; font-size: 12px; } +.field label { display: block; font-weight: 600; margin-bottom: 6px; } +.field input, .field select { min-height: 40px; } +.mini { color: #64748b; font-size: 12px; } +.table-controls input[type="text"], .table-controls select { width: 100%; min-height: 36px; padding: 7px 9px; border: 1px solid #cfd9e8; border-radius: 8px; box-sizing: border-box; } +.table-controls input[type="checkbox"] { transform: scale(1.1); width: auto; } +.actions { white-space: nowrap; } +@media (max-width: 760px) { .grid { grid-template-columns: 1fr; } } diff --git a/backend/workflows/static/workflows/css/docs_pages.css b/backend/workflows/static/workflows/css/docs_pages.css new file mode 100644 index 0000000..02b429d --- /dev/null +++ b/backend/workflows/static/workflows/css/docs_pages.css @@ -0,0 +1,22 @@ +body { margin: 0; font-family: Arial, sans-serif; background: #f4f8ff; color: #1b2b43; padding: 20px; } +.shell { max-width: 1120px; margin: 0 auto; background: #fff; border: 1px solid #d7e0ea; border-radius: 14px; padding: 18px; } +.brand-logo { width: 190px; max-width: 100%; height: auto; margin: 0 0 10px; display: block; } +.top { display: flex; justify-content: space-between; gap: 10px; align-items: center; flex-wrap: wrap; margin-bottom: 8px; } +h1 { margin: 0; color: #000078; font-size: 30px; } +.sub { margin: 8px 0 16px; color: #5f6f85; } +.toc { border: 1px solid #d7e0ea; border-radius: 10px; padding: 10px; background: #f7fbff; margin-bottom: 16px; } +.toc a { color: #0b4da2; text-decoration: none; margin-right: 10px; white-space: nowrap; } +h2 { margin: 20px 0 8px; color: #113a74; border-bottom: 1px solid #e1e8f2; padding-bottom: 4px; } +h3 { margin: 14px 0 6px; color: #183f77; } +ul { margin: 8px 0 12px 20px; } +li { margin: 4px 0; } +code { background: #f1f5fb; border: 1px solid #dce6f3; border-radius: 6px; padding: 2px 6px; } +pre { background: #f7fbff; border: 1px solid #dce6f3; border-radius: 10px; padding: 10px; overflow-x: auto; } +.box { border: 1px solid #d7e0ea; border-radius: 10px; padding: 10px; background: #fcfdff; margin: 8px 0 12px; } +.note { border-left: 4px solid #000078; padding: 8px 10px; background: #f4f8ff; margin: 10px 0; } +.grid { display: grid; grid-template-columns: repeat(3, minmax(260px, 1fr)); gap: 14px; } +.card { border: 1px solid #d7e0ea; border-radius: 14px; background: #fcfdff; padding: 16px; } +.eyebrow { display: inline-block; padding: 5px 10px; border-radius: 999px; background: #eef4ff; color: #244a8f; border: 1px solid #d5e2f9; font-size: 12px; font-weight: 700; margin-bottom: 10px; } +p { margin: 0 0 14px; color: #5f6f85; } +.actions { display: flex; gap: 8px; flex-wrap: wrap; } +@media (max-width: 760px) { .grid { grid-template-columns: 1fr; } } diff --git a/backend/workflows/static/workflows/css/home.css b/backend/workflows/static/workflows/css/home.css new file mode 100644 index 0000000..417d94a --- /dev/null +++ b/backend/workflows/static/workflows/css/home.css @@ -0,0 +1,432 @@ +:root { + --brand-blue: #000078; + --brand-red: #8c1d1d; + --ink: #102039; + --muted: #5f6f85; + --line: #d8e1ee; + --panel: #ffffff; + --bg-soft: #eff4ff; + --ok-bg: #effaf2; + --ok-ink: #166534; + --warn-bg: #fff6ea; + --warn-ink: #8a4f00; + } + + * { box-sizing: border-box; } + + body { + margin: 0; + font-family: "Segoe UI", "Helvetica Neue", Arial, sans-serif; + color: var(--ink); + background: + radial-gradient(80% 120% at 85% 8%, rgba(0, 0, 120, 0.12), rgba(0, 0, 120, 0)), + radial-gradient(70% 90% at 8% 92%, rgba(140, 29, 29, 0.10), rgba(140, 29, 29, 0)), + linear-gradient(165deg, #eef3ff, #f7f9ff 48%, #f0f5ff); + min-height: 100vh; + padding: 24px; + } + + .shell { + width: min(1380px, 100%); + margin: 0 auto; + background: var(--panel); + border: 1px solid var(--line); + border-radius: 20px; + box-shadow: 0 20px 44px rgba(16, 32, 57, 0.13); + overflow: hidden; + } + + .topbar { + display: flex; + justify-content: space-between; + align-items: flex-start; + gap: 16px; + padding: 18px 22px; + border-bottom: 1px solid var(--line); + background: #fff; + } + + .brand-wrap { + display: flex; + flex-direction: column; + gap: 8px; + flex: 0 0 auto; + min-width: 0; + } + + .brand-logo { + width: 210px; + max-width: 100%; + height: auto; + display: block; + margin: 0; + flex: 0 0 auto; + } + + .quick-actions { + display: flex; + gap: 8px; + flex-wrap: wrap; + justify-content: flex-end; + align-items: center; + } + .lang-switch { display:flex; gap:6px; } + .lang-btn { border:1px solid var(--line); background:#f8fbff; color:#1f3a5f; border-radius:999px; padding:6px 10px; font-size:12px; font-weight:700; cursor:pointer; } + .lang-btn.active { background:var(--brand-blue); border-color:var(--brand-blue); color:#fff; } + + .hero { + padding: 24px; + border-bottom: 1px solid var(--line); + background: + linear-gradient(135deg, rgba(0, 0, 120, 0.08), rgba(0, 0, 120, 0) 44%), + linear-gradient(180deg, rgba(255,255,255,0.96), rgba(246,250,255,0.92)); + } + + .hero-grid { + display: grid; + grid-template-columns: 1fr; + gap: 18px; + align-items: stretch; + } + + .hero-card, + .hero-sidecard { + border: 1px solid rgba(217, 227, 238, 0.94); + background: rgba(255,255,255,0.92); + border-radius: 22px; + box-shadow: inset 0 1px 0 rgba(255,255,255,0.92); + } + + .hero-card { + padding: 24px; + background: + radial-gradient(100% 140% at 10% 10%, rgba(31,79,214,0.12), rgba(31,79,214,0)), + linear-gradient(180deg, rgba(255,255,255,0.98), rgba(247,250,255,0.95)); + } + + .hero-sidecard { + padding: 18px; + display: flex; + flex-direction: column; + justify-content: space-between; + background: + radial-gradient(120% 120% at 100% 0%, rgba(163,32,32,0.10), rgba(163,32,32,0)), + linear-gradient(180deg, rgba(255,255,255,0.98), rgba(253,247,247,0.96)); + } + + .eyebrow { + display: inline-flex; + align-items: center; + gap: 8px; + padding: 6px 10px; + border-radius: 999px; + border: 1px solid rgba(0,0,120,0.12); + background: rgba(0,0,120,0.05); + color: var(--brand-blue); + font-size: 12px; + font-weight: 700; + letter-spacing: 0.03em; + text-transform: uppercase; + } + + .hero h1 { + margin: 14px 0 8px; + font-size: clamp(30px, 5vw, 46px); + line-height: 1; + letter-spacing: -0.04em; + color: var(--brand-blue); + } + + .hero p { + margin: 0; + color: var(--muted); + max-width: 720px; + font-size: 15px; + line-height: 1.55; + } + + .status-row { + margin-top: 16px; + display: flex; + gap: 8px; + flex-wrap: wrap; + } + + .status-pill { + border-radius: 999px; + border: 1px solid var(--line); + background: #fff; + color: #30445f; + padding: 8px 12px; + font-size: 12px; + font-weight: 700; + } + + .status-pill.ok { + background: var(--ok-bg); + color: var(--ok-ink); + border-color: #c7ecd2; + } + + .status-pill.warn { + background: var(--warn-bg); + color: var(--warn-ink); + border-color: #f7dfbb; + } + + .hero-sidecard h2 { + margin: 0; + font-size: 18px; + color: #1a3359; + } + + .hero-sidecard p { + margin: 8px 0 0; + color: var(--muted); + font-size: 13px; + line-height: 1.5; + } + + .hero-metrics { + margin-top: 18px; + display: grid; + gap: 10px; + } + + .hero-metric { + padding: 12px 14px; + border-radius: 16px; + background: rgba(255,255,255,0.86); + border: 1px solid rgba(217, 227, 238, 0.95); + } + + .hero-metric-label { + font-size: 12px; + color: var(--muted); + text-transform: uppercase; + letter-spacing: 0.04em; + font-weight: 700; + } + + .hero-metric-value { + margin-top: 5px; + font-size: 26px; + font-weight: 800; + color: var(--brand-blue); + letter-spacing: -0.03em; + } + + .main { + padding: 20px 22px 24px; + } + + .section-head { + margin: 0 0 12px; + display: flex; + justify-content: space-between; + gap: 10px; + align-items: flex-end; + flex-wrap: wrap; + } + + .section-head h2 { + margin: 0; + font-size: 19px; + color: #172b4a; + } + + .section-head p { + margin: 0; + color: var(--muted); + font-size: 13px; + } + + .apps-grid { + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: 14px; + margin-bottom: 18px; + } + + .app-card { + position: relative; + overflow: hidden; + border: 1px solid rgba(217, 227, 238, 0.94); + border-radius: 22px; + background: linear-gradient(180deg, rgba(255,255,255,0.98), rgba(247,250,255,0.95)); + padding: 18px; + min-height: 222px; + display: flex; + flex-direction: column; + justify-content: space-between; + box-shadow: inset 0 1px 0 rgba(255,255,255,0.92); + } + + .app-card::before { + content: ""; + position: absolute; + inset: auto -24px -38px auto; + width: 118px; + height: 118px; + border-radius: 50%; + background: rgba(31, 79, 214, 0.08); + } + + .app-card.primary { + background: linear-gradient(180deg, rgba(255,255,255,0.99), rgba(242,247,255,0.97)); + } + + .app-card.red { + background: linear-gradient(180deg, rgba(255,255,255,0.99), rgba(255,245,245,0.97)); + } + + .app-card.red::before { background: rgba(163, 32, 32, 0.08); } + + .app-card .top-line { + display: flex; + justify-content: space-between; + align-items: flex-start; + gap: 12px; + margin-bottom: 14px; + } + + .accent { + width: 38px; + height: 38px; + border-radius: 14px; + background: rgba(0, 0, 120, 0.06); + color: var(--brand-blue); + display: inline-flex; + align-items: center; + justify-content: center; + font-size: 12px; + font-weight: 800; + letter-spacing: 0.04em; + margin-bottom: 0; + } + + .accent.red { + background: rgba(163, 32, 32, 0.08); + color: var(--brand-red); + } + + .app-title { + margin: 0; + font-size: 24px; + line-height: 1.05; + letter-spacing: -0.03em; + color: var(--ink); + } + + .app-text { + margin: 10px 0 12px; + color: #5a6a81; + font-size: 14px; + line-height: 1.5; + max-width: 34ch; + } + + .tag-row { + display: flex; + gap: 8px; + flex-wrap: wrap; + margin-bottom: 12px; + } + + .tag { + border: 1px solid rgba(217, 227, 238, 0.96); + border-radius: 14px; + padding: 7px 10px; + background: linear-gradient(180deg, rgba(255,255,255,0.98), rgba(248,251,255,0.96)); + color: #486183; + font-size: 11px; + font-weight: 800; + letter-spacing: 0.03em; + text-transform: uppercase; + } + + .card-actions { + display: flex; + gap: 8px; + flex-wrap: wrap; + } + + .admin-grid { + display: grid; + grid-template-columns: repeat(4, minmax(0, 1fr)); + gap: 10px; + } + + .admin-card { + position: relative; + overflow: hidden; + border: 1px solid rgba(217, 227, 238, 0.94); + border-radius: 18px; + 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); + } + + .admin-card::before { + content: ""; + position: absolute; + inset: auto -18px -30px auto; + width: 84px; + height: 84px; + border-radius: 50%; + background: rgba(31, 79, 214, 0.06); + } + + .admin-card h3 { + margin: 0 0 7px; + font-size: 15px; + color: #18345f; + letter-spacing: -0.01em; + } + + .admin-card p { + margin: 0 0 11px; + font-size: 12px; + color: #607088; + min-height: 34px; + line-height: 1.45; + max-width: 26ch; + } + + .msg { + border-radius: 10px; + padding: 10px 12px; + margin: 0 0 14px; + border: 1px solid #d6e1ef; + background: #f8fbff; + color: #1f3a5f; + font-size: 14px; + } + + .msg.error { + border-color: #fecaca; + background: #fff1f2; + color: #991b1b; + } + + .footer-note { + margin-top: 16px; + border-top: 1px solid var(--line); + padding-top: 12px; + color: var(--muted); + font-size: 13px; + } + + @media (max-width: 1080px) { + .hero-grid { grid-template-columns: 1fr; } + .apps-grid { grid-template-columns: 1fr 1fr; } + .admin-grid { grid-template-columns: 1fr 1fr; } + } + + @media (max-width: 760px) { + body { padding: 12px; } + .topbar, .hero, .main { padding-left: 14px; padding-right: 14px; } + .hero h1 { font-size: 28px; } + .apps-grid, .admin-grid { grid-template-columns: 1fr; } + .quick-actions { justify-content: flex-start; } + } diff --git a/backend/workflows/static/workflows/css/login.css b/backend/workflows/static/workflows/css/login.css new file mode 100644 index 0000000..54cb0fd --- /dev/null +++ b/backend/workflows/static/workflows/css/login.css @@ -0,0 +1,99 @@ +body { + margin: 0; + min-height: 100vh; + font-family: "Segoe UI", "Helvetica Neue", Arial, sans-serif; + background: + radial-gradient(70% 90% at 8% 8%, rgba(0, 0, 120, 0.14), rgba(0, 0, 120, 0)), + radial-gradient(60% 85% at 92% 88%, rgba(163, 32, 32, 0.12), rgba(163, 32, 32, 0)), + linear-gradient(160deg, #eef3ff 0%, #f9fbff 48%, #edf4ff 100%); + padding: 24px; +} + +.shell { + background: rgba(255, 255, 255, 0.78); + backdrop-filter: blur(12px); + border: 1px solid rgba(217, 227, 238, 0.9); + border-radius: 28px; + box-shadow: 0 22px 48px rgba(18, 34, 56, 0.14); + overflow: hidden; +} + +.login-shell-body { + padding: 24px; + background: + radial-gradient(90% 120% at 10% 0%, rgba(31, 79, 214, 0.06), rgba(31, 79, 214, 0)), + linear-gradient(180deg, rgba(255,255,255,0.72), rgba(248,251,255,0.48)); +} + +.login-card { + width: min(440px, 100%); + margin: 0 auto; + background: rgba(255, 255, 255, 0.94); + border: 1px solid #d9e3f0; + border-radius: 20px; + padding: 22px; + box-shadow: 0 14px 32px rgba(28, 45, 79, 0.10), inset 0 1px 0 rgba(255,255,255,0.92); +} + +.login-card h1 { + margin: 0 0 8px; + font-size: 24px; + color: #132238; +} + +.login-card p { + margin: 0 0 14px; + color: #607086; + line-height: 1.45; +} + +.field { + margin-bottom: 12px; +} + +.field label { + display: block; + font-weight: 700; + margin-bottom: 6px; + color: #132238; +} + +.field input { + width: 100%; + padding: 10px 12px; + box-sizing: border-box; + border: 1px solid #cbd5e1; + border-radius: 10px; + min-height: 44px; + font: inherit; + background: #fff; +} + +.btn { + width: 100%; +} + +.errorlist { + color: #b91c1c; + margin: 0 0 10px; + padding: 10px 12px; + border-radius: 10px; + border: 1px solid #f0c8c8; + background: #fff3f3; +} + +@media (max-width: 760px) { + body { + padding: 14px; + } + + .login-shell-body { + padding: 16px; + } + + .login-card { + width: 100%; + padding: 18px; + border-radius: 16px; + } +} diff --git a/backend/workflows/static/workflows/css/onboarding_intro_session.css b/backend/workflows/static/workflows/css/onboarding_intro_session.css new file mode 100644 index 0000000..e4f7223 --- /dev/null +++ b/backend/workflows/static/workflows/css/onboarding_intro_session.css @@ -0,0 +1,46 @@ +:root { + --brand-blue: #000078; + --ink: #17253b; + --muted: #607087; + --line: #d7e0ea; + --panel: #ffffff; + --soft: #f4f8ff; + --soft-strong: #eef4ff; + --ok-bg: #edf9f1; + --ok-ink: #116634; + --warn-bg: #fff8ea; + --warn-ink: #8a5a00; + } + * { box-sizing: border-box; } + body { margin: 0; font-family: "Segoe UI", Arial, sans-serif; background: linear-gradient(180deg, #eef4ff, #f8fbff); color: var(--ink); padding: 24px; } + .shell { width: min(var(--app-shell-width), 100%); margin: 0 auto; background: var(--panel); border: 1px solid var(--line); border-radius: 28px; box-shadow: 0 22px 48px rgba(18,34,56,.14); overflow: hidden; } + .hero { padding: 20px 22px 18px; border-bottom: 1px solid var(--line); background: linear-gradient(135deg, rgba(0,0,120,.06), rgba(0,0,120,0) 48%), linear-gradient(180deg, #ffffff, #f8fbff); } + .hero h1 { margin: 0; font-size: 32px; line-height: 1.08; color: var(--brand-blue); } + .sub { margin: 8px 0 0; color: var(--muted); max-width: 780px; } + .content { padding: 20px 22px 24px; } + .flash { margin: 0 0 12px; padding: 10px 12px; border-radius: 10px; border: 1px solid #bfe6c9; background: var(--ok-bg); color: var(--ok-ink); } + .meta { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 14px; margin-bottom: 16px; } + .card { border: 1px solid var(--line); border-radius: 14px; background: #fff; padding: 14px; } + .card h2 { margin: 0 0 10px; font-size: 16px; color: #1b3764; } + .progress-block { border: 1px solid var(--line); border-radius: 14px; background: linear-gradient(180deg, #ffffff, #f9fbff); padding: 14px; margin-bottom: 16px; } + .progress-top { display: flex; justify-content: space-between; gap: 10px; align-items: center; flex-wrap: wrap; margin-bottom: 10px; } + .progress-label { font-size: 15px; font-weight: 700; color: #18345f; } + .progress-meta { color: var(--muted); font-size: 13px; } + .progress-bar { width: 100%; height: 12px; border-radius: 999px; overflow: hidden; background: #e7eefb; border: 1px solid #d6e1f5; } + .progress-fill { height: 100%; background: linear-gradient(90deg, #3056a3, #0f5fcf); } + .meta-grid { display: grid; grid-template-columns: 170px 1fr; gap: 8px 10px; font-size: 14px; } + .meta-grid strong { color: #334155; } + .status-pill { display: inline-block; padding: 4px 10px; border-radius: 999px; border: 1px solid #d7e0ea; background: #f8fbff; color: #486183; font-size: 12px; font-weight: 700; } + .status-pill.done { background: var(--ok-bg); color: var(--ok-ink); border-color: #bfe6c9; } + .status-pill.draft { background: var(--warn-bg); color: var(--warn-ink); border-color: #f5d8a8; } + .section { border: 1px solid var(--line); border-radius: 14px; overflow: hidden; margin-bottom: 14px; background: #fff; } + .section-head { padding: 11px 14px; font-weight: 700; color: #1f376b; background: var(--soft-strong); border-bottom: 1px solid #d5e2f9; } + .items { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0; padding: 6px 14px 10px; } + .item { display: flex; align-items: flex-start; gap: 12px; padding: 10px 0; border-bottom: 1px solid #eef3f8; } + .item:nth-last-child(-n+2) { border-bottom: 0; } + .item input { margin-top: 2px; width: 18px; height: 18px; accent-color: var(--brand-blue); } + .item span { line-height: 1.45; } + textarea { width: 100%; min-height: 150px; border: 1px solid #cfd9e8; border-radius: 10px; padding: 11px 12px; font: inherit; resize: vertical; } + .help { color: var(--muted); font-size: 13px; margin-top: 8px; } + .actions { display: flex; gap: 10px; flex-wrap: wrap; margin-top: 14px; } + @media (max-width: 900px) { .meta, .items { grid-template-columns: 1fr; } .meta-grid { grid-template-columns: 1fr; } .item:nth-last-child(-n+2) { border-bottom: 1px solid #eef3f8; } .item:last-child { border-bottom: 0; } } diff --git a/backend/workflows/static/workflows/css/release_checklist.css b/backend/workflows/static/workflows/css/release_checklist.css new file mode 100644 index 0000000..5011299 --- /dev/null +++ b/backend/workflows/static/workflows/css/release_checklist.css @@ -0,0 +1,20 @@ +body { margin: 0; font-family: Arial, sans-serif; background: #f4f8ff; color: #1b2b43; padding: 20px; } + .shell { max-width: 1120px; margin: 0 auto; } + .brand-logo { width: 190px; max-width: 100%; height: auto; margin: 0 0 10px; display: block; } + .hero, .panel { background: #fff; border: 1px solid #d7e0ea; border-radius: 16px; box-shadow: 0 20px 40px rgba(17, 58, 116, 0.08); } + .hero { padding: 20px; margin-bottom: 18px; } + .top { display: flex; justify-content: space-between; align-items: center; gap: 12px; flex-wrap: wrap; } + .eyebrow { display: inline-block; padding: 5px 10px; border-radius: 999px; background: #eef4ff; color: #244a8f; border: 1px solid #d5e2f9; font-size: 12px; font-weight: 700; margin-bottom: 10px; } + h1 { margin: 0; color: #000078; font-size: 30px; } + .sub { margin: 8px 0 0; color: #5f6f85; max-width: 820px; } + .grid { display: grid; grid-template-columns: repeat(2, minmax(320px, 1fr)); gap: 16px; } + .panel { padding: 18px; } + h2 { margin: 0 0 12px; color: #113a74; font-size: 20px; } + ol, ul { margin: 0; padding-left: 20px; } + li { margin: 8px 0; color: #334155; } + .checklist li { list-style: none; position: relative; padding-left: 28px; } + .checklist li::before { content: '\25A1'; position: absolute; left: 0; top: 0; color: #66758c; font-size: 16px; } + pre { margin: 12px 0 0; padding: 14px; border-radius: 12px; background: #f7faff; border: 1px solid #dfe8f4; overflow-x: auto; color: #16345f; } + code { font-family: Menlo, Monaco, Consolas, monospace; font-size: 13px; } + .note { margin-top: 16px; padding: 14px 16px; border-radius: 12px; background: #f7faff; border: 1px solid #dfe8f4; color: #355175; } + @media (max-width: 820px) { .grid { grid-template-columns: 1fr; } } diff --git a/backend/workflows/static/workflows/css/requests_dashboard.css b/backend/workflows/static/workflows/css/requests_dashboard.css new file mode 100644 index 0000000..61d8e11 --- /dev/null +++ b/backend/workflows/static/workflows/css/requests_dashboard.css @@ -0,0 +1,863 @@ +:root { + --brand-blue: #000078; + --brand-blue-soft: #1f4fd6; + --brand-red: #a32020; + --ink: #132238; + --muted: #607086; + --line: #d9e3ee; + --line-strong: #c8d5e5; + --panel: rgba(255, 255, 255, 0.9); + --panel-strong: #ffffff; + --bg-soft: #eef3ff; + --ok-bg: #ecf9f0; + --ok-ink: #1f7a3f; + --warn-bg: #fff6e5; + --warn-ink: #9a6400; + --danger-bg: #fff1f1; + --danger-ink: #982222; + --shadow: 0 22px 48px rgba(18, 34, 56, 0.14); + } + + * { box-sizing: border-box; } + + body { + margin: 0; + font-family: "Segoe UI", "Helvetica Neue", Arial, sans-serif; + color: var(--ink); + min-height: 100vh; + background: + radial-gradient(70% 90% at 8% 8%, rgba(0, 0, 120, 0.14), rgba(0, 0, 120, 0)), + radial-gradient(60% 85% at 92% 88%, rgba(163, 32, 32, 0.12), rgba(163, 32, 32, 0)), + linear-gradient(160deg, #eef3ff 0%, #f9fbff 48%, #edf4ff 100%); + padding: 24px; + } + + .shell { + width: min(1380px, 100%); + margin: 0 auto; + background: rgba(255, 255, 255, 0.78); + backdrop-filter: blur(12px); + border: 1px solid rgba(217, 227, 238, 0.9); + border-radius: 28px; + box-shadow: var(--shadow); + overflow: hidden; + } + + .topbar { + display: flex; + justify-content: space-between; + align-items: flex-start; + gap: 18px; + padding: 22px 24px 18px; + border-bottom: 1px solid rgba(217, 227, 238, 0.9); + background: linear-gradient(180deg, rgba(255,255,255,0.95), rgba(248,251,255,0.84)); + } + + .brand-wrap { + display: flex; + flex-direction: column; + gap: 12px; + } + + .brand-logo { + width: 212px; + max-width: 100%; + height: auto; + display: block; + margin: 0; + } + + .quick-actions { + display: flex; + gap: 8px; + flex-wrap: wrap; + justify-content: flex-end; + align-items: center; + } + .lang-switch { display:flex; gap:6px; } + .lang-btn { border:1px solid var(--line); background:#f8fbff; color:#1f3a5f; border-radius:999px; padding:6px 10px; font-size:12px; font-weight:700; cursor:pointer; } + .lang-btn.active { background:var(--brand-blue); border-color:var(--brand-blue); color:#fff; } + + .hero { + padding: 0; + border-bottom: 0; + background: transparent; + } + + .hero-grid { + display: grid; + grid-template-columns: minmax(0, 1.4fr) minmax(320px, 0.8fr); + gap: 12px; + align-items: stretch; + } + + .hero-card, + .pulse-card, + .controls-card, + .table-card, + .chart-card, + .stat-card { + border: 1px solid rgba(217, 227, 238, 0.94); + background: var(--panel); + border-radius: 22px; + box-shadow: inset 0 1px 0 rgba(255,255,255,0.92); + } + + .hero-card { + padding: 14px; + background: + radial-gradient(100% 140% at 10% 10%, rgba(31,79,214,0.10), rgba(31,79,214,0)), + linear-gradient(180deg, rgba(255,255,255,0.99), rgba(248,251,255,0.97)); + } + + .eyebrow { + display: inline-flex; + align-items: center; + gap: 8px; + padding: 6px 10px; + border-radius: 999px; + border: 1px solid rgba(0,0,120,0.12); + background: rgba(0,0,120,0.05); + color: var(--brand-blue); + font-size: 12px; + font-weight: 700; + letter-spacing: 0.03em; + text-transform: uppercase; + } + + .hero h1 { + margin: 8px 0 4px; + font-size: clamp(24px, 3.6vw, 34px); + line-height: 1; + letter-spacing: -0.04em; + color: var(--brand-blue); + } + + .hero p { + margin: 0; + max-width: 760px; + color: var(--muted); + font-size: 13px; + line-height: 1.45; + } + + .hero-pills { + margin-top: 12px; + display: flex; + gap: 8px; + flex-wrap: wrap; + } + + .hero-pill { + display: inline-flex; + align-items: center; + gap: 6px; + min-height: 34px; + padding: 8px 12px; + border-radius: 14px; + 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)); + color: #294465; + font-size: 12px; + font-weight: 800; + line-height: 1; + box-shadow: inset 0 1px 0 rgba(255,255,255,0.94); + } + + .pulse-card { + padding: 18px; + display: flex; + flex-direction: column; + justify-content: space-between; + min-height: 100%; + background: + radial-gradient(120% 120% at 100% 0%, rgba(163,32,32,0.10), rgba(163,32,32,0)), + linear-gradient(180deg, rgba(255,255,255,0.98), rgba(253,247,247,0.96)); + } + + .activity-card { + padding: 18px; + background: rgba(255,255,255,0.95); + } + + .activity-card .chart { + min-height: 150px; + padding-top: 6px; + } + + .activity-card .bar-wrap { + min-height: 104px; + } + + .pulse-card h2 { + margin: 0; + font-size: 18px; + color: #1a3359; + } + + .pulse-card p { + margin: 8px 0 0; + color: var(--muted); + font-size: 13px; + line-height: 1.5; + } + + .pulse-metrics { + margin-top: 18px; + display: grid; + gap: 10px; + } + + .pulse-metric { + padding: 12px 14px; + border-radius: 16px; + background: rgba(255,255,255,0.86); + border: 1px solid rgba(217, 227, 238, 0.95); + } + + .pulse-label { + font-size: 12px; + color: var(--muted); + text-transform: uppercase; + letter-spacing: 0.04em; + font-weight: 700; + } + + .pulse-value { + margin-top: 5px; + font-size: 26px; + font-weight: 800; + color: var(--brand-blue); + letter-spacing: -0.03em; + } + + .metrics-grid { + padding: 0; + display: grid; + grid-template-columns: repeat(4, minmax(0, 1fr)); + gap: 12px; + margin-bottom: 0; + } + + .stat-card { + padding: 14px; + position: relative; + overflow: hidden; + min-height: 110px; + border-radius: 18px; + box-shadow: 0 10px 24px rgba(18, 33, 56, 0.04), inset 0 1px 0 rgba(255,255,255,0.92); + } + + .stat-card::before { + content: ""; + position: absolute; + inset: auto -24px -36px auto; + width: 110px; + height: 110px; + border-radius: 50%; + background: rgba(31, 79, 214, 0.08); + } + + .stat-card.red::before { background: rgba(163, 32, 32, 0.08); } + .stat-card.gold::before { background: rgba(194, 137, 26, 0.10); } + .stat-card.ink::before { background: rgba(19, 34, 56, 0.08); } + + .stat-head { + display: flex; + justify-content: space-between; + gap: 10px; + align-items: flex-start; + } + + .stat-title { + margin: 0; + font-size: 12px; + color: var(--muted); + text-transform: uppercase; + letter-spacing: 0.05em; + font-weight: 800; + } + + .stat-kicker { + display: inline-flex; + align-items: center; + justify-content: center; + width: 34px; + height: 34px; + border-radius: 12px; + background: rgba(0, 0, 120, 0.06); + color: var(--brand-blue); + font-weight: 800; + } + + .stat-card.red .stat-kicker { + background: rgba(163, 32, 32, 0.08); + color: var(--brand-red); + } + + .stat-card.gold .stat-kicker { + background: rgba(194, 137, 26, 0.10); + color: #9a6400; + } + + .stat-card.ink .stat-kicker { + background: rgba(19, 34, 56, 0.08); + color: #132238; + } + + .stat-value { + margin-top: 12px; + font-size: clamp(24px, 2.6vw, 30px); + font-weight: 900; + letter-spacing: -0.05em; + color: var(--ink); + } + + .stat-foot { + margin-top: 5px; + color: var(--muted); + font-size: 12px; + line-height: 1.4; + max-width: 220px; + } + + .content-grid { + padding: 18px 24px 24px; + display: grid; + grid-template-columns: 1fr; + gap: 16px; + align-items: start; + } + + .top-stack { + padding: 12px 24px 0; + display: grid; + gap: 12px; + } + + + .top-content-grid { + display: grid; + grid-template-columns: minmax(0, 1.5fr) minmax(320px, 1fr); + gap: 16px; + align-items: stretch; + } + + .chart-card, + .controls-card, + .table-card { + padding: 14px; + background: rgba(255,255,255,0.94); + border-radius: 22px; + box-shadow: 0 12px 28px rgba(18, 33, 56, 0.045), inset 0 1px 0 rgba(255,255,255,0.92); + } + + .section-head { + display: flex; + justify-content: space-between; + align-items: flex-end; + gap: 10px; + flex-wrap: wrap; + margin-bottom: 10px; + } + + .section-head h2 { + margin: 0; + font-size: 18px; + color: #18345f; + } + + .section-head p { + margin: 0; + font-size: 13px; + color: var(--muted); + } + + .chart { + display: grid; + grid-template-columns: repeat(14, minmax(0, 1fr)); + gap: 6px; + align-items: end; + min-height: 180px; + padding-top: 6px; + } + + .chart-col { + display: flex; + flex-direction: column; + align-items: center; + gap: 6px; + min-width: 0; + } + + .bar-wrap { + width: 100%; + min-height: 124px; + display: flex; + align-items: flex-end; + justify-content: center; + padding: 0 4px; + } + + .bar { + width: 100%; + max-width: 34px; + background: linear-gradient(180deg, #2b5bda, #071f7d); + border-radius: 14px 14px 6px 6px; + box-shadow: inset 0 1px 0 rgba(255,255,255,0.28); + } + + .bar-value { + font-size: 11px; + font-weight: 800; + color: #294465; + } + + .bar-label { + font-size: 11px; + color: var(--muted); + text-align: center; + } + + .controls-card { + position: static; + } + + .search-form { + display: grid; + gap: 12px; + } + + .search-box { + position: relative; + } + + .search-box input { + width: 100%; + border: 1px solid var(--line-strong); + border-radius: 16px; + padding: 14px 16px; + font: inherit; + font-weight: 600; + color: var(--ink); + background: rgba(255,255,255,0.98); + box-shadow: inset 0 1px 0 rgba(255,255,255,0.98); + } + + .search-box input:focus { + outline: none; + border-color: rgba(0, 0, 120, 0.3); + box-shadow: 0 0 0 4px rgba(0, 0, 120, 0.08); + } + + .search-help { + color: var(--muted); + font-size: 12px; + line-height: 1.55; + max-width: 70ch; + } + + .control-panel { + margin-top: 16px; + padding-top: 16px; + border-top: 1px solid var(--line); + display: grid; + gap: 10px; + } + + .bulk-toolbar { + display: flex; + align-items: center; + gap: 10px; + flex-wrap: wrap; + margin: 0; + justify-content: flex-end; + } + + .bulk-info { + color: var(--muted); + font-size: 13px; + font-weight: 700; + } + + .table-card { + padding: 0; + overflow: hidden; + border-radius: 26px; + box-shadow: 0 16px 48px rgba(18, 33, 56, 0.06), inset 0 1px 0 rgba(255,255,255,0.92); + } + + .table-controls { + padding: 14px 18px 14px; + border-bottom: 1px solid var(--line); + background: linear-gradient(180deg, rgba(255,255,255,0.98), rgba(244,248,255,0.96)); + } + + .table-controls-grid { + display: grid; + grid-template-columns: minmax(0, 1fr) auto; + gap: 14px; + align-items: stretch; + } + + .control-stack { + display: grid; + gap: 12px; + padding: 14px; + border: 1px solid rgba(217, 227, 238, 0.95); + border-radius: 18px; + background: linear-gradient(180deg, rgba(255,255,255,0.98), rgba(249,251,255,0.95)); + box-shadow: inset 0 1px 0 rgba(255,255,255,0.9); + } + + .search-card { + padding: 18px; + background: rgba(255,255,255,0.92); + border: 1px solid rgba(217, 227, 238, 0.94); + border-radius: 22px; + box-shadow: inset 0 1px 0 rgba(255,255,255,0.92); + display: grid; + gap: 14px; + } + + .table-head { + display: flex; + justify-content: space-between; + gap: 14px; + align-items: center; + flex-wrap: wrap; + padding: 16px 18px 14px; + border-bottom: 1px solid var(--line); + background: linear-gradient(180deg, rgba(255,255,255,0.98), rgba(245,249,255,0.96)); + } + + .table-head h2 { + margin: 0; + font-size: 20px; + color: #18345f; + letter-spacing: -0.02em; + } + + .table-head p { + margin: 5px 0 0; + color: var(--muted); + font-size: 13px; + } + + .table-head-meta { + display: inline-flex; + align-items: center; + gap: 8px; + padding: 10px 14px; + border-radius: 999px; + border: 1px solid rgba(0, 0, 120, 0.10); + background: rgba(0, 0, 120, 0.05); + color: var(--brand-blue); + font-size: 12px; + font-weight: 800; + white-space: nowrap; + } + + .table-wrap { + overflow-x: auto; + padding: 0 18px 18px; + background: linear-gradient(180deg, rgba(249,251,255,0.72), rgba(255,255,255,0)); + } + + table { + width: 100%; + border-collapse: separate; + border-spacing: 0 12px; + font-size: 14px; + min-width: 980px; + } + + thead th { + position: sticky; + top: 0; + z-index: 2; + background: rgba(245,248,252,0.94); + color: #5b6b7f; + text-align: left; + font-size: 11px; + letter-spacing: 0.05em; + text-transform: uppercase; + font-weight: 800; + padding: 12px 14px; + border-top: 1px solid var(--line); + border-bottom: 1px solid var(--line); + } + + thead th:first-child { + border-left: 1px solid var(--line); + border-radius: 14px 0 0 14px; + } + + thead th:last-child { + border-right: 1px solid var(--line); + border-radius: 0 14px 14px 0; + } + + tbody td { + background: rgba(255,255,255,0.98); + border-top: 1px solid var(--line); + border-bottom: 1px solid var(--line); + padding: 15px 14px; + vertical-align: middle; + box-shadow: 0 8px 22px rgba(26, 51, 89, 0.035); + } + + tbody td:first-child { + border-left: 1px solid var(--line); + border-radius: 18px 0 0 18px; + } + + tbody td:last-child { + border-right: 1px solid var(--line); + border-radius: 0 18px 18px 0; + } + + tbody tr:hover td { + background: #fbfdff; + border-color: #cad7e8; + box-shadow: 0 12px 26px rgba(26, 51, 89, 0.06); + } + + .select-col { + width: 42px; + text-align: center; + } + + th.select-col, + td.select-col { + vertical-align: middle; + } + + td.actions-cell { + vertical-align: middle; + } + + td.intro-panel { + vertical-align: top; + } + + .select-col input[type="checkbox"] { + display: block; + margin: 0 auto; + } + + .kind-pill, + .status-badge, + .doc-link { + display: inline-flex; + align-items: center; + justify-content: center; + gap: 6px; + min-height: 38px; + padding: 10px 14px; + border-radius: 14px; + font-weight: 800; + font-size: 12px; + line-height: 1; + letter-spacing: 0.01em; + text-decoration: none; + white-space: nowrap; + 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); + } + + .kind-pill { + color: #294465; + } + + .kind-pill.onboarding { + background: linear-gradient(180deg, rgba(245,247,255,0.98), rgba(236,241,255,0.98)); + border-color: rgba(0, 0, 120, 0.12); + color: var(--brand-blue); + } + + .kind-pill.offboarding { + background: linear-gradient(180deg, rgba(255,247,247,0.98), rgba(255,240,240,0.98)); + border-color: rgba(163, 32, 32, 0.14); + color: var(--brand-red); + } + + .name-cell { + display: grid; + gap: 4px; + } + + .person-name { + font-weight: 800; + color: var(--ink); + letter-spacing: -0.01em; + } + + .person-meta { + color: var(--muted); + font-size: 12px; + } + + .mail-link { + color: #294465; + text-decoration: none; + font-weight: 600; + word-break: break-word; + } + + .mail-link:hover { text-decoration: underline; } + + .status-badge.ok { + background: linear-gradient(180deg, #f3fcf6, #e8f8ee); + color: var(--ok-ink); + border-color: #cdeed8; + } + + .status-badge.pending { + background: linear-gradient(180deg, #fffaf0, #fff3dc); + color: var(--warn-ink); + border-color: #f3dfb5; + } + + .doc-link { + color: var(--brand-blue); + background: linear-gradient(180deg, rgba(255,255,255,0.99), rgba(244,248,255,0.98)); + border-color: rgba(0, 0, 120, 0.12); + } + + .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); + } + + .actions-cell { white-space: normal; } + .inline-form { display: inline; margin: 0; } + .inline-delete { display: inline-flex; align-items: center; } + .inline-delete .btn { min-height: 38px; display: inline-flex; align-items: center; justify-content: center; } + .intro-panel { min-width: 260px; } + + details { + border: 1px solid var(--line); + border-radius: 16px; + background: linear-gradient(180deg, #ffffff, #f8fbff); + overflow: hidden; + } + + details[open] { + border-color: #cad7e8; + box-shadow: inset 0 1px 0 rgba(255,255,255,0.95); + } + + .intro-toggle { + cursor: pointer; + list-style: none; + display: flex; + align-items: center; + justify-content: space-between; + gap: 10px; + padding: 12px 14px; + font-weight: 800; + color: #18345f; + background: rgba(255,255,255,0.65); + } + + .intro-toggle::after { + content: "+"; + font-size: 18px; + line-height: 1; + color: var(--brand-blue); + } + + details[open] .intro-toggle::after { content: "−"; } + .intro-toggle::-webkit-details-marker { display: none; } + + .intro-menu { + padding: 0 14px 14px; + display: grid; + gap: 10px; + } + + .intro-group { + border-top: 1px solid var(--line); + padding-top: 10px; + } + + .intro-group:first-child { + border-top: 0; + padding-top: 0; + } + + .intro-group-title { + font-size: 11px; + font-weight: 800; + color: var(--muted); + text-transform: uppercase; + letter-spacing: 0.05em; + margin-bottom: 7px; + } + + .intro-actions { + display: flex; + gap: 6px; + flex-wrap: wrap; + } + + .intro-meta { + padding: 0 14px 14px; + color: var(--muted); + font-size: 12px; + } + + .flash { + margin: 0 24px 14px; + padding: 12px 14px; + border-radius: 16px; + border: 1px solid rgba(217, 227, 238, 0.9); + background: rgba(248, 251, 255, 0.95); + } + + .flash.success { border-color: #bfe6c9; background: #edf9f1; color: #116634; } + .flash.warning { border-color: #f5d8a8; background: #fff8ea; color: #8a5a00; } + .flash.error { border-color: #f4c7c7; background: #fff1f1; color: #8e1e1e; } + + .empty-state { + text-align: center; + color: var(--muted); + padding: 26px 14px; + } + + .footer-bar { + padding: 0 24px 24px; + display: flex; + justify-content: space-between; + gap: 12px; + align-items: center; + flex-wrap: wrap; + } + + .footer-note { + color: var(--muted); + font-size: 13px; + } + + @media (max-width: 1180px) { + .hero-grid, + .metrics-grid, + .top-content-grid, + .table-controls-grid { + grid-template-columns: 1fr; + } + } + + @media (max-width: 760px) { + body { padding: 14px; } + .topbar, + .hero, + .metrics-grid, + .content-grid, + .footer-bar { padding-left: 16px; padding-right: 16px; } + .topbar { flex-direction: column; } + .quick-actions { justify-content: flex-start; } + .table-wrap { padding-left: 12px; padding-right: 12px; } + } diff --git a/backend/workflows/static/workflows/css/success_pages.css b/backend/workflows/static/workflows/css/success_pages.css new file mode 100644 index 0000000..bbb1e02 --- /dev/null +++ b/backend/workflows/static/workflows/css/success_pages.css @@ -0,0 +1,6 @@ +body { margin: 0; font-family: Arial, sans-serif; background: #f4f8ff; color: #1b2b43; padding: 24px; } +.shell { background: #fff; border: 1px solid #d7e0ea; border-radius: 16px; padding: 20px; box-shadow: 0 16px 36px rgba(18,34,56,0.10); } +h1 { margin: 0 0 10px; color: #000078; } +p { margin: 0 0 10px; } +code { background: #f4f6fa; padding: 2px 6px; border-radius: 6px; } +.actions { display:flex; gap:8px; flex-wrap:wrap; margin-top: 14px; } diff --git a/backend/workflows/static/workflows/js/offboarding_form.js b/backend/workflows/static/workflows/js/offboarding_form.js new file mode 100644 index 0000000..c7dec47 --- /dev/null +++ b/backend/workflows/static/workflows/js/offboarding_form.js @@ -0,0 +1,51 @@ +(function () { + function byName(name) { + return document.querySelector('[name="' + name + '"]'); + } + + function slugifyForEmail(value) { + const map = { 'ä': 'ae', 'ö': 'oe', 'ü': 'ue', 'ß': 'ss' }; + const lower = (value || '').toLowerCase(); + const mapped = lower.replace(/[äöüß]/g, function (m) { return map[m] || m; }); + return mapped + .normalize('NFD') + .replace(/[\u0300-\u036f]/g, '') + .replace(/[^a-z0-9]+/g, '.') + .replace(/^\.+|\.+$/g, '') + .replace(/\.{2,}/g, '.'); + } + + function extractLastName(fullName) { + const parts = (fullName || '').trim().split(/\s+/).filter(Boolean); + return parts.length ? parts[parts.length - 1] : ''; + } + + const fullName = byName('full_name'); + const workEmail = byName('work_email'); + if (!fullName || !workEmail) return; + + let lastSuggested = ''; + let userEditedEmail = false; + + function suggestEmail() { + const lastName = extractLastName(fullName.value); + const slug = slugifyForEmail(lastName); + if (!slug) return; + const suggestion = slug + '@tub.co'; + const current = (workEmail.value || '').trim(); + if (!userEditedEmail || current === '' || current === lastSuggested) { + workEmail.value = suggestion; + lastSuggested = suggestion; + } + } + + workEmail.addEventListener('input', function () { + const current = (workEmail.value || '').trim(); + userEditedEmail = current !== '' && current !== lastSuggested; + }); + + fullName.addEventListener('input', suggestEmail); + fullName.addEventListener('change', suggestEmail); + fullName.addEventListener('blur', suggestEmail); + suggestEmail(); + }()); diff --git a/backend/workflows/static/workflows/js/onboarding_form.js b/backend/workflows/static/workflows/js/onboarding_form.js new file mode 100644 index 0000000..8ae6960 --- /dev/null +++ b/backend/workflows/static/workflows/js/onboarding_form.js @@ -0,0 +1,253 @@ +(function () { + const pages = Array.from(document.querySelectorAll('.page')); + const navItems = Array.from(document.querySelectorAll('.step-item')); + const btnPrev = document.getElementById('btn-prev'); + const btnNext = document.getElementById('btn-next'); + const btnSubmit = document.getElementById('btn-submit'); + const form = document.getElementById('onboarding-form'); + let current = 0; + form.setAttribute('novalidate', 'novalidate'); + + function byName(name) { return document.querySelector('[name="' + name + '"]'); } + function toggle(id, state) { + const el = document.getElementById(id); + if (!el) return; + el.classList.toggle('hidden', !state); + } + + function syncConditionals() { + const orderCards = byName('order_business_cards'); + toggle('business-card-box', orderCards && orderCards.checked); + + const employmentType = byName('employment_type'); + toggle('employment-end-box', employmentType && employmentType.value === 'befristet'); + + const groupMailbox = byName('group_mailboxes_required_choice'); + toggle('group-mailboxes-box', groupMailbox && groupMailbox.value === 'ja'); + + const extraHardware = byName('additional_hardware_needed_choice'); + toggle('extra-hardware-box', extraHardware && extraHardware.value === 'ja'); + + const extraSoftware = byName('additional_software_needed_choice'); + toggle('extra-software-box', extraSoftware && extraSoftware.value === 'ja'); + + const extraAccess = byName('additional_access_needed_choice'); + toggle('extra-access-box', extraAccess && extraAccess.value === 'ja'); + + const successor = byName('successor_required_choice'); + const showSuccessor = successor && successor.value === 'ja'; + toggle('successor-box', showSuccessor); + + const inheritPhone = byName('inherit_phone_number_choice'); + const hidePhone = showSuccessor && inheritPhone && inheritPhone.value === 'ja'; + toggle('phone-box', !hidePhone); + + // Hidden conditional groups must not block submit with invisible required fields. + document.querySelectorAll('.field-group').forEach(function (group) { + const hidden = group.classList.contains('hidden'); + group.querySelectorAll('input, select, textarea').forEach(function (el) { + if (el.type === 'hidden' || el.disabled) return; + if (hidden) { + if (el.required) { + el.dataset.requiredOriginal = '1'; + el.required = false; + } + } else if (el.dataset.requiredOriginal === '1') { + el.required = true; + } + }); + }); + } + + function slugifyForEmail(value) { + const map = { 'ä': 'ae', 'ö': 'oe', 'ü': 'ue', 'ß': 'ss' }; + const lower = (value || '').toLowerCase(); + const mapped = lower.replace(/[äöüß]/g, function (m) { return map[m] || m; }); + return mapped + .normalize('NFD') + .replace(/[\u0300-\u036f]/g, '') + .replace(/[^a-z0-9]+/g, '.') + .replace(/^\.+|\.+$/g, '') + .replace(/\.{2,}/g, '.'); + } + + function setupWorkEmailAutofill() { + const lastName = byName('last_name'); + const workEmail = byName('work_email'); + if (!lastName || !workEmail) return; + + let lastSuggested = ''; + let userEditedEmail = !!(workEmail.value && workEmail.value.trim()); + + function suggestEmail() { + const slug = slugifyForEmail(lastName.value); + if (!slug) return; + const suggestion = slug + '@tub.co'; + if (!userEditedEmail || workEmail.value === '' || workEmail.value === lastSuggested) { + workEmail.value = suggestion; + lastSuggested = suggestion; + } + } + + workEmail.addEventListener('input', function () { + const current = (workEmail.value || '').trim(); + userEditedEmail = current !== '' && current !== lastSuggested; + }); + + lastName.addEventListener('input', suggestEmail); + suggestEmail(); + } + + function setupBusinessCardAutofill() { + const checkbox = byName('order_business_cards'); + const firstName = byName('first_name'); + const lastName = byName('last_name'); + const jobTitle = byName('job_title'); + const workEmail = byName('work_email'); + const cardName = byName('business_card_name'); + const cardTitle = byName('business_card_title'); + const cardEmail = byName('business_card_email'); + if (!checkbox || !cardName || !cardTitle || !cardEmail) return; + const cardBox = document.getElementById('business-card-box'); + + function suggestionName() { + const first = (firstName && firstName.value || '').trim(); + const last = (lastName && lastName.value || '').trim(); + return [first, last].filter(Boolean).join(' ').trim(); + } + + function setField(field, value, force) { + if (!field || !value) return; + const current = (field.value || '').trim(); + const autoMarked = field.dataset.autofilled === '1'; + if (force || !current || autoMarked) { + field.value = value; + field.dataset.autofilled = '1'; + } + } + + function applyDefaults(force) { + if (!checkbox.checked) return; + const name = suggestionName(); + const title = (jobTitle && jobTitle.value || '').trim(); + const email = (workEmail && workEmail.value || '').trim(); + setField(cardName, name, force); + setField(cardTitle, title, force); + setField(cardEmail, email, force); + } + + [cardName, cardTitle, cardEmail].forEach(function (field) { + field.addEventListener('input', function () { + field.dataset.autofilled = '0'; + }); + }); + + if (cardBox) { + const grid = cardBox.querySelector('.grid-2'); + if (grid && !document.getElementById('business-card-autofill-btn')) { + const actionWrap = document.createElement('div'); + actionWrap.className = 'field field-full'; + const btn = document.createElement('button'); + btn.type = 'button'; + btn.id = 'business-card-autofill-btn'; + btn.className = 'btn btn-secondary'; + btn.textContent = 'Visitenkarten-Felder automatisch ausfüllen'; + btn.addEventListener('click', function () { + applyDefaults(true); + }); + actionWrap.appendChild(btn); + grid.insertBefore(actionWrap, grid.firstChild); + } + } + + // Manual-only behavior: fill only when button is clicked. + } + + function setupChecklistToggles() { + document.querySelectorAll('[data-checklist-toggle]').forEach(function (button) { + const panel = button.closest('.itsetup-checklist-panel'); + if (!panel) return; + + const getCheckboxes = function () { + return Array.from(panel.querySelectorAll('input[type="checkbox"]')); + }; + + const refreshButtonLabel = function () { + const checkboxes = getCheckboxes(); + if (!checkboxes.length) return; + const allChecked = checkboxes.every(function (box) { return box.checked; }); + button.textContent = allChecked ? (button.dataset.labelClear || 'Auswahl aufheben') : (button.dataset.labelSelect || 'Alle auswählen'); + }; + + button.addEventListener('click', function () { + const checkboxes = getCheckboxes(); + if (!checkboxes.length) return; + const shouldCheck = checkboxes.some(function (box) { return !box.checked; }); + checkboxes.forEach(function (box) { + box.checked = shouldCheck; + box.dispatchEvent(new Event('change', { bubbles: true })); + }); + refreshButtonLabel(); + }); + + getCheckboxes().forEach(function (box) { + box.addEventListener('change', refreshButtonLabel); + }); + + refreshButtonLabel(); + }); + } + + function setupChecklistColumns() { + document.querySelectorAll('.itsetup-checklist-body > [id^="id_"]').forEach(function (container) { + const itemCount = container.querySelectorAll(':scope > div').length; + container.classList.toggle('cols-3', itemCount > 8); + }); + } + + function updateStep() { + pages.forEach((p, i) => p.classList.toggle('active', i === current)); + navItems.forEach((n, i) => n.classList.toggle('active', i === current)); + btnPrev.disabled = current === 0; + const last = current === pages.length - 1; + btnNext.classList.toggle('hidden', last); + btnSubmit.classList.toggle('hidden', !last); + } + + function jumpToFirstErrorPage() { + const firstError = document.querySelector('.errorlist'); + if (!firstError) return; + const page = firstError.closest('.page'); + if (!page) return; + const step = Number(page.getAttribute('data-step') || '1') - 1; + current = Math.max(0, step); + } + + document.addEventListener('change', syncConditionals); + navItems.forEach((n, idx) => { + n.addEventListener('click', function () { current = idx; updateStep(); }); + n.addEventListener('keydown', function (e) { + if (e.key === 'Enter' || e.key === ' ') { + e.preventDefault(); + current = idx; + updateStep(); + } + }); + }); + btnPrev.addEventListener('click', function () { if (current > 0) { current -= 1; updateStep(); } }); + btnNext.addEventListener('click', function () { if (current < pages.length - 1) { current += 1; updateStep(); } }); + btnSubmit.addEventListener('click', function () { + syncConditionals(); + btnSubmit.disabled = true; + btnSubmit.textContent = btnSubmit.dataset.loadingLabel || 'Wird gesendet...'; + form.submit(); + }); + + syncConditionals(); + setupWorkEmailAutofill(); + setupBusinessCardAutofill(); + setupChecklistToggles(); + setupChecklistColumns(); + jumpToFirstErrorPage(); + updateStep(); + })(); diff --git a/backend/workflows/static/workflows/js/requests_dashboard.js b/backend/workflows/static/workflows/js/requests_dashboard.js new file mode 100644 index 0000000..4c13e85 --- /dev/null +++ b/backend/workflows/static/workflows/js/requests_dashboard.js @@ -0,0 +1,20 @@ +(function () { + const selectAll = document.getElementById('select-all'); + const rowChecks = Array.from(document.querySelectorAll('.row-select')); + const selectedCount = document.getElementById('selected-count'); + if (!selectAll || !selectedCount || !rowChecks.length) return; + + function updateCount() { + const checked = rowChecks.filter((c) => c.checked).length; + selectedCount.textContent = String(checked); + selectAll.checked = checked > 0 && checked === rowChecks.length; + selectAll.indeterminate = checked > 0 && checked < rowChecks.length; + } + + selectAll.addEventListener('change', function () { + rowChecks.forEach((c) => { c.checked = selectAll.checked; }); + updateCount(); + }); + rowChecks.forEach((c) => c.addEventListener('change', updateCount)); + updateCount(); + })(); diff --git a/backend/workflows/static/workflows/js/welcome_emails.js b/backend/workflows/static/workflows/js/welcome_emails.js new file mode 100644 index 0000000..456d734 --- /dev/null +++ b/backend/workflows/static/workflows/js/welcome_emails.js @@ -0,0 +1,52 @@ +(function () { + const selectAll = document.getElementById('select-all-welcome'); + const rowChecks = Array.from(document.querySelectorAll('.welcome-select')); + const selectedCount = document.getElementById('selected-count'); + const selectedIds = document.getElementById('selected_ids'); + const bulkForm = document.getElementById('welcome-bulk-form'); + const bulkAction = document.getElementById('bulk_action'); + + function currentSelected() { + return rowChecks.filter((c) => c.checked).map((c) => c.value); + } + + function syncState() { + const ids = currentSelected(); + selectedCount.textContent = String(ids.length); + selectedIds.value = ids.join(','); + if (!rowChecks.length) { + selectAll.checked = false; + selectAll.indeterminate = false; + return; + } + selectAll.checked = ids.length > 0 && ids.length === rowChecks.length; + selectAll.indeterminate = ids.length > 0 && ids.length < rowChecks.length; + } + + selectAll.addEventListener('change', function () { + rowChecks.forEach((c) => { c.checked = selectAll.checked; }); + syncState(); + }); + + rowChecks.forEach((c) => c.addEventListener('change', syncState)); + bulkForm.addEventListener('submit', syncState); + + window.confirmBulkAction = function () { + syncState(); + const count = currentSelected().length; + if (!count) { + alert(bulkForm.dataset.alertEmpty || 'Bitte mindestens einen Welcome-Eintrag auswählen.'); + return false; + } + const action = bulkAction.value; + if (action === 'delete') { + return confirm(bulkForm.dataset.confirmDelete || 'Ausgewählte Welcome-Einträge wirklich löschen?'); + } + 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?'); + }; + + syncState(); + })(); diff --git a/backend/workflows/templates/registration/login.html b/backend/workflows/templates/registration/login.html index ac58f7b..4ba811d 100644 --- a/backend/workflows/templates/registration/login.html +++ b/backend/workflows/templates/registration/login.html @@ -3,26 +3,20 @@ {% block title %}{% trans "Anmeldung" %}{% endblock %} -{% block extra_head %} - -{% endblock %} + {% block shell_header %} -{% include 'workflows/includes/app_header.html' %} +{% include 'workflows/includes/app_header.html' with header_inside_shell=1 %} +{% endblock %} + +{% block extra_css %} + {% endblock %} {% block shell_body %} -

{% trans "Anmeldung" %}

+ {% endblock %} -{% endblock %} - diff --git a/backend/workflows/templates/workflows/developer_handbook.html b/backend/workflows/templates/workflows/developer_handbook.html index f662366..6e9bc0f 100644 --- a/backend/workflows/templates/workflows/developer_handbook.html +++ b/backend/workflows/templates/workflows/developer_handbook.html @@ -3,25 +3,10 @@ {% block title %}Developer Handbook{% endblock %} -{% block extra_head %} - + + +{% block extra_css %} + {% endblock %} {% block shell_body %} diff --git a/backend/workflows/templates/workflows/handbook.html b/backend/workflows/templates/workflows/handbook.html index 69b28b0..decb080 100644 --- a/backend/workflows/templates/workflows/handbook.html +++ b/backend/workflows/templates/workflows/handbook.html @@ -3,24 +3,10 @@ {% block title %}{% trans "Handbook" %}{% endblock %} -{% block extra_head %} - + + +{% block extra_css %} + {% endblock %} {% block shell_body %} diff --git a/backend/workflows/templates/workflows/home.html b/backend/workflows/templates/workflows/home.html index dbedf55..fd20aab 100644 --- a/backend/workflows/templates/workflows/home.html +++ b/backend/workflows/templates/workflows/home.html @@ -3,441 +3,10 @@ {% block title %}{% trans "TUBCO Onboarding & Offboarding Portal" %}{% endblock %} -{% block extra_head %} - +{% block extra_css %} + {% endblock %} {% block shell_body %} diff --git a/backend/workflows/templates/workflows/integrations_setup.html b/backend/workflows/templates/workflows/integrations_setup.html index a7ce8e2..1d44d55 100644 --- a/backend/workflows/templates/workflows/integrations_setup.html +++ b/backend/workflows/templates/workflows/integrations_setup.html @@ -3,70 +3,10 @@ {% block title %}{% trans "Integrationen Setup" %}{% endblock %} -{% block extra_head %} - + + +{% block extra_css %} + {% endblock %} {% block shell_body %} diff --git a/backend/workflows/templates/workflows/intro_builder.html b/backend/workflows/templates/workflows/intro_builder.html index d9bc8fb..2131683 100644 --- a/backend/workflows/templates/workflows/intro_builder.html +++ b/backend/workflows/templates/workflows/intro_builder.html @@ -3,32 +3,10 @@ {% block title %}{% trans "Einweisungs-Builder" %}{% endblock %} -{% block extra_head %} - + + +{% block extra_css %} + {% endblock %} {% block shell_body %} diff --git a/backend/workflows/templates/workflows/offboarding_form.html b/backend/workflows/templates/workflows/offboarding_form.html index ffac2ed..29d83a2 100644 --- a/backend/workflows/templates/workflows/offboarding_form.html +++ b/backend/workflows/templates/workflows/offboarding_form.html @@ -69,57 +69,6 @@ {% endblock %} {% block extra_scripts %} - + {% endblock %} diff --git a/backend/workflows/templates/workflows/offboarding_success.html b/backend/workflows/templates/workflows/offboarding_success.html index a9ca8c2..cbffd51 100644 --- a/backend/workflows/templates/workflows/offboarding_success.html +++ b/backend/workflows/templates/workflows/offboarding_success.html @@ -3,21 +3,16 @@ {% block title %}{% trans "Offboarding gespeichert" %}{% endblock %} -{% block extra_head %} - -{% endblock %} + {% block shell_header %} {% include 'workflows/includes/app_header.html' with header_show_home=1 %} {% endblock %} +{% block extra_css %} + +{% endblock %} + {% block shell_body %}

{% trans "Offboarding gespeichert" %}

{% trans "Vorgangs-ID:" %} {{ obj.id }}

diff --git a/backend/workflows/templates/workflows/onboarding_form.html b/backend/workflows/templates/workflows/onboarding_form.html index 92ec418..b6d7edd 100644 --- a/backend/workflows/templates/workflows/onboarding_form.html +++ b/backend/workflows/templates/workflows/onboarding_form.html @@ -154,7 +154,7 @@
- +
@@ -163,259 +163,6 @@ {% endblock %} {% block extra_scripts %} - + {% endblock %} diff --git a/backend/workflows/templates/workflows/onboarding_intro_session.html b/backend/workflows/templates/workflows/onboarding_intro_session.html index 0564503..b2388af 100644 --- a/backend/workflows/templates/workflows/onboarding_intro_session.html +++ b/backend/workflows/templates/workflows/onboarding_intro_session.html @@ -3,55 +3,10 @@ {% block title %}{% trans "Einweisung durchführen" %}{% endblock %} -{% block extra_head %} - + + +{% block extra_css %} + {% endblock %} {% block shell_body %} diff --git a/backend/workflows/templates/workflows/onboarding_success.html b/backend/workflows/templates/workflows/onboarding_success.html index 15cee6a..cd01905 100644 --- a/backend/workflows/templates/workflows/onboarding_success.html +++ b/backend/workflows/templates/workflows/onboarding_success.html @@ -3,21 +3,16 @@ {% block title %}{% trans "Onboarding gespeichert" %}{% endblock %} -{% block extra_head %} - -{% endblock %} + {% block shell_header %} {% include 'workflows/includes/app_header.html' with header_show_home=1 %} {% endblock %} +{% block extra_css %} + +{% endblock %} + {% block shell_body %}

{% trans "Anfrage erfolgreich gespeichert" %}

{% trans "Vorgangs-ID:" %} {{ obj.id }}

diff --git a/backend/workflows/templates/workflows/project_wiki.html b/backend/workflows/templates/workflows/project_wiki.html index 20cff62..5b8f6cf 100644 --- a/backend/workflows/templates/workflows/project_wiki.html +++ b/backend/workflows/templates/workflows/project_wiki.html @@ -3,24 +3,10 @@ {% block title %}Project Wiki{% endblock %} -{% block extra_head %} - + + +{% block extra_css %} + {% endblock %} {% block shell_body %} diff --git a/backend/workflows/templates/workflows/release_checklist.html b/backend/workflows/templates/workflows/release_checklist.html index 89f5239..55efb5e 100644 --- a/backend/workflows/templates/workflows/release_checklist.html +++ b/backend/workflows/templates/workflows/release_checklist.html @@ -3,29 +3,10 @@ {% block title %}{% trans "Release Checklist" %}{% endblock %} -{% block extra_head %} - + + +{% block extra_css %} + {% endblock %} {% block shell_body %} diff --git a/backend/workflows/templates/workflows/requests_dashboard.html b/backend/workflows/templates/workflows/requests_dashboard.html index c0d2267..cd9ca92 100644 --- a/backend/workflows/templates/workflows/requests_dashboard.html +++ b/backend/workflows/templates/workflows/requests_dashboard.html @@ -3,872 +3,10 @@ {% block title %}{% trans "Anfragen Dashboard" %}{% endblock %} -{% block extra_head %} - +{% block extra_css %} + {% endblock %} {% block shell_body %} @@ -997,10 +135,9 @@
{% trans "Datensätze können direkt in der Tabelle gefiltert, geöffnet, geprüft oder gelöscht werden." %}
-{% endblock %} - -{% block extra_scripts %} -{% if request.user.is_staff %}
+
+ {% if request.user.is_staff %} +
{% csrf_token %}
@@ -1116,30 +253,8 @@ {% trans "Zur Startseite" %}
-
- {% if request.user.is_staff %} - - {% endif %} {% endblock %} +{% block extra_scripts %} + +{% endblock %} diff --git a/backend/workflows/templates/workflows/welcome_emails.html b/backend/workflows/templates/workflows/welcome_emails.html index b013c79..d3ff3c7 100644 --- a/backend/workflows/templates/workflows/welcome_emails.html +++ b/backend/workflows/templates/workflows/welcome_emails.html @@ -3,41 +3,10 @@ {% block title %}{% trans "Welcome E-Mails" %}{% endblock %} -{% block extra_head %} - + + +{% block extra_css %} + {% endblock %} {% block shell_body %} @@ -93,7 +62,7 @@ -
+ {% csrf_token %}