snapshot: preserve shell cleanup and login/dashboard fixes

This commit is contained in:
Md Bayazid Bostame
2026-03-24 18:19:12 +01:00
parent a4f5b9ee8f
commit 719d122c26
27 changed files with 1976 additions and 1976 deletions

View File

@@ -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; } }

View File

@@ -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; } }

View File

@@ -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; }
}

View File

@@ -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;
}
}

View File

@@ -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; } }

View File

@@ -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; } }

View File

@@ -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; }
}

View File

@@ -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; }

View File

@@ -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();
}());

View File

@@ -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();
})();

View File

@@ -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();
})();

View File

@@ -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();
})();

View File

@@ -3,26 +3,20 @@
{% block title %}{% trans "Anmeldung" %}{% endblock %}
{% block extra_head %}
<style>
body { margin: 0; font-family: Arial, sans-serif; min-height: 100vh; background: linear-gradient(160deg, #eef6ff, #fff3f3); padding: 24px; }
.card { width: min(420px, calc(100% - 28px)); margin: 0 auto; background: #fff; border: 1px solid #d9e3f0; border-radius: 14px; padding: 20px; box-shadow: 0 12px 30px rgba(28, 45, 79, 0.12); }
h1 { margin: 0 0 8px; font-size: 24px; }
p { margin: 0 0 14px; color: #607086; }
.field { margin-bottom: 12px; }
label { display: block; font-weight: 600; margin-bottom: 6px; }
input { width: 100%; padding: 10px; box-sizing: border-box; border: 1px solid #cbd5e1; border-radius: 8px; }
.btn { width: 100%; }
.errorlist { color: #b91c1c; margin: 6px 0; }
</style>
{% 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 %}
<link rel="stylesheet" href="{% static 'workflows/css/login.css' %}" />
{% endblock %}
{% block shell_body %}
<div class="card"><h1>{% trans "Anmeldung" %}</h1>
<section class="login-shell-body">
<div class="login-card">
<h1>{% trans "Anmeldung" %}</h1>
<p>{% trans "Bitte melden Sie sich mit Ihrem Benutzerkonto an." %}</p>
<form method="post" action="/accounts/login/">
@@ -35,6 +29,5 @@
<button class="btn btn-primary" type="submit">{% trans "Anmelden" %}</button>
</form>
</div>
</section>
{% endblock %}
{% endblock %}

View File

@@ -3,25 +3,10 @@
{% block title %}Developer Handbook{% endblock %}
{% block extra_head %}
<style>
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; }
</style>
{% block extra_css %}
<link rel="stylesheet" href="{% static 'workflows/css/docs_pages.css' %}" />
{% endblock %}
{% block shell_body %}

View File

@@ -3,24 +3,10 @@
{% block title %}{% trans "Handbook" %}{% endblock %}
{% block extra_head %}
<style>
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 18px; color: #5f6f85; max-width: 760px; }
.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; }
h2 { margin: 0 0 8px; color: #113a74; }
p { margin: 0 0 14px; color: #5f6f85; }
ul { margin: 0 0 14px 18px; color: #334155; }
li { margin: 4px 0; }
.actions { display: flex; gap: 8px; flex-wrap: wrap; }
@media (max-width: 760px) { .grid { grid-template-columns: 1fr; } }
</style>
{% block extra_css %}
<link rel="stylesheet" href="{% static 'workflows/css/docs_pages.css' %}" />
{% endblock %}
{% block shell_body %}

View File

@@ -3,441 +3,10 @@
{% block title %}{% trans "TUBCO Onboarding & Offboarding Portal" %}{% endblock %}
{% block extra_head %}
<style>
: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; }
}
</style>
{% block extra_css %}
<link rel="stylesheet" href="{% static 'workflows/css/home.css' %}" />
{% endblock %}
{% block shell_body %}

View File

@@ -3,70 +3,10 @@
{% block title %}{% trans "Integrationen Setup" %}{% endblock %}
{% block extra_head %}
<style>
body { margin: 0; font-family: Arial, sans-serif; background: #f4f8ff; color: #0f172a; padding: 20px; }
.shell { max-width: 980px; margin: 0 auto; background: #fff; border: 1px solid #d8e3f0; border-radius: 14px; padding: 16px; }
.topbar { display: flex; justify-content: space-between; align-items: center; gap: 10px; flex-wrap: wrap; }
.brand-logo { width: 190px; max-width: 100%; height: auto; display: block; }
h1 { margin: 12px 0 6px; color: #000078; }
.sub { margin: 0 0 12px; color: #54657c; }
.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; }
.msg { border-radius: 10px; padding: 10px 12px; margin: 0 0 12px; border: 1px solid #d6e1ef; background: #f8fbff; color: #1f3a5f; }
.msg.error { border-color: #fecaca; background: #fff1f2; color: #991b1b; }
.card { border: 1px solid #d8e3f0; border-radius: 12px; background: #fbfdff; padding: 12px; }
.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 { width: 100%; box-sizing: border-box; border: 1px solid #cbd5e1; border-radius: 8px; padding: 8px 9px; }
select { width: 100%; box-sizing: border-box; border: 1px solid #cbd5e1; border-radius: 8px; padding: 8px 9px; background: #fff; }
.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; }
.actions { margin-top: 10px; display: flex; gap: 8px; flex-wrap: wrap; }
.hint { margin-top: 6px; color: #64748b; font-size: 12px; }
.template-block {
border: 1px solid #d8e3f0;
border-radius: 10px;
background: #fff;
padding: 10px;
margin-top: 10px;
}
.template-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;
}
.rule-title {
margin: 0 0 8px;
color: #23344f;
font-weight: 700;
font-size: 14px;
}
textarea {
width: 100%;
box-sizing: border-box;
border: 1px solid #cbd5e1;
border-radius: 8px;
padding: 8px 9px;
min-height: 120px;
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
font-size: 12px;
}
@media (max-width: 760px) { .grid { grid-template-columns: 1fr; } }
</style>
{% block extra_css %}
<link rel="stylesheet" href="{% static 'workflows/css/admin_tools.css' %}" />
{% endblock %}
{% block shell_body %}

View File

@@ -3,32 +3,10 @@
{% block title %}{% trans "Einweisungs-Builder" %}{% endblock %}
{% block extra_head %}
<style>
body { margin: 0; font-family: Arial, sans-serif; background: #f4f8ff; color: #1b2b43; padding: 20px; }
.shell { max-width: 1180px; margin: 0 auto; background: #fff; border: 1px solid #d7e0ea; border-radius: 14px; padding: 18px; }
.topbar { display: flex; justify-content: space-between; align-items: center; gap: 12px; flex-wrap: wrap; margin-bottom: 10px; }
.brand-logo { width: 190px; max-width: 100%; height: auto; display: block; }
h1 { margin: 0; color: #000078; font-size: 28px; }
.sub { margin: 8px 0 14px; color: #5f6f85; }
.flash { margin: 0 0 12px; padding: 10px; border-radius: 8px; border: 1px solid #dbe5f2; background: #f8fbff; }
.flash.error { border-color: #f4c7c7; background: #fff1f1; color: #8e1e1e; }
.flash.success { border-color: #bfe6c9; background: #edf9f1; color: #116634; }
.card { border: 1px solid #d7e0ea; border-radius: 12px; padding: 14px; background: #fcfdff; margin-bottom: 14px; }
.grid { display: grid; grid-template-columns: 1.1fr 1.4fr 1fr 1fr 1fr auto; gap: 10px; align-items: end; }
.field label { display: block; font-weight: 600; margin-bottom: 6px; }
.field input, .field select { width: 100%; min-height: 40px; padding: 8px 10px; border: 1px solid #cfd9e8; border-radius: 8px; box-sizing: border-box; }
.table-wrap { overflow-x: auto; }
table { width: 100%; border-collapse: collapse; }
th, td { border: 1px solid #e2e8f0; padding: 8px; text-align: left; vertical-align: top; }
th { background: #f6f8fb; color: #334155; }
.mini { color: #64748b; font-size: 12px; }
.hint { margin-top: 6px; color: #5f6f85; font-size: 13px; }
.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); }
.actions { white-space: nowrap; }
.toolbar { display: flex; justify-content: space-between; align-items: center; gap: 10px; margin-bottom: 10px; flex-wrap: wrap; }
</style>
{% block extra_css %}
<link rel="stylesheet" href="{% static 'workflows/css/admin_tools.css' %}" />
{% endblock %}
{% block shell_body %}

View File

@@ -69,57 +69,6 @@
{% endblock %}
{% block extra_scripts %}
<script>(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();
}());
</script>
<script src="{% static 'workflows/js/offboarding_form.js' %}"></script>
{% endblock %}

View File

@@ -3,21 +3,16 @@
{% block title %}{% trans "Offboarding gespeichert" %}{% endblock %}
{% block extra_head %}
<style>
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; }
</style>
{% endblock %}
{% block shell_header %}
{% include 'workflows/includes/app_header.html' with header_show_home=1 %}
{% endblock %}
{% block extra_css %}
<link rel="stylesheet" href="{% static 'workflows/css/success_pages.css' %}" />
{% endblock %}
{% block shell_body %}
<h1>{% trans "Offboarding gespeichert" %}</h1>
<p>{% trans "Vorgangs-ID:" %} <code>{{ obj.id }}</code></p>

View File

@@ -154,7 +154,7 @@
<div class="actions">
<button class="btn btn-secondary" type="button" id="btn-prev">{% trans "Zurück" %}</button>
<button class="btn btn-primary" type="button" id="btn-next">{% trans "Weiter" %}</button>
<button type="submit" id="btn-submit" class="btn btn-primary hidden">{% trans "Onboarding-Anfrage absenden" %}</button>
<button type="submit" id="btn-submit" class="btn btn-primary hidden" data-loading-label="{% trans 'Wird gesendet...' %}">{% trans "Onboarding-Anfrage absenden" %}</button>
</div>
</form>
</main>
@@ -163,259 +163,6 @@
{% endblock %}
{% block extra_scripts %}
<script>(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 = 'Wird gesendet...';
form.submit();
});
syncConditionals();
setupWorkEmailAutofill();
setupBusinessCardAutofill();
setupChecklistToggles();
setupChecklistColumns();
jumpToFirstErrorPage();
updateStep();
})();
</script>
<script src="{% static 'workflows/js/onboarding_form.js' %}"></script>
{% endblock %}

View File

@@ -3,55 +3,10 @@
{% block title %}{% trans "Einweisung durchführen" %}{% endblock %}
{% block extra_head %}
<style>
: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; } }
</style>
{% block extra_css %}
<link rel="stylesheet" href="{% static 'workflows/css/onboarding_intro_session.css' %}" />
{% endblock %}
{% block shell_body %}

View File

@@ -3,21 +3,16 @@
{% block title %}{% trans "Onboarding gespeichert" %}{% endblock %}
{% block extra_head %}
<style>
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; }
</style>
{% endblock %}
{% block shell_header %}
{% include 'workflows/includes/app_header.html' with header_show_home=1 %}
{% endblock %}
{% block extra_css %}
<link rel="stylesheet" href="{% static 'workflows/css/success_pages.css' %}" />
{% endblock %}
{% block shell_body %}
<h1>{% trans "Anfrage erfolgreich gespeichert" %}</h1>
<p>{% trans "Vorgangs-ID:" %} <code>{{ obj.id }}</code></p>

View File

@@ -3,24 +3,10 @@
{% block title %}Project Wiki{% endblock %}
{% block extra_head %}
<style>
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; }
.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; }
</style>
{% block extra_css %}
<link rel="stylesheet" href="{% static 'workflows/css/docs_pages.css' %}" />
{% endblock %}
{% block shell_body %}

View File

@@ -3,29 +3,10 @@
{% block title %}{% trans "Release Checklist" %}{% endblock %}
{% block extra_head %}
<style>
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; } }
</style>
{% block extra_css %}
<link rel="stylesheet" href="{% static 'workflows/css/release_checklist.css' %}" />
{% endblock %}
{% block shell_body %}

View File

@@ -3,872 +3,10 @@
{% block title %}{% trans "Anfragen Dashboard" %}{% endblock %}
{% block extra_head %}
<style>
: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; }
}
</style>
{% block extra_css %}
<link rel="stylesheet" href="{% static 'workflows/css/requests_dashboard.css' %}" />
{% endblock %}
{% block shell_body %}
@@ -997,10 +135,9 @@
</div>
</form>
<div class="search-help">{% trans "Datensätze können direkt in der Tabelle gefiltert, geöffnet, geprüft oder gelöscht werden." %}</div>
{% endblock %}
{% block extra_scripts %}
{% if request.user.is_staff %}<div class="control-stack">
</div>
{% if request.user.is_staff %}
<div class="control-stack">
<form method="post" action="/requests/" id="bulk-delete-form" onsubmit="return confirm('Ausgewählte Einträge wirklich löschen?');">
{% csrf_token %}
<div class="bulk-toolbar">
@@ -1116,30 +253,8 @@
<div class="footer-note">{% trans "TUBCO Onboarding & Offboarding Portal" %}</div>
<a class="btn btn-secondary" href="/">{% trans "Zur Startseite" %}</a>
</div>
</div>
{% if request.user.is_staff %}
<script>
(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();
})();
</script>
{% endif %}
{% endblock %}
{% block extra_scripts %}
<script src="{% static 'workflows/js/requests_dashboard.js' %}"></script>
{% endblock %}

View File

@@ -3,41 +3,10 @@
{% block title %}{% trans "Welcome E-Mails" %}{% endblock %}
{% block extra_head %}
<style>
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; }
.topbar { display: flex; justify-content: space-between; align-items: center; gap: 10px; flex-wrap: wrap; }
.brand-logo { width: 190px; max-width: 100%; height: auto; display: block; }
h1 { margin: 12px 0 6px; color: #000078; }
.sub { margin: 0 0 12px; color: #54657c; }
.msg { border-radius: 10px; padding: 10px 12px; margin: 0 0 12px; border: 1px solid #d6e1ef; background: #f8fbff; color: #1f3a5f; }
.msg.error { border-color: #fecaca; background: #fff1f2; color: #991b1b; }
.card { border: 1px solid #d8e3f0; border-radius: 12px; background: #fbfdff; padding: 12px; margin-bottom: 14px; }
.grid { display: grid; grid-template-columns: repeat(2, minmax(260px, 1fr)); gap: 10px; }
label { display: block; margin-bottom: 4px; font-size: 12px; color: #334155; font-weight: 700; }
input, textarea { width: 100%; box-sizing: border-box; border: 1px solid #cbd5e1; border-radius: 8px; padding: 8px 9px; }
textarea { min-height: 120px; font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 12px; }
.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 { 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; }
.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; }
.actions { margin-top: 10px; display: flex; gap: 8px; flex-wrap: wrap; }
.table-actions { display: flex; gap: 6px; flex-wrap: wrap; }
.hint { margin-top: 6px; color: #64748b; font-size: 12px; }
.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; }
</style>
{% block extra_css %}
<link rel="stylesheet" href="{% static 'workflows/css/admin_tools.css' %}" />
{% endblock %}
{% block shell_body %}
@@ -93,7 +62,7 @@
</div>
</form>
<form class="bulk-bar" id="welcome-bulk-form" method="post" action="/admin-tools/welcome-emails/bulk-action/" onsubmit="return confirmBulkAction();">
<form class="bulk-bar" id="welcome-bulk-form" method="post" action="/admin-tools/welcome-emails/bulk-action/" onsubmit="return confirmBulkAction();" data-alert-empty="{% trans 'Bitte mindestens einen Welcome-Eintrag auswählen.' %}" data-confirm-delete="{% trans 'Ausgewählte Welcome-Einträge wirklich löschen?' %}" data-confirm-pause="{% trans 'Ausgewählte Welcome-Einträge pausieren?' %}" data-confirm-send="{% trans 'Ausgewählte Welcome-Einträge sofort senden?' %}">
{% csrf_token %}
<label style="display:inline-flex; align-items:center; gap:6px; margin:0;">
<input type="checkbox" id="select-all-welcome" />
@@ -180,58 +149,6 @@
{% endblock %}
{% block extra_scripts %}
<script>(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('{% trans "Bitte mindestens einen Welcome-Eintrag auswählen." %}');
return false;
}
const action = bulkAction.value;
if (action === 'delete') {
return confirm('{% trans "Ausgewählte Welcome-Einträge wirklich löschen?" %}');
}
if (action === 'pause') {
return confirm('{% trans "Ausgewählte Welcome-Einträge pausieren?" %}');
}
return confirm('{% trans "Ausgewählte Welcome-Einträge sofort senden?" %}');
};
syncState();
})();
</script>
<script src="{% static 'workflows/js/welcome_emails.js' %}"></script>
{% endblock %}