web - refactor core modules, harden authorization, simplify endpoints

This commit is contained in:
2026-08-29 11:15:54 +02:00
parent e1109a07ef
commit 79d857d686
71 changed files with 8269 additions and 9119 deletions
@@ -0,0 +1,357 @@
.admin-users-shell {
padding: 18px;
background: linear-gradient(135deg, var(--gray-50) 0%, var(--theme-light, var(--nak-blue-light)) 100%);
min-height: 100%;
}
.admin-table-card {
overflow: hidden;
border: 1px solid rgba(120, 140, 160, 0.22);
background: rgba(255,255,255,0.86);
box-shadow: 0 20px 45px rgba(31, 38, 46, 0.10), 0 1px 0 rgba(255,255,255,0.9) inset;
}
.admin-list-header {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
padding: 16px 18px 12px 18px;
border-bottom: 1px solid rgba(120, 140, 160, 0.16);
background: linear-gradient(180deg, rgba(255,255,255,0.92), rgba(255,255,255,0.72));
}
.admin-list-title {
font-size: 1.05em;
font-weight: 800;
color: hsl(210, 20%, 15%);
}
.admin-list-hint {
font-size: 0.86em;
color: hsl(210, 15%, 46%);
}
table.admin-user-table {
display: table !important;
width: calc(100% - 24px);
margin: 8px 12px 14px 12px;
border-collapse: separate;
border-spacing: 0 8px;
padding: 0;
background: transparent;
overflow: visible;
}
.admin-user-table thead th {
position: sticky;
top: 0;
padding: 10px 14px;
color: hsl(210, 16%, 46%);
font-size: 0.78em;
font-weight: 800;
text-align: left;
letter-spacing: 0.04em;
text-transform: uppercase;
background: transparent !important;
border: none !important;
}
.admin-user-table tbody tr.admin-user-row {
position: relative;
cursor: pointer;
background: rgba(255, 255, 255, 0.92);
box-shadow: 0 6px 18px rgba(31, 38, 46, 0.06);
transition: transform 0.18s ease, box-shadow 0.18s ease, background 0.18s ease;
}
.admin-user-table tbody tr.admin-user-row:hover {
transform: translateY(-1px);
background: #FFFFFF;
box-shadow: 0 12px 26px rgba(31, 38, 46, 0.13);
}
.admin-user-table td {
padding: 12px 14px !important;
vertical-align: middle;
border: none !important;
background: transparent !important;
}
.admin-user-table tbody tr.admin-user-row td:first-child {
width: 82px;
min-width: 82px;
max-width: 82px;
}
.admin-profile-button {
display: block;
width: 58px;
height: 58px;
border-radius: 12px;
overflow: hidden;
border: 0;
padding: 0;
background: transparent;
cursor: pointer;
box-shadow: 0 8px 20px rgba(31, 38, 46, 0.18), 0 0 0 3px rgba(255,255,255,0.95);
}
.admin-profile-button img {
display: block;
width: 58px !important;
height: 58px !important;
object-fit: cover;
border-radius: 12px !important;
transition: transform 0.2s ease;
}
.admin-user-table tbody tr.admin-user-row:hover .admin-profile-button img {
transform: scale(1.08);
}
.admin-user-name {
display: inline-flex;
align-items: center;
gap: 8px;
font-size: 1em;
font-weight: 800;
color: hsl(210, 20%, 15%);
}
.bs-status-badge {
display: inline-flex;
align-items: center;
padding: 4px 10px;
font-size: 0.78em;
font-weight: 800;
}
.bs-status-ok {
color: hsl(145, 50%, 28%);
background: hsl(145, 50%, 93%);
border: 1px solid hsl(145, 50%, 82%);
}
.bs-status-warning {
color: hsl(35, 78%, 30%);
background: hsl(42, 90%, 93%);
border: 1px solid hsl(42, 90%, 78%);
}
.bs-status-overdue {
color: hsl(0, 65%, 38%);
background: hsl(0, 65%, 95%);
border: 1px solid hsl(0, 65%, 82%);
}
.bs-status-critical {
color: #FFFFFF;
background: hsl(0, 65%, 45%);
border: 1px solid hsl(0, 65%, 38%);
}
.bs-due-date {
font-size: 0.9em;
font-weight: 650;
color: hsl(210, 14%, 35%);
}
/* Modal */
.admin-modal-card {
position: relative;
width: min(720px, calc(100vw - 24px));
max-height: min(760px, calc(100vh - 24px));
overflow: auto;
padding: 0;
background: #FFFFFF;
box-shadow: 0 24px 70px rgba(0,0,0,0.36);
}
.admin-modal-hero {
position: relative;
display: grid;
grid-template-columns: 112px 1fr;
gap: 18px;
align-items: center;
padding: 24px;
color: #FFFFFF;
background:
radial-gradient(circle at 10% 0%, rgba(255,255,255,0.24), transparent 32%),
linear-gradient(135deg, hsl(25, 90%, 45%), hsl(25, 95%, 35%));
}
.admin-modal-profile-img {
width: 104px;
height: 104px;
object-fit: cover;
border-radius: 16px;
box-shadow: 0 12px 28px rgba(0,0,0,0.32), 0 0 0 4px rgba(255,255,255,0.18);
}
.admin-modal-title {
margin: 0 42px 6px 0;
font-size: 1.45em;
font-weight: 850;
color: #FFFFFF;
}
.admin-modal-subtitle {
display: flex;
flex-flow: row wrap;
gap: 8px;
align-items: center;
margin-bottom: 10px;
color: rgba(255,255,255,0.86);
font-weight: 600;
}
.admin-modal-chip {
display: inline-flex;
align-items: center;
padding: 4px 9px;
border-radius: 9999px;
background: rgba(255,255,255,0.14);
border: 1px solid rgba(255,255,255,0.18);
font-size: 0.82em;
}
.admin-modal-body {
padding: 22px 24px 24px 24px;
}
.admin-section-title {
margin: 0 0 10px 0;
font-size: 0.9em;
font-weight: 850;
letter-spacing: 0.04em;
text-transform: uppercase;
color: hsl(210, 18%, 36%);
}
.admin-modal-card .exit {
position: absolute;
top: 14px;
right: 14px;
width: 34px;
height: 34px;
border: 1px solid rgba(255,255,255,0.25);
background: rgba(255,255,255,0.16);
color: #FFFFFF;
font-size: 1.35em;
line-height: 1;
cursor: pointer;
}
.bs-detail-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 12px;
margin-bottom: 20px;
}
.bs-detail-card {
padding: 14px 16px;
border: 1px solid hsl(210, 18%, 88%);
background: linear-gradient(180deg, #FFFFFF, hsl(210, 12%, 98%));
}
.bs-detail-label {
margin-bottom: 3px;
color: hsl(210, 14%, 46%);
font-size: 0.74em;
font-weight: 850;
letter-spacing: 0.04em;
text-transform: uppercase;
}
.bs-detail-value {
color: hsl(210, 20%, 18%);
font-size: 1.02em;
font-weight: 850;
}
.bs-detail-value.overdue {
color: hsl(0, 65%, 42%);
}
.bs-detail-value.ok {
color: hsl(145, 50%, 32%);
}
.admin-modal-actions {
display: flex;
justify-content: space-between;
gap: 10px;
border-top: 1px solid hsl(210, 18%, 90%);
padding-top: 16px;
}
.admin-modal-actions button {
display: inline-flex;
align-items: center;
justify-content: center;
min-height: 36px;
padding: 8px 13px;
border: 1px solid hsl(210, 18%, 84%);
background: #FFFFFF;
color: hsl(210, 20%, 22%);
font-size: 0.86em;
font-weight: 850;
cursor: pointer;
box-shadow: 0 4px 12px rgba(31,38,46,0.06);
}
.admin-modal-actions button:hover {
background: hsl(210, 57%, 96%);
}
.admin-save-button {
color: #FFFFFF !important;
background: linear-gradient(135deg, var(--nak-orange), hsl(25, 95%, 40%)) !important;
border-color: hsl(25, 95%, 40%) !important;
}
.bs-date-input {
padding: 10px 12px;
font-size: var(--font-size-sm);
font-family: var(--font-primary);
border: 1px solid var(--gray-300);
background: var(--gray-50);
width: 100%;
box-sizing: border-box;
font-weight: 650;
}
.bs-date-input:focus {
border-color: hsl(25, 80%, 55%);
background: #FFFFFF;
outline: none;
box-shadow: 0 0 0 4px hsla(25, 80%, 55%, 0.14);
}
@media (max-width: 780px) {
.admin-users-shell { padding: 10px; }
.admin-list-header {
flex-direction: column;
align-items: flex-start;
padding: 14px;
gap: 8px;
}
table.admin-user-table {
display: block !important;
width: auto;
margin: 8px;
}
.admin-user-table thead { display: none; }
.admin-user-table tbody {
display: grid;
gap: 10px;
}
.admin-user-table tbody tr.admin-user-row {
display: grid;
grid-template-columns: 72px minmax(0, 1fr);
gap: 0 12px;
padding: 12px;
border: 1px solid rgba(120, 140, 160, 0.16);
box-shadow: 0 8px 22px rgba(31, 38, 46, 0.08);
}
.admin-user-table tbody tr.admin-user-row td {
display: block;
padding: 0 !important;
}
.admin-user-table tbody tr.admin-user-row td:first-child {
grid-row: 1 / span 3;
width: 72px;
min-width: 72px;
max-width: 72px;
}
.admin-profile-button,
.admin-profile-button img {
width: 64px !important;
height: 64px !important;
border-radius: 12px !important;
}
.admin-modal-card {
width: calc(100vw - 12px);
max-height: calc(100dvh - 12px);
}
.admin-modal-hero {
grid-template-columns: 78px minmax(0, 1fr);
gap: 14px;
padding: 18px;
}
.admin-modal-profile-img {
width: 72px;
height: 72px;
border-radius: 14px;
}
.admin-modal-title {
font-size: 1.15em;
margin-right: 40px;
}
.admin-modal-body { padding: 18px; }
.bs-detail-grid { grid-template-columns: 1fr; }
.admin-modal-actions { flex-direction: column-reverse; }
.admin-modal-actions button { width: 100%; }
}
@@ -0,0 +1,26 @@
.login_button {
position: relative;
}
.login_button.is-loading {
color: transparent !important;
pointer-events: none;
}
.login_button.is-loading::after {
content: '';
position: absolute;
top: 50%;
left: 50%;
width: 22px;
height: 22px;
margin: -11px 0 0 -11px;
border: 3px solid rgba(255, 255, 255, 0.35);
border-top-color: #fff;
border-radius: 50%;
animation: authorization-spin 0.7s linear infinite;
}
@keyframes authorization-spin {
to { transform: rotate(360deg); }
}
@@ -0,0 +1,694 @@
/* ══════════════════════════════════════
BRANDSCHUTZ, Variables & Base
══════════════════════════════════════ */
:root {
--bs-primary: #f08200;
--bs-primary-hover: #d97400;
--bs-primary-light: #fff4e6;
--bs-primary-shadow: rgba(240,130,0,0.35);
--bs-red: #d21400;
--bs-red-light: #ffeaea;
--bs-green: #2e7d32;
--bs-green-light: #e8f5e9;
}
/* ══════════════════════════════════════
WIZARD CONTAINER
══════════════════════════════════════ */
.bs-page-wrapper {
margin-top: calc(var(--header-height) + var(--header-title-height));
min-height: calc(100vh - var(--header-height) - var(--header-title-height) - var(--footer-height));
background: linear-gradient(135deg, var(--gray-50) 0%, hsl(30, 80%, 95%) 100%);
padding: var(--space-lg) 0 var(--space-3xl);
}
.bs-wizard {
max-width: 1400px;
width: 90%;
margin: 0 auto;
background: #ffffff;
box-shadow: var(--shadow-md);
overflow: hidden;
}
/* ══════════════════════════════════════
MAIN PROGRESS BAR (Sections)
══════════════════════════════════════ */
.bs-progress {
display: flex;
align-items: center;
justify-content: center;
gap: 0;
padding: 20px 28px 16px;
background: var(--gray-50);
border-bottom: 1px solid var(--gray-200);
flex-wrap: wrap;
}
.bs-progress-step {
display: flex;
flex-direction: column;
align-items: center;
gap: 4px;
flex-shrink: 0;
cursor: default;
transition: opacity 150ms;
}
.bs-progress-step:hover { opacity: 1; }
.bs-progress-circle {
width: 32px;
height: 32px;
display: flex;
align-items: center;
justify-content: center;
font-size: var(--font-size-sm);
font-weight: 600;
color: var(--gray-500);
background: var(--gray-200);
border: 2px solid var(--gray-300);
transition: all 250ms ease;
}
.bs-progress-step.active .bs-progress-circle {
background: var(--bs-primary);
border-color: var(--bs-primary);
color: #ffffff;
box-shadow: 0 2px 8px var(--bs-primary-shadow);
}
.bs-progress-step.done .bs-progress-circle {
background: #ffffff;
border-color: var(--bs-primary);
color: var(--bs-primary);
}
.bs-progress-label {
font-size: var(--font-size-xs);
font-weight: 500;
color: var(--gray-500);
white-space: nowrap;
transition: color 250ms ease;
max-width: 80px;
text-align: center;
overflow: hidden;
text-overflow: ellipsis;
}
.bs-progress-step.active .bs-progress-label,
.bs-progress-step.done .bs-progress-label {
color: var(--bs-primary);
}
.bs-progress-line {
flex: 1;
height: 2px;
min-width: 16px;
max-width: 50px;
background: var(--gray-300);
margin: 0 6px;
margin-bottom: 20px;
transition: background 250ms ease;
}
.bs-progress-line.done {
background: var(--bs-primary);
}
/* ══════════════════════════════════════
BODY & SECTION CONTENT
══════════════════════════════════════ */
.bs-body {
padding: 28px 32px 20px;
}
.bs-section-header {
text-align: center;
padding: 12px 20px;
background: var(--bs-primary-light);
border-bottom: 1px solid #fde0b0;
font-size: var(--font-size-sm);
font-weight: 600;
color: var(--bs-primary-hover);
letter-spacing: 0.3px;
}
.bs-chapter-title {
font-size: 1.6rem;
font-weight: 600;
color: var(--gray-900);
margin: 0 0 8px 0;
}
.bs-text {
font-size: var(--font-size-base);
color: var(--gray-800);
line-height: 1.7;
margin-bottom: 16px;
}
.bs-text strong { color: var(--gray-900); }
.bs-text a,
.bs-wizard label a {
text-decoration: none;
pointer-events: auto;
position: relative;
z-index: 1;
}
/* ══════════════════════════════════════
ACTIONS BAR
══════════════════════════════════════ */
.bs-actions {
display: flex;
justify-content: space-between;
align-items: center;
gap: 10px;
padding: 16px 28px 20px;
border-top: 1px solid var(--gray-200);
}
.bs-actions-left,
.bs-actions-right {
display: flex;
gap: 8px;
}
.bs-btn {
display: inline-flex;
align-items: center;
justify-content: center;
min-height: 38px;
padding: 8px 18px;
font-size: var(--font-size-sm);
font-weight: 600;
font-family: var(--font-primary);
border: 1px solid var(--gray-300);
background: #ffffff;
color: var(--gray-800);
cursor: pointer;
transition: background 150ms ease, transform 150ms ease;
white-space: nowrap;
}
.bs-btn:hover {
background: var(--gray-100);
transform: translateY(-1px);
}
.bs-btn-primary {
background: var(--bs-primary);
border-color: var(--bs-primary);
color: #ffffff;
}
.bs-btn-primary:hover {
background: var(--bs-primary-hover);
border-color: var(--bs-primary-hover);
}
.bs-btn-locked {
opacity: 0.4;
pointer-events: none;
cursor: not-allowed;
}
.bs-btn-check-disabled {
opacity: 0.4;
pointer-events: none;
}
.bs-btn-success {
background: var(--bs-green);
border-color: var(--bs-green);
color: #ffffff;
}
.bs-btn-success:hover {
background: var(--bs-green);
border-color: var(--bs-green);
transform: none;
}
.bs-btn-success.bs-btn-check-disabled {
opacity: 1;
}
.bs-btn-failed {
background: var(--bs-red);
border-color: var(--bs-red);
color: #ffffff;
}
.bs-btn-failed.bs-btn-check-disabled {
opacity: 1;
}
.bs-quiz-btn-group {
display: flex;
align-items: center;
gap: 8px;
flex-wrap: wrap;
margin-top: 20px;
}
/* ══════════════════════════════════════
INFO BOX / TIP BOX / WARNING BOX
══════════════════════════════════════ */
.bs-infobox {
padding: 16px 20px;
margin: 16px 0;
border-left: 4px solid var(--bs-primary);
background: var(--bs-primary-light);
font-size: var(--font-size-sm);
color: var(--gray-800);
line-height: 1.6;
}
.bs-infobox-title {
font-weight: 600;
margin-bottom: 6px;
color: var(--bs-primary);
}
.bs-warnbox {
padding: 16px 20px;
margin: 16px 0;
border-left: 4px solid var(--bs-red);
background: var(--bs-red-light);
font-size: var(--font-size-sm);
color: var(--gray-800);
line-height: 1.6;
}
.bs-warnbox-title {
font-weight: 600;
margin-bottom: 6px;
color: var(--bs-red);
}
.bs-successbox {
padding: 16px 20px;
margin: 16px 0;
border-left: 4px solid var(--bs-green);
background: var(--bs-green-light);
font-size: var(--font-size-sm);
color: var(--gray-800);
line-height: 1.6;
}
/* ══════════════════════════════════════
LIST STYLES
══════════════════════════════════════ */
.bs-list {
list-style: none;
padding: 0;
margin: 12px 0;
}
.bs-list li {
padding: 8px 0 8px 28px;
position: relative;
font-size: var(--font-size-sm);
color: var(--gray-800);
line-height: 1.6;
border-bottom: 1px solid var(--gray-100);
}
.bs-list li:last-child { border-bottom: none; }
.bs-list li::before {
content: '';
position: absolute;
left: 6px;
top: 14px;
width: 8px;
height: 8px;
background: var(--bs-primary);
}
.bs-list-numbered {
list-style: none;
padding: 0;
margin: 12px 0;
counter-reset: bs-counter;
}
.bs-list-numbered li {
padding: 10px 0 10px 40px;
position: relative;
font-size: var(--font-size-sm);
color: var(--gray-800);
line-height: 1.6;
border-bottom: 1px solid var(--gray-100);
counter-increment: bs-counter;
}
.bs-list-numbered li:last-child { border-bottom: none; }
.bs-list-numbered li::before {
content: counter(bs-counter);
position: absolute;
left: 0;
top: 8px;
width: 26px;
height: 26px;
background: var(--bs-primary);
color: #ffffff;
font-size: 0.75rem;
font-weight: 700;
display: flex;
align-items: center;
justify-content: center;
}
/* ══════════════════════════════════════
QUIZ / INTERACTIVE ELEMENTS
══════════════════════════════════════ */
.bs-quiz {
margin: 20px 0;
padding: 20px;
background: var(--gray-50);
border: 1px solid var(--gray-200);
}
.bs-quiz-question {
font-size: var(--font-size-base);
font-weight: 600;
color: var(--gray-900);
margin-bottom: 14px;
line-height: 1.5;
}
.bs-quiz-options { display: flex; flex-direction: column; gap: 8px; }
.bs-quiz-option {
display: flex;
align-items: center;
gap: 10px;
padding: 10px 14px;
background: #ffffff;
border: 2px solid var(--gray-200);
cursor: pointer;
transition: all 150ms;
font-size: var(--font-size-sm);
color: var(--gray-800);
}
.bs-quiz-option:hover { border-color: var(--bs-primary); background: var(--bs-primary-light); }
.bs-quiz-locked .bs-quiz-option { pointer-events: none; }
.bs-quiz-option.selected { border-color: var(--bs-primary); background: var(--bs-primary-light); }
.bs-quiz-option.correct { border-color: var(--bs-green); background: var(--bs-green-light); }
.bs-quiz-option.wrong { border-color: var(--bs-red); background: var(--bs-red-light); }
.bs-quiz-option input[type="checkbox"] { display: none; }
.bs-quiz-indicator {
width: 20px;
height: 20px;
border: 2px solid var(--gray-400);
flex-shrink: 0;
display: flex;
align-items: center;
justify-content: center;
font-size: 0.75rem;
color: transparent;
}
.bs-quiz-option.selected .bs-quiz-indicator {
border-color: var(--bs-primary);
background: var(--bs-primary);
color: #ffffff;
}
.bs-quiz-option.correct .bs-quiz-indicator {
border-color: var(--bs-green);
background: var(--bs-green);
color: #ffffff;
}
.bs-quiz-option.wrong .bs-quiz-indicator {
border-color: var(--bs-red);
background: var(--bs-red);
color: #ffffff;
}
.bs-quiz-feedback,
.bs-feedback {
margin-top: 12px;
padding: 10px 14px;
font-size: var(--font-size-sm);
font-weight: 600;
display: none;
}
.bs-quiz-feedback.show,
.bs-feedback.show { display: block; }
.bs-quiz-feedback.correct,
.bs-feedback.correct {
background: var(--bs-green-light);
color: var(--bs-green);
border-left: 4px solid var(--bs-green);
}
.bs-quiz-feedback.wrong,
.bs-feedback.wrong {
background: var(--bs-red-light);
color: var(--bs-red);
border-left: 4px solid var(--bs-red);
}
/* ══════════════════════════════════════
DROPDOWN MATCHING
══════════════════════════════════════ */
.bs-dropdown-match { margin: 16px 0; }
.bs-dropdown-match-row {
display: flex;
align-items: center;
gap: 16px;
padding: 12px 16px;
background: #ffffff;
border: 2px solid var(--gray-200);
margin-bottom: 8px;
transition: border-color 150ms;
}
.bs-dropdown-match-row.correct { border-color: var(--bs-green); background: var(--bs-green-light); }
.bs-dropdown-match-row.wrong { border-color: var(--bs-red); background: var(--bs-red-light); }
.bs-dropdown-match-label { flex: 1; font-weight: 600; color: var(--gray-800); }
.bs-dropdown-match-select {
padding: 8px 12px;
border: 2px solid var(--gray-200);
font-size: var(--font-size-sm);
color: var(--gray-800);
background: #fff;
min-width: 200px;
}
.bs-dropdown-match-select:focus { border-color: var(--bs-primary); outline: none; }
/* ══════════════════════════════════════
SCENARIO CARD
══════════════════════════════════════ */
.bs-scenario {
margin: 20px 0;
border: 1px solid var(--gray-200);
overflow: hidden;
}
.bs-scenario-header {
padding: 14px 20px;
background: linear-gradient(135deg, var(--bs-primary), var(--bs-primary-hover));
color: #ffffff;
font-weight: 600;
font-size: var(--font-size-sm);
}
.bs-scenario-body {
padding: 20px;
}
.bs-scenario-text {
font-size: var(--font-size-sm);
color: var(--gray-800);
line-height: 1.6;
margin-bottom: 16px;
font-style: italic;
}
/* ══════════════════════════════════════
CHECKLIST
══════════════════════════════════════ */
.bs-checklist { list-style: none; padding: 0; margin: 16px 0; }
.bs-checklist li {
display: flex;
align-items: flex-start;
gap: 10px;
padding: 10px 0;
border-bottom: 1px solid var(--gray-100);
font-size: var(--font-size-sm);
color: var(--gray-800);
cursor: pointer;
}
.bs-checklist li:last-child { border-bottom: none; }
.bs-check-box {
width: 22px;
height: 22px;
border: 2px solid var(--gray-300);
flex-shrink: 0;
display: flex;
align-items: center;
justify-content: center;
font-size: 0.8rem;
color: transparent;
transition: all 150ms;
margin-top: 1px;
}
.bs-checklist li.checked .bs-check-box {
border-color: var(--bs-green);
background: var(--bs-green);
color: #ffffff;
}
.bs-checklist li.checked { color: var(--gray-500); }
/* ══════════════════════════════════════
FILL-IN-THE-BLANK
══════════════════════════════════════ */
.bs-fill-input {
display: inline-block;
border: none;
border-bottom: 2px solid var(--bs-primary);
background: transparent;
font-family: var(--font-primary);
font-size: var(--font-size-sm);
font-weight: 600;
color: var(--gray-900);
padding: 2px 6px;
width: 120px;
outline: none;
transition: border-color 200ms;
}
.bs-fill-input:focus { border-color: var(--bs-primary-hover); }
.bs-fill-input.correct { border-color: var(--bs-green); color: var(--bs-green); }
.bs-fill-input.wrong { border-color: var(--bs-red); color: var(--bs-red); }
/* ══════════════════════════════════════
ACCORDION / EXPANDABLE
══════════════════════════════════════ */
.bs-accordion {
margin: 16px 0;
border: 1px solid var(--gray-200);
}
.bs-accordion-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 14px 18px;
background: var(--gray-50);
cursor: pointer;
font-weight: 600;
font-size: var(--font-size-sm);
color: var(--gray-800);
transition: background 150ms;
}
.bs-accordion-header:hover { background: var(--gray-100); }
.bs-accordion-arrow {
transition: transform 250ms;
font-size: 0.8rem;
color: var(--gray-500);
}
.bs-accordion.open .bs-accordion-arrow { transform: rotate(180deg); }
.bs-accordion-content {
display: none;
padding: 16px 18px;
font-size: var(--font-size-sm);
color: var(--gray-700);
line-height: 1.6;
border-top: 1px solid var(--gray-200);
}
.bs-accordion.open .bs-accordion-content { display: block; }
/* ══════════════════════════════════════
INTRO HERO / 5 SÄULEN ERKLÄRUNG
══════════════════════════════════════ */
.bs-intro-hero {
margin: 16px 0 24px;
background: linear-gradient(180deg, #fff9f0 0%, #ffffff 100%);
border: 1px solid #fde0b0;
box-shadow: 0 8px 24px rgba(0,0,0,0.06);
overflow: hidden;
}
.bs-intro-card {
margin: 18px 18px 18px;
padding: 18px 20px 20px;
background: #ffffff;
border-top: 4px solid var(--bs-primary);
box-shadow: 0 -6px 18px rgba(0,0,0,0.05);
text-align: left;
}
.bs-intro-lead {
margin: 0 0 16px 0;
font-size: var(--font-size-base);
line-height: 1.7;
color: var(--gray-800);
}
.bs-intro-lead strong { color: var(--bs-primary-hover); }
.bs-intro-pillars {
display: grid;
grid-template-columns: repeat(5, 1fr);
gap: 8px;
}
.bs-intro-pillar {
padding: 12px 10px;
background: var(--bs-primary-light);
border: 1px solid #fde0b0;
text-align: center;
transition: transform 150ms ease, box-shadow 150ms ease;
}
.bs-intro-pillar:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(240,130,0,0.16);
}
.bs-intro-pillar-number {
display: inline-flex;
align-items: center;
justify-content: center;
width: 26px;
height: 26px;
margin-bottom: 8px;
background: var(--bs-primary);
color: #ffffff;
font-size: 0.75rem;
font-weight: 700;
}
.bs-intro-pillar-title {
display: block;
font-size: var(--font-size-xs);
font-weight: 700;
color: var(--gray-900);
line-height: 1.35;
}
/* ══════════════════════════════════════
SCORE / PROGRESS PAGE
══════════════════════════════════════ */
.bs-score-bar-wrap {
margin: 24px 0;
}
.bs-score-bar {
width: 100%;
height: 24px;
background: var(--gray-200);
overflow: hidden;
}
.bs-score-bar-fill {
height: 100%;
transition: width 600ms ease;
}
.bs-score-bar-label {
margin-top: 8px;
font-size: var(--font-size-sm);
color: var(--gray-600);
font-weight: 500;
text-align: center;
}
.bs-score-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
gap: 12px;
margin: 20px 0;
}
.bs-score-card {
padding: 20px 16px;
text-align: center;
background: var(--gray-50);
border: 1px solid var(--gray-200);
}
.bs-score-card-num {
font-size: 1.8rem;
font-weight: 700;
color: var(--gray-800);
margin-bottom: 4px;
}
.bs-score-card-label {
font-size: var(--font-size-xs);
font-weight: 600;
color: var(--gray-500);
text-transform: uppercase;
letter-spacing: 0.05em;
}
.bs-score-correct .bs-score-card-num { color: var(--bs-green); }
.bs-score-wrong .bs-score-card-num { color: var(--bs-red); }
.bs-score-open .bs-score-card-num { color: var(--bs-primary); }
/* ══════════════════════════════════════
RESPONSIVE
══════════════════════════════════════ */
@media (max-width: 600px) {
.bs-wizard { margin: 0; width: 100%; }
.bs-progress { padding: 14px 8px 10px; }
.bs-progress-step[data-section="0"],
.bs-progress-step[data-section="0"] + .bs-progress-line {
display: none;
}
.bs-progress-label { font-size: 0.6rem; max-width: 48px; }
.bs-progress-circle { width: 26px; height: 26px; font-size: 0.7rem; }
.bs-progress-line { min-width: 8px; max-width: 20px; margin-bottom: 16px; }
.bs-body { padding: 16px; }
.bs-actions { padding: 12px 16px 16px; flex-wrap: wrap; }
.bs-dropdown-match-row { flex-direction: column; align-items: flex-start; gap: 8px; }
.bs-dropdown-match-select { min-width: unset; width: 100%; }
.bs-chapter-title { font-size: 1.3rem; }
.bs-intro-card { margin: 10px 10px 10px; padding: 14px; }
.bs-intro-pillars { grid-template-columns: 1fr; }
.bs-intro-pillar { display: flex; align-items: center; gap: 10px; text-align: left; }
.bs-intro-pillar-number { margin-bottom: 0; flex-shrink: 0; }
.bs-score-grid { grid-template-columns: repeat(2, 1fr); }
}
@@ -0,0 +1,321 @@
/* Schwarzes Brett / Contentpanel */
.sb-page {
position: relative;
display: flex;
flex-direction: column;
align-items: center;
margin-top: calc(var(--header-height) + var(--header-title-height));
width: 100%;
min-height: calc(100vh - var(--header-height) - var(--header-title-height) - var(--footer-height));
padding: var(--space-lg) var(--space-md) var(--space-3xl);
background: linear-gradient(135deg, var(--gray-50) 0%, hsl(330, 60%, 95%) 100%);
}
.sb-page > .content-tab-bar {
margin: calc(-1 * var(--space-lg)) calc(-1 * var(--space-md)) 0;
width: calc(100% + 2 * var(--space-md));
}
.sb-tab-content {
display: none;
width: 100%;
max-width: 960px;
margin: var(--space-lg) auto 0;
animation: sbFadeIn 300ms ease;
}
.sb-tab-content.active {
display: block;
}
@keyframes sbFadeIn {
from { opacity: 0; transform: translateY(8px); }
to { opacity: 1; transform: translateY(0); }
}
.sb-card {
margin-bottom: var(--space-lg);
padding: 24px 28px;
background: #ffffff;
box-shadow: var(--shadow-md);
}
.sb-card-title {
display: flex;
align-items: center;
gap: 8px;
margin: 0 0 var(--space-md);
color: var(--gray-800);
font-size: 1rem;
font-weight: 600;
letter-spacing: 0.04em;
text-transform: uppercase;
}
.sb-card-title svg {
width: 20px;
height: 20px;
flex-shrink: 0;
fill: var(--nak-raspberry);
}
.sb-entry-list {
display: flex;
flex-direction: column;
max-height: 360px;
margin-bottom: var(--space-lg);
overflow-y: auto;
border: 1px solid var(--gray-200);
}
.sb-entry {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
padding: 12px 16px;
border-bottom: 1px solid var(--gray-100);
}
.sb-entry:last-child {
border-bottom: none;
}
.sb-entry-info {
flex: 1;
min-width: 0;
}
.sb-entry-date {
color: var(--nak-raspberry);
font-size: var(--font-size-xs);
font-weight: 600;
letter-spacing: 0.03em;
text-transform: uppercase;
}
.sb-entry-title {
margin-top: 2px;
color: var(--gray-800);
font-size: var(--font-size-sm);
font-weight: 600;
}
.sb-entry-subtitle,
.sb-entry-meta {
margin-top: 2px;
color: var(--gray-500);
font-size: var(--font-size-xs);
}
.sb-entry-subtitle {
font-style: italic;
}
.sb-empty {
padding: 16px;
color: var(--gray-500);
font-size: var(--font-size-sm);
text-align: center;
}
.sb-form-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: var(--space-md);
}
.sb-form-grid-single {
grid-template-columns: 1fr;
}
.sb-form-full {
grid-column: 1 / -1;
}
.sb-field-group {
display: flex;
flex-direction: column;
gap: 4px;
}
.sb-field-group-action {
margin-top: var(--space-sm);
}
.sb-label {
color: var(--gray-700);
font-size: var(--font-size-sm);
font-weight: 500;
}
.sb-required {
color: var(--nak-red);
}
.sb-optional {
color: var(--gray-500);
font-weight: 300;
}
.sb-field {
box-sizing: border-box;
width: 100%;
padding: 10px 14px;
border: 1px solid var(--gray-300);
outline: none;
background: var(--gray-50);
font-family: var(--font-primary);
font-size: var(--font-size-base);
font-weight: 300;
transition: border-color 150ms ease, box-shadow 150ms ease;
}
.sb-field:focus {
border-color: var(--nak-raspberry);
background: #ffffff;
box-shadow: 0 0 0 3px rgba(215, 50, 130, 0.15);
}
.sb-btn {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 6px;
min-height: 40px;
padding: 10px 20px;
border: 1px solid var(--gray-300);
background: #ffffff;
color: var(--gray-700);
cursor: pointer;
font-family: var(--font-primary);
font-size: var(--font-size-sm);
font-weight: 600;
white-space: nowrap;
transition: background 150ms ease, transform 150ms ease, box-shadow 150ms ease;
}
.sb-btn:hover {
background: var(--gray-100);
box-shadow: var(--shadow-sm);
transform: translateY(-1px);
}
.sb-btn-primary {
border-color: var(--nak-raspberry);
background: linear-gradient(135deg, var(--nak-raspberry), #b8296e);
color: #ffffff;
}
.sb-btn-primary:hover {
border-color: #b8296e;
background: linear-gradient(135deg, #b8296e, #9c2260);
}
.sb-btn-danger {
min-height: 30px;
padding: 6px 12px;
border: 1px solid var(--gray-300);
background: none;
color: var(--nak-red);
font-size: var(--font-size-xs);
}
.sb-btn-danger:hover {
border-color: var(--nak-red);
background: hsl(0, 78%, 96%);
}
.sb-alert {
margin: var(--space-md) 0;
padding: 10px 16px;
border: 1px solid hsl(145, 50%, 85%);
background: hsl(145, 50%, 93%);
color: hsl(145, 50%, 28%);
font-size: var(--font-size-sm);
font-weight: 500;
}
.sb-alert-error {
border-color: hsl(0, 70%, 86%);
background: hsl(0, 78%, 96%);
color: hsl(0, 65%, 38%);
}
.sb-aushang-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
gap: var(--space-md);
margin-bottom: var(--space-lg);
}
.sb-aushang-card {
display: flex;
flex-direction: column;
align-items: center;
gap: 8px;
padding: 12px;
border: 1px solid var(--gray-200);
background: var(--gray-50);
transition: box-shadow 200ms ease, transform 200ms ease;
}
.sb-aushang-card:hover {
box-shadow: var(--shadow-md);
transform: translateY(-2px);
}
.sb-aushang-card img {
width: 100%;
height: 180px;
object-fit: cover;
border: 1px solid var(--gray-200);
}
.sb-aushang-title {
color: var(--gray-800);
font-size: var(--font-size-sm);
font-weight: 600;
text-align: center;
}
.sb-aushang-meta {
color: var(--gray-500);
font-size: var(--font-size-xs);
text-align: center;
}
.sb-aushang-delete-form {
width: 100%;
text-align: center;
}
.sb-spinner {
display: none;
width: 24px;
height: 24px;
margin: 0 auto;
border: 3px solid var(--gray-200);
border-top-color: var(--nak-raspberry);
border-radius: 50%;
animation: sbSpin 0.8s linear infinite;
}
.sb-uploading .sb-upload-button {
display: none;
}
.sb-uploading .sb-spinner {
display: block;
}
@keyframes sbSpin {
to { transform: rotate(360deg); }
}
@media (max-width: 768px) {
.sb-page { padding: var(--space-md) var(--space-sm); }
.sb-card { padding: 16px; }
.sb-form-grid { grid-template-columns: 1fr; }
.sb-aushang-grid { grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); }
}
@@ -0,0 +1,337 @@
/* Kalli – Eintragungsassistent */
.kalli-wizard {
width: 90%;
max-width: 1400px;
margin: var(--space-xl) auto;
overflow: hidden;
background: #fff;
box-shadow: var(--shadow-md);
}
.kalli-progress {
display: flex;
align-items: center;
justify-content: center;
gap: 0;
padding: 20px 28px 16px;
background: var(--gray-50);
border-bottom: 1px solid var(--gray-200);
}
.kalli-progress-step {
display: flex;
flex-shrink: 0;
flex-direction: column;
align-items: center;
gap: 4px;
}
.kalli-progress-circle {
display: flex;
align-items: center;
justify-content: center;
width: 32px;
height: 32px;
color: var(--gray-500);
font-size: var(--font-size-sm);
font-weight: 600;
background: var(--gray-200);
border: 2px solid var(--gray-300);
transition: all 250ms ease;
}
.kalli-progress-step.active .kalli-progress-circle {
color: #fff;
background: var(--nak-blue);
border-color: var(--nak-blue);
box-shadow: 0 2px 8px rgba(39, 134, 207, 0.35);
}
.kalli-progress-step.done .kalli-progress-circle {
color: var(--nak-blue);
background: #fff;
border-color: var(--nak-blue);
}
.kalli-progress-label {
color: var(--gray-500);
font-size: var(--font-size-xs);
font-weight: 500;
white-space: nowrap;
transition: color 250ms ease;
}
.kalli-progress-step.active .kalli-progress-label,
.kalli-progress-step.done .kalli-progress-label {
color: var(--nak-blue);
}
.kalli-progress-line {
flex: 1;
min-width: 20px;
max-width: 60px;
height: 2px;
margin: 0 6px 20px;
background: var(--gray-300);
transition: background 250ms ease;
}
.kalli-progress-line.done { background: var(--nak-blue); }
.kalli-step[hidden] { display: none !important; }
.kalli-modal-body { padding: 22px 28px 16px; }
.kalli-section-title {
margin: 0 0 16px;
color: var(--gray-600);
font-size: var(--font-size-sm);
font-weight: 600;
letter-spacing: 0.04em;
text-transform: uppercase;
}
.kalli-category-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
gap: var(--space-lg);
}
.kalli-category {
display: flex;
flex-direction: column;
gap: 2px;
}
.kalli-category-label {
margin-bottom: 4px;
color: var(--gray-700);
font-size: var(--font-size-sm);
font-weight: 600;
letter-spacing: 0.03em;
text-transform: uppercase;
}
.kalli-category-label--custom { color: var(--nak-red); }
label.kalli-radio-item {
display: flex;
align-items: center;
gap: 8px;
padding: 0 5px;
color: var(--gray-600);
font-size: var(--font-size-sm);
font-weight: 300;
letter-spacing: normal;
text-transform: none;
cursor: pointer;
border: 1px solid transparent;
transition: background 150ms ease, border-color 150ms ease;
}
label.kalli-radio-item:hover {
background: var(--gray-50);
border-color: var(--gray-200);
}
label.kalli-radio-item input[type="radio"] {
flex-shrink: 0;
align-self: center;
width: 16px;
height: 16px;
margin: 0;
accent-color: var(--nak-blue);
}
label.kalli-radio-item span {
overflow-wrap: break-word;
line-height: 1.4;
word-break: break-word;
hyphens: auto;
}
.kalli-custom-section {
margin-top: var(--space-lg);
padding: var(--space-md);
background: hsl(0, 60%, 98%);
border: 1px solid hsl(0, 60%, 90%);
}
.kalli-custom-hint {
margin: 0 0 var(--space-sm);
color: var(--gray-600);
font-size: var(--font-size-sm);
}
.kalli-custom-options {
display: flex;
flex-wrap: wrap;
gap: var(--space-lg);
margin-bottom: var(--space-sm);
}
.kalli-field-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: var(--space-md);
}
.kalli-field-grid-single { grid-template-columns: 1fr; }
.kalli-field-group {
display: flex;
flex-direction: column;
gap: 4px;
}
.kalli-label {
color: var(--gray-700);
font-size: var(--font-size-sm);
font-weight: 500;
}
.kalli-label-note {
color: var(--gray-500);
font-weight: 300;
}
.kalli-required { color: var(--nak-red); }
.kalli-field {
box-sizing: border-box;
width: 100%;
padding: 12px 14px;
color: inherit;
font-family: var(--font-primary);
font-size: var(--font-size-base);
font-weight: 300;
background: var(--gray-50);
border: 1px solid var(--gray-300);
outline: none;
transition: border-color 150ms ease, box-shadow 150ms ease;
}
.kalli-field:focus {
background: #fff;
border-color: var(--nak-blue);
box-shadow: 0 0 0 3px rgba(39, 134, 207, 0.2);
}
.kalli-summary {
display: flex;
flex-direction: column;
border: 1px solid var(--gray-200);
}
.kalli-summary-row {
display: flex;
align-items: center;
justify-content: space-between;
padding: 12px 16px;
border-bottom: 1px solid var(--gray-100);
}
.kalli-summary-row:last-child { border-bottom: 0; }
.kalli-summary-label {
color: var(--gray-600);
font-size: var(--font-size-sm);
font-weight: 500;
}
.kalli-summary-value {
color: var(--gray-900);
font-size: var(--font-size-sm);
font-weight: 500;
text-align: right;
}
.kalli-validation-error {
margin: var(--space-md) 28px 0;
padding: 10px 14px;
color: hsl(0, 78%, 40%);
font-size: var(--font-size-sm);
font-weight: 600;
background: hsl(0, 78%, 95%);
}
.kalli-validation-error--step { margin: var(--space-md) 0 0; }
.kalli-modal-actions {
display: flex;
align-items: center;
justify-content: space-between;
gap: 10px;
padding: 16px 28px 20px;
border-top: 1px solid var(--gray-200);
}
.kalli-modal-actions-left,
.kalli-modal-actions-right {
display: flex;
gap: 8px;
}
.kalli-btn {
display: inline-flex;
align-items: center;
justify-content: center;
min-height: 38px;
padding: 8px 18px;
color: var(--gray-800);
font-family: var(--font-primary);
font-size: var(--font-size-sm);
font-weight: 600;
white-space: nowrap;
cursor: pointer;
background: #fff;
border: 1px solid var(--gray-300);
transition: background 150ms ease, transform 150ms ease;
}
.kalli-btn:hover {
background: var(--gray-100);
transform: translateY(-1px);
}
.kalli-btn:disabled {
opacity: 0.65;
cursor: wait;
transform: none;
}
.kalli-btn-primary {
color: #fff;
background: var(--nak-blue);
border-color: var(--nak-blue);
}
.kalli-btn-primary:hover {
background: var(--nak-blue-hover);
border-color: var(--nak-blue-hover);
}
.kalli-btn-submit {
padding: 10px 24px;
color: #fff;
font-weight: 700;
background: linear-gradient(135deg, var(--nak-blue), var(--nak-blue-hover));
border-color: var(--nak-blue-hover);
}
.kalli-btn-submit:hover {
background: linear-gradient(135deg, var(--nak-blue-hover), #185d8f);
}
@media (max-width: 600px) {
.kalli-wizard { width: 100%; margin: 0; }
.kalli-progress { padding: 14px 12px 10px; }
.kalli-progress-label { font-size: 0.65rem; }
.kalli-progress-circle { width: 26px; height: 26px; font-size: 0.7rem; }
.kalli-progress-line { min-width: 12px; margin-bottom: 16px; }
.kalli-modal-body { padding: 16px; }
.kalli-category-grid,
.kalli-field-grid { grid-template-columns: 1fr; }
.kalli-validation-error { margin-right: 16px; margin-left: 16px; }
.kalli-validation-error--step { margin-right: 0; margin-left: 0; }
.kalli-modal-actions { flex-wrap: wrap; padding: 12px 16px 16px; }
}
@@ -1,6 +1,6 @@
/* Name: seelsorge.css
Beschreibung: Spezielles CSS für Seelsorge Planer */
#seelsorge.menue {
position: relative;
top: 0px;
@@ -24,7 +24,7 @@
width: 100%;
}
#seelsorge.menue .menu-item:link { color: hsl(210, 60%, 50%);}
#seelsorge.menue .menu-item:link { color: hsl(210, 60%, 50%);}
#seelsorge.menue .menu-item:hover { color: #FFFFFF;
opacity: 1.0;
transition: top 0.6s ease-in-out;
@@ -52,7 +52,7 @@
font-weight: 100;
cursor: pointer;
opacity: 0.9;
transition: background-color 0.5s ease;
-webkit-transition: background-color 0.5s ease;
/*box-shadow: 0 1px 1px 0 rgba(255, 255, 255, 0.3),
@@ -81,7 +81,7 @@
border-radius: 0;
}
.content .active { /* bezieht sich damit auf .content_seelsorge*/
display: flex;
display: flex;
}
/* Besuchsstellen */
@@ -102,7 +102,7 @@
color: white;
}
.reservation-button {
background-color: hsl(210, 57%, 50%)!important;
color: white;
}
@@ -138,7 +138,7 @@ div.content_output {
overflow: visible;
width: calc(100% - 31px);
background-color: hsl( 60, 33%, 95%);
border: 1px solid #DDDDDD;
border: 1px solid #DDDDDD;
border-radius: 0;
}
div.content_output ul {
@@ -176,7 +176,7 @@ ul.galery {
padding: 0px;
margin: 0px;
width:auto;
list-style: none;
list-style: none;
}
li.galery {
position: relative;
@@ -204,7 +204,7 @@ ul.galery .available {
}
ul.galery .notavailable {
box-shadow: 0 0 2px 1px hsl(0,0%,50%);
}
}
img.galery_image {
padding: 0px;
margin: 0px;
@@ -221,11 +221,11 @@ img.galery_image {
justify-content: center;
align-items: center;
align-self: center;
padding: 0px 5px 0px 5px;
margin: 0px;
height: 17px;
text-align: center;
text-align: center;
font-size: 0.78em;
font-weight: 500;
color: hsl(297, 40%, 45%);
@@ -256,7 +256,7 @@ img.galery_image {
margin: 5px;
width: 250px;
text-align: center;
background-color: #FFFFFF;
border: 5px solid hsl(60, 33%, 80%);
border-radius: 10px;
@@ -273,7 +273,7 @@ img.galery_image {
flex: 50%;
padding: 30px;
margin: 5px;
border: 0px solid #DDDDDD;
background-color: hsl(60, 33%, 80%);
border-radius: 10px;
@@ -286,10 +286,10 @@ img.galery_image {
padding-bottom: 2px;
margin: 0px;
width: auto;
border-spacing: 0px;
border-bottom: 1px solid #AAAAAA;
} */
/* .profile_calendar {
-ms-flex: 50%;
@@ -424,16 +424,16 @@ img.galery_image {
width: 100%;
overflow: hidden;
font-size: 1.0em;
font-weight: 600;
font-weight: 600;
border-spacing: 0;
border: none;
border-radius: 0;
border-radius: 0;
}
.calendar_content table {
padding: 0px;
margin: 0px 0px 0px 0px;
width: 100%;
height: auto;
height: auto;
table-layout: fixed;
border-spacing: 5px;
border: 0px solid #DDDDDD;
@@ -482,7 +482,7 @@ td.calendar_day_area:hover {
td.calendar_day_area a {
text-decoration: none;
}
#calendar_day_item:hover, #calendar_day_item *:hover {
.calendar_day_item:hover, .calendar_day_item *:hover {
filter: brightness(0.82);
box-shadow: 0 4px 12px rgba(0,0,0,0.2);
}
@@ -491,27 +491,27 @@ td.calendar_day_area a {
display: flex;
justify-content: center;
align-items: center;
align-content: center;
align-content: center;
width: auto;
height: 100%;
height: 100%;
aspect-ratio: 1/1;
vertical-align: middle;
border-radius: 0;
box-shadow: 0 2px 8px rgba(0,0,0,0.12);
transition: background 0.18s ease, box-shadow 0.18s ease, transform 0.18s ease;
}
.available_single {
.calendar_day_item.available_single {
color: hsl(210, 20%, 15%);
background-color: #ffdc0f;
}
.available_multiple {
.calendar_day_item.available_multiple {
color: #FFFFFF;
background-color: #006e14;
}
.normalday {
.calendar_day_item.normalday {
background-color: hsl(297, 10%, 98%);
}
.today {
.calendar_day_item.today {
color: #FFFFFF;
background-color: #1243a0;
box-shadow: 0 4px 16px rgba(18,67,160,0.35);
@@ -521,32 +521,32 @@ td.calendar_day_area a {
background-color: hsl(195, 80%, 60%);
box-shadow: 1px 1px 2px 1px rgba(0, 0, 0, 0.3);
} */
.saturday {
.calendar_day_item.saturday {
color: #FFFFFF;
background-color: hsl(297, 25%, 45%);
}
.sunday, .localholiday {
.calendar_day_item.sunday, .calendar_day_item.localholiday {
color: #FFFFFF;
background-color: hsl(297, 80%, 40%);
}
.past {
.calendar_day_item.past {
color: hsl(0, 0%, 55%);
background-color: hsl(0, 0%, 90%);
}
.editmode {
border-radius: 0 !important;
box-shadow: 0 4px 16px rgba(0,0,0,0.2) !important;
}
}
.calendar_day {
display: flex;
flex-wrap: wrap;
justify-content: center;
align-content: center;
align-content: center;
width: auto;
height: 43px;
border: 0px solid #000;
border: 0px solid #000;
}
.feiertagsname {
position: absolute;
@@ -557,7 +557,7 @@ td.calendar_day_area a {
padding: 0px 1px;
/*yphens: auto; */ /* Textumbruch erstmal nicht*/
font-size: 0.55em;
text-align: center;
text-align: center;
width: auto;
color: hsl(297, 100%, 20%);
border: 1px solid hsl(297, 30%, 90%);
@@ -593,13 +593,13 @@ td.greenbox {
justify-content: space-evenly;
padding: 0;
margin: 0;
}
#seelsorge.menue .menu-item {
padding: 5px 2vw 5px 2vw;
margin: 0.125vh 1vw 0.0125vh 1vw;
max-width: 24vw;
font-size: 80%;
font-size: 80%;
}
.content_seelsorge {
padding: 0px;
@@ -636,7 +636,7 @@ td.greenbox {
padding: 0px;
margin: 5px;
width:auto;
list-style: none;
list-style: none;
}
}
@media (max-width: 400px) {
@@ -647,11 +647,11 @@ td.greenbox {
padding: 5px 5px 5px 5px;
margin: 0px 5px 0px 5px;
max-width: 24vw;
font-size: 77%;
font-size: 77%;
}
.page_calendar {
width: calc(100vw - 10px - 2px);
max-width: 400px;
transform: scale(0.98);
overflow-x: hidden;
@@ -661,7 +661,7 @@ td.greenbox {
}
.calendar_day_item {
width: auto;
height: 90%;
height: 90%;
aspect-ratio: 1/1;
vertical-align: middle;
border-radius: 0;
@@ -685,10 +685,10 @@ td.greenbox {
}
@media (max-width: 475px) {
.page_calendar {
max-height: 475px;
max-width: 475px;
transform: scale(0.6);
}
} */
@@ -1002,3 +1002,571 @@ td.greenbox {
font-size: 0.85em;
}
}
/* Besuchsstellen: Karten, Detail- und Bearbeitungsmodals */
/* --- Shell --- */
.visit-shell {
padding: 18px;
width: 100%;
background: linear-gradient(135deg, var(--gray-50) 0%, var(--theme-light, var(--nak-blue-light)) 100%);
min-height: 100%;
}
.visit-card {
overflow: hidden;
border: 1px solid rgba(120, 140, 160, 0.22);
border-radius: 0;
background: rgba(255,255,255,0.86);
box-shadow: 0 20px 45px rgba(31, 38, 46, 0.10), 0 1px 0 rgba(255,255,255,0.9) inset;
}
.visit-list-header {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
padding: 16px 18px 12px 18px;
border-bottom: 1px solid rgba(120, 140, 160, 0.16);
background: linear-gradient(180deg, rgba(255,255,255,0.92), rgba(255,255,255,0.72));
}
.visit-list-title {
font-size: 1.05em;
font-weight: 800;
color: hsl(210, 20%, 15%);
}
.visit-list-actions {
display: flex;
gap: 8px;
align-items: center;
}
.visit-action-btn {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 7px 14px;
border: 1px solid hsl(210, 18%, 84%);
border-radius: 0;
background: #FFFFFF;
color: hsl(210, 20%, 22%);
font-size: 0.86em;
font-weight: 850;
cursor: pointer;
box-shadow: 0 4px 12px rgba(31,38,46,0.06);
text-decoration: none;
transition: background 0.18s ease;
}
.visit-action-btn:hover { background: hsl(210, 57%, 96%); }
.visit-action-btn-primary {
color: #FFFFFF;
background: linear-gradient(135deg, var(--nak-purple), #5c1865);
border-color: #5c1865;
}
.visit-action-btn-primary:hover { background: linear-gradient(135deg, #5c1865, #4a1352); }
/* --- Table (admin-style) --- */
table.visit-table {
display: table !important;
width: calc(100% - 24px);
margin: 8px 12px 14px 12px;
border-collapse: separate;
border-spacing: 0 8px;
padding: 0;
background: transparent;
overflow: visible;
}
.visit-table:after { display: none; }
.visit-table thead th {
position: sticky;
top: 0;
padding: 10px 14px;
color: hsl(210, 16%, 46%);
font-size: 0.78em;
font-weight: 800;
text-align: left;
letter-spacing: 0.04em;
text-transform: uppercase;
background: transparent !important;
border: none !important;
box-shadow: none;
}
.visit-table thead th::after { display: none; }
.visit-table tbody tr.visit-row {
position: relative;
cursor: pointer;
background: rgba(255, 255, 255, 0.92);
border-radius: 0;
box-shadow: 0 6px 18px rgba(31, 38, 46, 0.06);
transition: transform 0.18s ease, box-shadow 0.18s ease, background 0.18s ease;
}
.visit-table tbody tr.visit-row:hover,
.visit-table tbody tr.visit-row:focus {
transform: translateY(-1px);
background: #FFFFFF;
box-shadow: 0 12px 26px rgba(31, 38, 46, 0.13);
outline: none !important;
filter: none !important;
}
.visit-table td {
padding: 12px 14px !important;
vertical-align: middle;
border: none !important;
background: transparent !important;
}
.visit-table tbody tr.visit-row td:first-child {
position: static !important;
width: 18px;
min-width: 18px;
max-width: 18px;
padding-right: 0 !important;
border-radius: 0;
}
.visit-table tbody tr.visit-row td:last-child {
border-radius: 0;
}
/* Status dot (first column) */
.visit-status-dot {
display: block;
width: 14px;
height: 14px;
border-radius: 50%;
box-shadow: 0 2px 6px rgba(0,0,0,0.15);
}
.visit-status-dot.is-blue { background: var(--nak-blue); }
.visit-status-dot.is-red { background: var(--nak-red); }
.visit-status-dot.is-orange { background: var(--nak-orange); }
.visit-status-dot.is-green { background: var(--nak-darkgreen); }
/* Status badge (pill below name) */
.visit-status-badge {
display: inline-flex;
align-items: center;
padding: 2px 7px;
border-radius: 0;
font-size: 0.72em;
font-weight: 800;
}
.visit-status-badge.is-blue {
color: hsl(210, 57%, 30%);
background: hsl(210, 57%, 92%);
}
.visit-status-badge.is-red {
color: hsl(0, 60%, 36%);
background: hsl(0, 65%, 93%);
}
.visit-status-badge.is-orange {
color: hsl(30, 70%, 30%);
background: hsl(35, 80%, 92%);
}
.visit-status-badge.is-green {
color: hsl(145, 50%, 28%);
background: hsl(145, 50%, 92%);
}
/* Name column */
.visit-name-text {
display: inline-flex;
align-items: center;
gap: 8px;
font-size: 1em;
font-weight: 800;
color: hsl(210, 20%, 15%);
}
/* Info column */
.visit-info-cell {
font-size: 0.9em;
color: hsl(210, 14%, 40%);
font-weight: 600;
}
.visit-info-cell .visit-planned-by {
display: inline-flex;
align-items: center;
gap: 6px;
margin-top: 4px;
font-size: 0.88em;
color: hsl(210, 57%, 40%);
font-weight: 700;
}
.visit-info-cell .visit-planned-by img {
width: 24px;
height: 24px;
border-radius: 50%;
object-fit: cover;
}
.visit-empty-row td {
padding: 24px !important;
text-align: center !important;
color: hsl(210, 20%, 45%);
background: #FFFFFF !important;
border-radius: 0;
}
/* --- Detail Modal --- */
.visit-detail-overlay {
position: fixed;
inset: 0;
background: rgba(0,0,0,0.45);
z-index: 9999;
display: none;
align-items: center;
justify-content: center;
padding: 18px;
}
.visit-detail-card {
position: relative;
width: min(640px, calc(100vw - 24px));
max-height: min(780px, calc(100vh - 24px));
overflow: auto;
background: #FFFFFF;
border-radius: 0;
box-shadow: 0 24px 70px rgba(0,0,0,0.36);
}
.visit-detail-hero {
padding: 24px;
color: #FFFFFF;
background: radial-gradient(circle at 10% 0%, rgba(255,255,255,0.24), transparent 32%),
linear-gradient(135deg, var(--nak-purple), #5c1865);
border-radius: 0;
}
.visit-detail-hero-title {
font-size: 1.3em;
font-weight: 850;
margin-bottom: 6px;
}
.visit-detail-hero-sub {
color: rgba(255,255,255,0.8);
font-weight: 600;
}
.visit-detail-close {
position: absolute;
top: 14px;
right: 14px;
width: 34px;
height: 34px;
border-radius: 0;
border: 1px solid rgba(255,255,255,0.25);
background: rgba(255,255,255,0.16);
color: #FFFFFF;
font-size: 1.35em;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
line-height: 1;
}
.visit-detail-body {
padding: 22px 24px 24px 24px;
}
.visit-detail-section-title {
margin: 16px 0 8px 0;
font-size: 0.88em;
font-weight: 850;
text-transform: uppercase;
letter-spacing: 0.04em;
color: hsl(210, 18%, 36%);
}
.visit-detail-section-title:first-child { margin-top: 0; }
.visit-detail-member {
display: flex;
align-items: center;
justify-content: space-between;
padding: 12px 14px;
border: 1px solid hsl(210, 18%, 88%);
border-radius: 0;
background: hsl(210, 12%, 98%);
margin-bottom: 8px;
}
.visit-detail-member-info {
font-weight: 700;
color: hsl(210, 20%, 18%);
}
.visit-detail-member-info .age {
font-size: 0.88em;
color: hsl(210, 14%, 46%);
font-weight: 600;
margin-left: 8px;
}
.visit-detail-member-edit {
width: 32px;
height: 32px;
border-radius: 0;
border: 1px solid hsl(210, 18%, 84%);
background: #FFF;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
font-size: 0.9em;
transition: background 0.18s ease;
}
.visit-detail-member-edit:hover { background: hsl(210, 57%, 96%); }
.visit-detail-info-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 10px;
margin-bottom: 12px;
}
.visit-detail-info-item {
padding: 10px 12px;
border: 1px solid hsl(210, 18%, 90%);
border-radius: 0;
background: hsl(210, 12%, 98.5%);
}
.visit-detail-info-label {
font-size: 0.72em;
font-weight: 850;
text-transform: uppercase;
letter-spacing: 0.04em;
color: hsl(210, 14%, 46%);
margin-bottom: 2px;
}
.visit-detail-info-value {
font-weight: 750;
color: hsl(210, 20%, 18%);
font-size: 0.95em;
}
.visit-detail-actions {
display: flex;
flex-wrap: wrap;
gap: 8px;
margin-top: 18px;
padding-top: 16px;
border-top: 1px solid hsl(210, 18%, 90%);
}
.visit-detail-actions .visit-action-btn { font-size: 0.88em; padding: 9px 16px; }
/* --- Edit/Picker Modals (kept from original) --- */
.visit-modal-overlay {
position: fixed;
inset: 0;
background: rgba(0,0,0,0.45);
z-index: 10000;
display: none;
align-items: center;
justify-content: center;
padding: 18px;
}
.visit-modal {
width: min(820px, 96vw);
max-height: 92vh;
overflow: auto;
background: #FFFFFF;
border-radius: 0;
box-shadow: 0 24px 70px rgba(0,0,0,0.36);
}
.visit-modal-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 20px 24px;
border-bottom: 1px solid hsl(210, 18%, 90%);
background: linear-gradient(180deg, rgba(255,255,255,0.95), rgba(255,255,255,0.8));
position: sticky;
top: 0;
z-index: 1;
border-radius: 0;
}
.visit-modal-header h2 { margin: 0; font-size: 1.15em; font-weight: 850; color: hsl(210, 20%, 15%); }
.visit-modal-close {
cursor: pointer;
border: 1px solid hsl(210, 18%, 84%);
background: #FFF;
border-radius: 0;
width: 34px;
height: 34px;
font-size: 1.3em;
line-height: 1;
display: flex;
align-items: center;
justify-content: center;
}
.visit-modal-close:hover { background: hsl(210, 12%, 95%); }
.visit-modal-body { padding: 20px 24px 24px 24px; }
.visit-picker-search {
width: 100%;
box-sizing: border-box;
padding: 12px 14px;
border-radius: 0;
border: 1px solid hsl(210, 18%, 84%);
background: hsl(210, 12%, 98%);
font-size: 1em;
font-weight: 650;
outline: none;
margin-bottom: 12px;
}
.visit-picker-search:focus { border-color: var(--nak-purple); background: #FFF; }
.visit-picker-list { display: flex; flex-direction: column; gap: 8px; }
.visit-picker-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px 14px;
border-radius: 0;
border: 1px solid hsl(210, 18%, 88%);
background: #FFF;
cursor: pointer;
transition: background 0.18s ease, transform 0.18s ease;
}
.visit-picker-item:hover { background: hsl(210, 57%, 97%); transform: translateY(-1px); }
.visit-picker-name { font-weight: 800; color: hsl(210, 20%, 15%); }
.visit-picker-sub { font-size: 0.82em; color: hsl(210, 14%, 46%); margin-top: 2px; }
.visit-form-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 12px;
}
.visit-form-grid .full { grid-column: 1 / -1; }
.visit-form-field label {
display: block;
font-weight: 750;
margin-bottom: 6px;
font-size: 0.88em;
color: hsl(210, 18%, 36%);
text-transform: uppercase;
letter-spacing: 0.04em;
}
.visit-form-field input,
.visit-form-field select {
width: 100%;
box-sizing: border-box;
padding: 12px 14px;
border-radius: 0;
border: 1px solid hsl(210, 18%, 84%);
background: hsl(210, 12%, 98%);
font-size: 1em;
font-weight: 650;
outline: none;
}
.visit-form-field input:focus,
.visit-form-field select:focus { border-color: var(--nak-purple); background: #FFF; }
.visit-modal-actions {
display: flex;
gap: 8px;
justify-content: flex-end;
margin-top: 18px;
padding-top: 16px;
border-top: 1px solid hsl(210, 18%, 90%);
}
.visit-form-error {
margin-bottom: 14px;
padding: 10px 12px;
border: 1px solid hsl(0, 72%, 88%);
background: hsl(0, 72%, 97%);
color: hsl(0, 72%, 38%);
font-size: 0.9em;
font-weight: 700;
}
.visit-modal-actions button { cursor: pointer; border: none; border-radius: 0; padding: 10px 18px; font-weight: 850; font-size: 0.88em; }
.visit-btn-save { background: linear-gradient(135deg, hsl(145, 55%, 42%), hsl(145, 55%, 34%)); color: #fff; }
.visit-btn-cancel { background: hsl(210, 12%, 92%); color: hsl(210, 20%, 30%); }
.visit-btn-delete { background: hsl(0, 72%, 95%); color: hsl(0, 72%, 42%); border: 1px solid hsl(0, 72%, 88%); margin-right: auto; }
.visit-phone-row { display: flex; align-items: stretch; width: 100%; }
.visit-input-prefix {
display: flex;
align-items: center;
padding: 0 12px;
border: 1px solid hsl(210, 18%, 84%);
border-right: none;
border-radius: 0;
background: hsl(210, 12%, 95%);
font-weight: 800;
font-size: 0.9em;
white-space: nowrap;
color: hsl(210, 14%, 40%);
}
.visit-phone-row input.visit-phone-input {
width: 100%;
box-sizing: border-box;
padding: 12px 14px;
border: 1px solid hsl(210, 18%, 84%);
border-left: none;
border-radius: 0;
background: hsl(210, 12%, 98%);
font-size: 1em;
font-weight: 650;
outline: none;
}
@media (max-width: 780px) {
.visit-shell { padding: 10px; }
.visit-list-header {
align-items: flex-start;
flex-direction: column;
padding: 14px;
gap: 8px;
}
.visit-list-actions {
width: 100%;
justify-content: space-between;
}
table.visit-table {
display: block !important;
width: auto;
margin: 8px;
padding: 0;
border-spacing: 0;
}
.visit-table thead { display: none; }
.visit-table tbody {
display: grid;
gap: 10px;
}
.visit-table tbody tr.visit-row {
display: grid;
grid-template-columns: 18px minmax(0, 1fr);
gap: 0 12px;
padding: 14px;
border: 1px solid rgba(120, 140, 160, 0.16);
border-radius: 0;
box-shadow: 0 8px 22px rgba(31, 38, 46, 0.08);
}
.visit-table tbody tr.visit-row td {
display: block;
padding: 0 !important;
border-radius: 0 !important;
}
.visit-table tbody tr.visit-row td:first-child {
grid-row: 1 / span 2;
width: 18px;
min-width: 18px;
max-width: 18px;
display: flex;
align-items: center;
}
.visit-table tbody tr.visit-row td:nth-child(2) {
align-self: end;
min-width: 0;
}
.visit-table tbody tr.visit-row td:nth-child(3) {
align-self: start;
min-width: 0;
padding-top: 5px !important;
}
.visit-name-text {
max-width: 100%;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.visit-empty-row {
display: block !important;
}
.visit-empty-row td {
display: block;
}
.visit-detail-info-grid { grid-template-columns: 1fr; }
.visit-detail-card { width: calc(100vw - 12px); max-height: calc(100dvh - 12px); }
}
@media (max-width: 480px) {
.visit-shell { padding: 8px; }
.visit-card { border-radius: 0; }
.visit-list-title { font-size: 1em; }
.visit-table tbody tr.visit-row {
gap: 0 10px;
padding: 10px;
}
.visit-name-text { font-size: 0.96em; }
.visit-info-cell { font-size: 0.85em; }
.visit-action-btn { font-size: 0.78em; padding: 6px 10px; }
}
@@ -0,0 +1,15 @@
function openBsModal(userId) {
var modal = document.getElementById('bsModal-' + userId);
if (modal) modal.style.display = 'flex';
}
function closeBsModal(userId) {
var modal = document.getElementById('bsModal-' + userId);
if (modal) modal.style.display = 'none';
}
document.addEventListener('keydown', function(e) {
if (e.key === 'Escape') {
document.querySelectorAll('.standard_overlay[id^="bsModal-"]').forEach(function(m) {
m.style.display = 'none';
});
}
});
@@ -0,0 +1,72 @@
'use strict';
document.addEventListener('DOMContentLoaded', function () {
document.querySelectorAll('[data-auth-toast]').forEach(function (toast) {
requestAnimationFrame(function () {
toast.classList.add('nak-toast--visible');
});
});
var otpRequestForm = document.getElementById('otpRequestForm');
if (otpRequestForm) {
otpRequestForm.addEventListener('submit', function () {
var button = document.getElementById('requestBtn');
if (button) {
button.classList.add('is-loading');
button.disabled = true;
}
});
}
var signupForm = document.getElementById('signup');
if (!signupForm || typeof window.JustValidate !== 'function') {
return;
}
var validation = new window.JustValidate('#signup');
validation
.addField('#first_name', [
{ rule: 'required', errorMessage: 'Pflichtfeld' },
{ rule: 'maxLength', value: 100, errorMessage: 'Zu lang' }
])
.addField('#last_name', [
{ rule: 'required', errorMessage: 'Pflichtfeld' },
{ rule: 'maxLength', value: 100, errorMessage: 'Zu lang' }
])
.addField('#email', [
{ rule: 'required', errorMessage: 'Pflichtfeld' },
{ rule: 'email', errorMessage: 'Ungültig' },
{ rule: 'maxLength', value: 254, errorMessage: 'Zu lang' }
])
.addField('#phone', [
{ rule: 'required', errorMessage: 'Pflichtfeld' },
{
validator: function (value) {
var digits = String(value).replace(/\D+/g, '');
return /^[1-9][0-9]{5,14}$/.test(digits);
},
errorMessage: 'Ohne führende 0, 6–15 Ziffern'
}
])
.addField('#password', [
{ rule: 'required', errorMessage: 'Pflichtfeld' },
{
validator: function (value) {
return String(value).length >= 8 && /[a-z]/i.test(value) && /[0-9]/.test(value);
},
errorMessage: 'Mind. 8 Zeichen, Buchstabe und Zahl'
}
])
.addField('#password_confirmation', [
{ rule: 'required', errorMessage: 'Pflichtfeld' },
{
validator: function (value, fields) {
return value === fields['#password'].elem.value;
},
errorMessage: 'Passwörter müssen übereinstimmen'
}
])
.onSuccess(function () {
signupForm.submit();
});
});
@@ -0,0 +1,601 @@
/* ══════════════════════════════════════
NAVIGATION
══════════════════════════════════════ */
var bsCurrentSection = 0;
var bsTotalSections = 6; // 0=intro, 1-5=pillars
var bsCurrentSub = {}; // track sub-steps per section
// Init sub-step tracking
for (var i = 1; i <= 5; i++) bsCurrentSub[i] = 1;
function bsGoToSection(section) {
var scoreEl = document.getElementById('bsSectionScore');
if (scoreEl) scoreEl.style.display = 'none';
// Hide all sections
for (var i = 0; i < bsTotalSections; i++) {
var el = document.getElementById('bsSection' + i);
if (el) el.style.display = 'none';
}
// Show target section
var target = document.getElementById('bsSection' + section);
if (target) target.style.display = '';
bsCurrentSection = section;
bsUpdateMainProgress(section);
// Reset to first sub-step when entering a section
if (section >= 1 && section <= 5) {
bsCurrentSub[section] = 1;
bsShowSubStep(section, 1);
}
bsScrollToTop();
}
function bsUpdateMainProgress(section) {
var steps = document.querySelectorAll('#bsMainProgress .bs-progress-step');
var lines = document.querySelectorAll('#bsMainProgress .bs-progress-line');
steps.forEach(function(el, idx) {
el.classList.remove('active', 'done');
if (idx === section) el.classList.add('active');
else if (idx < section) el.classList.add('done');
});
lines.forEach(function(el, idx) {
el.classList.toggle('done', idx < section);
});
}
/* ── Sub-step navigation ── */
function bsSubStep(section, step) {
bsCurrentSub[section] = step;
bsShowSubStep(section, step);
bsScrollToTop();
}
function bsShowSubStep(section, step) {
// Count sub-steps
var maxSub = document.querySelectorAll('#bsSection' + section + ' .bs-substep').length;
for (var i = 1; i <= maxSub; i++) {
var el = document.getElementById('bsSub' + section + '_' + i);
if (el) el.style.display = (i === step) ? '' : 'none';
}
}
function bsScrollToTop() {
window.scrollTo({top: 0, behavior: 'smooth'});
var headerHeight = parseFloat(getComputedStyle(document.documentElement).getPropertyValue('--header-height')) || 64;
var elemHeaderMenue = document.getElementById('header_menue');
var elemHeaderTitle = document.getElementById('header_title');
if (elemHeaderMenue) elemHeaderMenue.style.top = '0';
if (elemHeaderTitle) elemHeaderTitle.style.top = headerHeight + 'px';
}
/* ── Dropdown Matching ── */
function bsCheckDropdownMatch() {
var container = document.getElementById('match3');
var rows = container.querySelectorAll('.bs-dropdown-match-row');
var correct = 0;
var total = rows.length;
rows.forEach(function(row) {
var select = row.querySelector('select');
row.classList.remove('correct', 'wrong');
if (select.value === select.dataset.correct) {
row.classList.add('correct');
correct++;
} else {
row.classList.add('wrong');
}
});
var fb = document.getElementById('match3_fb');
if (correct === total) {
fb.className = 'bs-quiz-feedback show correct';
fb.textContent = 'Richtig: Alle Zuordnungen korrekt! Gut gemacht!';
} else {
fb.className = 'bs-quiz-feedback show wrong';
fb.textContent = correct + ' von ' + total + ' richtig. Bitte überprüfen Sie Ihre Auswahl.';
}
return correct === total;
}
/* ══════════════════════════════════════
FILL-IN-THE-BLANK
══════════════════════════════════════ */
function bsCheckFill(ids) {
var correct = 0;
var total = ids.length;
ids.forEach(function(id) {
var input = document.getElementById(id);
var answer = input.dataset.answer.toLowerCase().trim();
var value = input.value.toLowerCase().trim();
input.classList.remove('correct', 'wrong');
if (value === answer) {
input.classList.add('correct');
correct++;
} else {
input.classList.add('wrong');
}
});
// Find feedback element near the button
var fb = document.getElementById('fill2_fb');
if (fb) {
fb.classList.remove('correct', 'wrong');
fb.classList.add('show');
if (correct === total) {
fb.classList.add('correct');
fb.textContent = 'Richtig: Alle Lücken richtig ausgefüllt!';
} else {
fb.classList.add('wrong');
fb.textContent = correct + ' von ' + total + ' richtig. Korrekte Antworten: 112, Klixstraße 17, 13403';
}
}
return correct === total;
}
/* ══════════════════════════════════════
MULTI-SELECT CHECKBOX QUIZ
══════════════════════════════════════ */
function bsToggleCheckbox(optionEl) {
var cb = optionEl.querySelector('input[type="checkbox"]');
cb.checked = !cb.checked;
optionEl.classList.toggle('selected', cb.checked);
}
function bsCheckMultiGeneric(quizId, correctMsg, wrongMsg) {
var quiz = document.getElementById(quizId);
var options = quiz.querySelectorAll('.bs-quiz-option');
var allCorrect = true;
options.forEach(function(opt) {
var cb = opt.querySelector('input[type="checkbox"]');
var isCorrect = cb.dataset.correct === 'true';
opt.classList.remove('correct', 'wrong');
if (isCorrect && cb.checked) {
opt.classList.add('correct');
} else if (isCorrect && !cb.checked) {
opt.classList.add('correct');
allCorrect = false;
} else if (!isCorrect && cb.checked) {
opt.classList.add('wrong');
allCorrect = false;
}
});
var fb = document.getElementById(quizId + '_fb');
fb.classList.remove('correct', 'wrong');
fb.classList.add('show');
if (allCorrect) {
fb.classList.add('correct');
fb.textContent = correctMsg;
} else {
fb.classList.add('wrong');
fb.textContent = wrongMsg;
}
return allCorrect;
}
/* ══════════════════════════════════════
INDIVIDUAL QUIZ GATES
══════════════════════════════════════ */
var bsQuizPassed = {};
var bsQuizAttempts = {};
var bsQuizCorrect = {}; // true = correct, false = nach drei Versuchen beendet
var bsQuizGates = [
{ section: 1, nextBtnId: 'bsNextS1Q', quizIds: ['quiz1_1', 'quiz1_2', 'quiz1_3'] },
{ section: 2, nextBtnId: 'bsNextS2Q', quizIds: ['scenario2_1', 'fill2'] },
{ section: 3, nextBtnId: 'bsNextS3Q', quizIds: ['match3', 'quiz3_1', 'quiz3_2'] },
{ section: 4, nextBtnId: 'bsNextS4Q', quizIds: ['tf4', 'scenario4_1'] },
{ section: 5, nextBtnId: 'bsNextS5P1', quizIds: ['bs5q1', 'bs5q2', 'bs5q3', 'bs5q4', 'bs5q5'] },
{ section: 5, nextBtnId: 'bsNextS5P2', quizIds: ['bs5q6', 'bs5q7', 'bs5q8', 'bs5q9', 'bs5q10'] }
];
var bsQuizMessages = {
scenario2_1: [
'Richtig! Nicht durch das verrauchte Treppenhaus gehen und keinen Löschversuch im Rauch unternehmen. Richtig ist: 112 anrufen, warnen, in einem rauchfreien Raum bleiben und sich am Fenster bemerkbar machen.',
'Falsch. Gesucht sind die Verhaltensweisen, die man nicht tun sollte: durch das verrauchte Treppenhaus laufen oder im verrauchten Bereich selbst löschen.'
],
tf4: [
'Richtig: Alle Antworten richtig! Ausgezeichnet!',
'Nicht ganz richtig. Die korrekten Antworten sind markiert.'
],
scenario4_1: [
'Richtig! Bei Elektrobränden niemals Wasser verwenden, Stromschlaggefahr! Der CO₂-Löscher ist die richtige Wahl.',
'Falsch. Bei Elektrobränden darf kein Wasser verwendet werden (Stromschlaggefahr). Ein CO₂-Löscher ist hier das richtige Löschmittel.'
],
bs5q1: [
'Richtig! Die Feuerlöscher befinden sich im Vorraum am seitlichen Ausgang, im Treppenhaus 1. OG und im hinteren Kirchenanbau neben der Toilette.',
'Falsch: Nicht ganz. Die korrekten Standorte sind: Vorraum am seitlichen Ausgang, Treppenhaus 1. OG und hinterer Kirchenanbau neben der Toilette.'
],
bs5q2: [
'Richtig! Alle drei Ausgänge stehen zur Verfügung: Haupteingang, hinterer Kirchenanbau und seitlicher Eingang. Achte auf die Stufen und den Gegenverkehr von der Empore!',
'Falsch: Nicht ganz. Die korrekten Rettungswege sind: Haupteingang (Stufen!), hinterer Kirchenanbau (Stufen!) und seitlicher Eingang (Emporenverkehr!). Fenster sind keine Rettungswege.'
],
bs5q3: [
'Richtig! Wir haben keine Brandmeldeanlage. Deshalb ist es besonders wichtig, dass alle Anwesenden wachsam sind und die Abläufe kennen.',
'Falsch. Wir haben keine Brandmeldeanlage, daher müssen wir uns umso mehr auf geschultes Verhalten und aufmerksame Personen verlassen.'
],
bs5q4: [
'Richtig! Die Adresse für den Notruf findest du innen an der rechten Tür des Haupteingangs. Merke dir diesen Ort!',
'Falsch. Die Adresse befindet sich innen an der rechten Tür des Haupteingangs (Fluchttür), nicht auf dem Altar oder im Treppenhaus.'
],
bs5q5: [
'Richtig! Unsere Kirchengemeinde fasst 180 bis 200 Personen.',
'Falsch. Unsere Kirchengemeinde fasst 180 bis 200 Personen, nicht mehr.'
],
bs5q6: [
'Richtig! Die Gefahrenstellen sind: Stufen zum Anbau, Stufen am Haupteingang und das Treppenhaus an der Empore (Gegenverkehr).',
'Falsch: Nicht ganz. Die Gefahrenstellen sind: Stufen zwischen Kirchenschiff und Anbau, Stufen vor dem Haupteingang und das Treppenhaus an der Empore. Die Rampe ist keine Gefahrenstelle.'
],
bs5q7: [
'Richtig! Die zuständige vBsU koordiniert, eine klare Zuständigkeit verhindert Chaos und widersprüchliche Anweisungen.',
'Falsch. Nur die zuständige vBsU koordiniert die Aktionen. Das verhindert widersprüchliche Anweisungen und sorgt für eine geordnete Evakuierung.'
],
bs5q8: [
'Richtig! Notleuchten befinden sich an sechs Standorten: Sakristei, unterm Altar, seitliche Klappen, Empore, Eltern-Kind-Raum und Vorraum.',
'Falsch: Nicht ganz. Die Notleuchten befinden sich an: Sakristei, unterm Altar, letzte seitliche Klappen im Kirchenschiff, Empore (Tisch), Eltern-Kind-Raum (Schrank unter TV) und „Diakonie-Schrank" im Vorraum.'
],
bs5q9: [
'Richtig! Der vorbereitete Text wird vom Pult aus verlesen, so erreicht die Information alle Anwesenden gleichzeitig und klar verständlich.',
'Falsch. Die Räumung wird eingeleitet, indem der vorbereitete Text „zur Räumung des Gebäudes" vom Pult aus verlesen wird (rotes, laminiertes DIN-A4-Blatt). So wird sichergestellt, dass alle die gleiche, klare Ansage hören.'
],
bs5q10: [
'Richtig! Anwesende Ersthelfer*innen übernehmen die weitere Erste Hilfe. Die vBsU koordiniert die Evakuierung, nicht die medizinische Versorgung.',
'Falsch. Für die weitere Erste Hilfe sind die anwesenden Ersthelfer*innen zuständig, nicht die vBsU. Für weitergehende Erste Hilfe ist eine gesonderte Schulung notwendig. Die erste Hilfeleistung ist natürlich von jedem erwünscht und gesetzlich vorgeschrieben.'
]
};
function bsGetQuizGate(quizId) {
return bsQuizGates.find(function(gate) {
return gate.quizIds.includes(quizId);
}) || null;
}
function bsGetAllQuizIds() {
return bsQuizGates.reduce(function(ids, gate) { return ids.concat(gate.quizIds); }, []);
}
function bsRenderQuizResult(quizId) {
if (quizId === 'fill2') {
return bsCheckFill(['fill2_1', 'fill2_2', 'fill2_3']);
}
if (quizId === 'match3') {
return bsCheckDropdownMatch();
}
var messages = bsQuizMessages[quizId] || ['Richtig!', 'Falsch: Leider falsch. Die richtige Antwort ist markiert.'];
return bsCheckMultiGeneric(quizId, messages[0], messages[1]);
}
function bsGetScoreStats() {
var allIds = bsGetAllQuizIds();
var total = allIds.length;
var correct = 0;
var wrong = 0;
var open = 0;
allIds.forEach(function(id) {
if (bsQuizCorrect[id] === true) correct++;
else if (bsQuizCorrect[id] === false) wrong++;
else open++;
});
var percent = total > 0 ? Math.round((correct / total) * 100) : 0;
return { correct: correct, wrong: wrong, open: open, total: total, percent: percent };
}
function bsShowSectionScore(completedSection, nextAction) {
var stats = bsGetScoreStats();
var answered = stats.correct + stats.wrong;
var maxReachable = stats.total > 0 ? 100 - Math.round((stats.wrong / stats.total) * 100) : 100;
var scoreEl = document.getElementById('bsSectionScore');
var body = scoreEl.querySelector('.bs-body');
var barColor = maxReachable >= 70 ? 'var(--bs-green)' : 'var(--bs-red)';
body.innerHTML =
'<div class="bs-chapter-title">Zwischenstand nach Säule ' + completedSection + '</div>' +
'<div class="bs-score-bar-wrap">' +
'<div class="bs-score-bar"><div class="bs-score-bar-fill" style="width:' + maxReachable + '%;background:' + barColor + '"></div></div>' +
'<div class="bs-score-bar-label">Max. erreichbar: ' + maxReachable + '% (mindestens 70% erforderlich)</div>' +
'</div>' +
'<div class="bs-score-grid">' +
'<div class="bs-score-card bs-score-correct"><div class="bs-score-card-num">' + stats.correct + '</div><div class="bs-score-card-label">Richtig</div></div>' +
'<div class="bs-score-card bs-score-wrong"><div class="bs-score-card-num">' + stats.wrong + '</div><div class="bs-score-card-label">Falsch</div></div>' +
'<div class="bs-score-card bs-score-open"><div class="bs-score-card-num">' + stats.open + '</div><div class="bs-score-card-label">Offen</div></div>' +
'<div class="bs-score-card"><div class="bs-score-card-num">' + answered + '/' + stats.total + '</div><div class="bs-score-card-label">Gesamt</div></div>' +
'</div>';
var actionsRight = scoreEl.querySelector('.bs-actions-right');
actionsRight.innerHTML = '';
if (typeof nextAction === 'function') {
var btn = document.createElement('button');
btn.type = 'button';
btn.className = 'bs-btn bs-btn-primary';
btn.textContent = completedSection < 5 ? 'Weiter zu Säule ' + (completedSection + 1) + ' >' : 'Zum Ergebnis >';
btn.onclick = nextAction;
actionsRight.appendChild(btn);
}
var actionsLeft = scoreEl.querySelector('.bs-actions-left');
actionsLeft.innerHTML = '<button type="button" class="bs-btn" onclick="bsGoToSection(' + completedSection + ')">< Zurück zu Säule ' + completedSection + '</button>';
for (var i = 0; i < 6; i++) {
var el = document.getElementById('bsSection' + i);
if (el) el.style.display = 'none';
}
scoreEl.style.display = '';
bsScrollToTop();
}
function bsFinalResultAction() {
document.getElementById('bsSectionScore').style.display = 'none';
bsShowFinalResult();
document.getElementById('bsSection5').style.display = '';
bsCurrentSub[5] = 5;
bsShowSubStep(5, 5);
bsScrollToTop();
}
function bsShowFinalResult() {
var stats = bsGetScoreStats();
var passed = stats.percent >= 70;
var section = document.getElementById('bsSub5_5');
var body = section.querySelector('.bs-body');
var barColor = passed ? 'var(--bs-green)' : 'var(--bs-red)';
var html = '<div class="bs-chapter-title">' + (passed ? 'Schulung bestanden!' : 'Schulung nicht bestanden') + '</div>';
html += '<div class="bs-score-bar-wrap">' +
'<div class="bs-score-bar"><div class="bs-score-bar-fill" style="width:' + stats.percent + '%;background:' + barColor + '"></div></div>' +
'<div class="bs-score-bar-label">' + stats.percent + '% richtig (' + stats.correct + ' von ' + stats.total + ' Fragen)</div>' +
'</div>';
html += '<div class="bs-score-grid">' +
'<div class="bs-score-card bs-score-correct"><div class="bs-score-card-num">' + stats.correct + '</div><div class="bs-score-card-label">Richtig</div></div>' +
'<div class="bs-score-card bs-score-wrong"><div class="bs-score-card-num">' + stats.wrong + '</div><div class="bs-score-card-label">Falsch</div></div>' +
'</div>';
if (passed) {
html += '<div class="bs-successbox" style="margin-top:20px;">' +
'<strong>Herzlichen Glückwunsch!</strong> Du hast die Brandschutzunterweisung erfolgreich absolviert. ' +
'Du kennst jetzt die Grundlagen der Brandverhütung, weißt, wie du dich im Brandfall verhältst, ' +
'kennst die Aufgaben der vBsU, kannst einen Feuerlöscher richtig einsetzen und weißt, wo sich die wichtigsten ' +
'Einrichtungen in unserer Kirchengemeinde befinden.' +
'</div>';
html += '<div style="margin-top:24px;padding:20px;background:var(--gray-50);border:1px solid var(--gray-200);">' +
'<label style="display:flex;align-items:flex-start;gap:12px;cursor:pointer;">' +
'<input type="checkbox" id="bsConfirmCheckbox" onchange="bsToggleConfirm()" style="width:20px;height:20px;margin-top:2px;">' +
'<span style="font-size:var(--font-size-sm);color:var(--gray-800);line-height:1.6;">' +
'Ich bestätige, dass ich die Inhalte der <a href="/intranet_repo/projects/brandschutz/Brandschutzordnung.pdf" target="_blank" onclick="event.stopPropagation();">Brandschutzordnung</a> verstanden und verinnerlicht habe. ' +
'Ich bin bereit, die Aufgaben als vBsU zu übernehmen, sofern ich dazu eingeteilt werde.' +
'</span>' +
'</label>' +
'</div>';
html += '<button type="button" class="bs-btn bs-btn-primary bs-btn-locked" id="bsFinishBtn" onclick="bsFinishTraining()" style="margin-top:24px;width:100%;padding:16px;font-size:var(--font-size-md);">Schulung abschließen</button>';
} else {
html += '<div class="bs-warnbox" style="margin-top:20px;">' +
'<div class="bs-warnbox-title">Leider nicht bestanden</div>' +
'Du hast ' + stats.percent + '% der Fragen richtig beantwortet. Zum Bestehen sind mindestens 70% erforderlich. ' +
'Bitte wiederhole die Schulung, um alle Inhalte zu verinnerlichen.' +
'</div>';
html += '<button type="button" class="bs-btn bs-btn-primary" onclick="bsRestartTraining()" style="margin-top:24px;width:100%;padding:16px;font-size:var(--font-size-md);">Schulung neu starten</button>';
}
body.innerHTML = html;
}
function bsSetQuizButtonState(quizId, state) {
var btn = document.getElementById('bsCheck_' + quizId);
if (!btn) return;
btn.classList.remove('bs-btn-success', 'bs-btn-failed', 'bs-btn-check-disabled');
if (state === 'passed') {
btn.textContent = 'Richtig';
btn.classList.add('bs-btn-success', 'bs-btn-check-disabled');
btn.onclick = null;
return;
}
if (state === 'failed') {
btn.textContent = 'Falsch';
btn.classList.add('bs-btn-failed', 'bs-btn-check-disabled');
btn.onclick = null;
return;
}
btn.textContent = 'Antwort prüfen';
btn.onclick = function() { bsCheckIndividualQuiz(quizId); };
}
function bsUpdatePageGate(nextBtnId, quizIds) {
var nextBtn = document.getElementById(nextBtnId);
if (!nextBtn) return;
var allPassed = quizIds.every(function(id) { return !!bsQuizPassed[id]; });
nextBtn.classList.toggle('bs-btn-locked', !allPassed);
}
/* Validate quiz without marking options */
function bsValidateQuiz(quizId) {
var quiz = document.getElementById(quizId);
if (!quiz) return false;
// Checkbox-based quizzes
var cbOptions = quiz.querySelectorAll('.bs-quiz-option input[type="checkbox"]');
if (cbOptions.length > 0) {
var ok = true;
cbOptions.forEach(function(cb) {
var isCorrect = cb.dataset.correct === 'true';
if ((isCorrect && !cb.checked) || (!isCorrect && cb.checked)) ok = false;
});
return ok;
}
// Fill-in-the-blank
var fills = quiz.querySelectorAll('.bs-fill-input');
if (fills.length > 0) {
var ok = true;
fills.forEach(function(input) {
if (input.value.toLowerCase().trim() !== input.dataset.answer.toLowerCase().trim()) ok = false;
});
return ok;
}
// Dropdown matching
var selects = quiz.querySelectorAll('.bs-dropdown-match-select');
if (selects.length > 0) {
var ok = true;
selects.forEach(function(sel) {
if (sel.value !== sel.dataset.correct) ok = false;
});
return ok;
}
return false;
}
function bsCheckIndividualQuiz(quizId) {
var gate = bsGetQuizGate(quizId);
if (!gate) return;
if (!bsQuizAttempts[quizId]) bsQuizAttempts[quizId] = 0;
bsQuizAttempts[quizId]++;
var attempt = bsQuizAttempts[quizId];
var passed = bsValidateQuiz(quizId);
if (passed) {
// Correct — show markings and lock
bsRenderQuizResult(quizId);
bsQuizPassed[quizId] = true;
bsQuizCorrect[quizId] = true;
bsSetQuizButtonState(quizId, 'passed');
document.getElementById(quizId).classList.add('bs-quiz-locked');
} else if (attempt >= 3) {
// Third wrong attempt — show solution, lock as failed
bsRenderQuizResult(quizId);
bsQuizPassed[quizId] = true; // done (failed but completed)
bsQuizCorrect[quizId] = false;
bsSetQuizButtonState(quizId, 'failed');
document.getElementById(quizId).classList.add('bs-quiz-locked');
} else {
// Wrong but retries left — show hint, keep selections
var remaining = 3 - attempt;
var fb = document.getElementById(quizId + '_fb');
if (fb) {
fb.classList.remove('correct', 'wrong');
fb.classList.add('show', 'wrong');
fb.textContent = 'Leider falsch. Du hast noch ' + remaining + (remaining === 1 ? ' Versuch.' : ' Versuche.');
}
}
bsUpdatePageGate(gate.nextBtnId, gate.quizIds);
}
function bsResetQuizContainer(containerId) {
var container = document.getElementById(containerId);
if (!container) return;
container.querySelectorAll('input[type="checkbox"]').forEach(function(input) {
input.checked = false;
});
container.querySelectorAll('.bs-quiz-option').forEach(function(opt) {
opt.classList.remove('correct', 'wrong', 'selected');
});
container.querySelectorAll('select').forEach(function(select) {
select.selectedIndex = 0;
});
container.querySelectorAll('.bs-dropdown-match-row').forEach(function(row) {
row.classList.remove('correct', 'wrong');
});
container.querySelectorAll('input[type="text"]').forEach(function(input) {
input.value = '';
input.classList.remove('correct', 'wrong');
});
container.querySelectorAll('.bs-quiz-feedback, .bs-feedback').forEach(function(fb) {
fb.classList.remove('show', 'correct', 'wrong');
fb.textContent = '';
});
}
function bsUpdateChecklistNext(checklistId, buttonId) {
const checklist = document.getElementById(checklistId);
const button = document.getElementById(buttonId);
if (!checklist || !button) return;
const items = checklist.querySelectorAll('li');
const checkedItems = checklist.querySelectorAll('li.checked');
const allChecked = items.length > 0 && items.length === checkedItems.length;
button.classList.toggle('bs-btn-locked', !allChecked);
}
function bsRestartTraining() {
bsQuizPassed = {};
bsQuizAttempts = {};
bsQuizCorrect = {};
bsGetAllQuizIds().forEach(function(id) {
bsResetQuizContainer(id);
bsSetQuizButtonState(id, 'default');
var quiz = document.getElementById(id);
if (quiz) quiz.classList.remove('bs-quiz-locked');
});
['bsNextS1Q', 'bsNextS2Q', 'bsNextS3Q', 'bsNextS4Q', 'bsNextS5P1', 'bsNextS5P2'].forEach(function(id) {
var btn = document.getElementById(id);
if (btn) btn.classList.add('bs-btn-locked');
});
var checklist = document.getElementById('bsChecklist3_2');
if (checklist) {
checklist.querySelectorAll('li.checked').forEach(function(li) { li.classList.remove('checked'); });
var clBtn = document.getElementById('bsNextSub3_2');
if (clBtn) clBtn.classList.add('bs-btn-locked');
}
bsGoToSection(0);
}
/* ══════════════════════════════════════
ABSCHLUSS, BESTÄTIGUNG
══════════════════════════════════════ */
function bsToggleConfirm() {
var checkbox = document.getElementById('bsConfirmCheckbox');
var finishBtn = document.getElementById('bsFinishBtn');
if (checkbox.checked) {
finishBtn.classList.remove('bs-btn-locked');
} else {
finishBtn.classList.add('bs-btn-locked');
}
}
function bsFinishTraining() {
var checkbox = document.getElementById('bsConfirmCheckbox');
var form = document.getElementById('bsCompleteForm');
if (!checkbox || !checkbox.checked || !form) return;
form.submit();
}
/* ══════════════════════════════════════
SHUFFLE QUIZ OPTIONS ON LOAD
══════════════════════════════════════ */
(function() {
var quizBlocks = document.querySelectorAll('.bs-quiz-options');
quizBlocks.forEach(function(container) {
var options = Array.from(container.querySelectorAll(':scope > .bs-quiz-option'));
if (options.length < 2) return;
// Fisher-Yates shuffle
for (var i = options.length - 1; i > 0; i--) {
var j = Math.floor(Math.random() * (i + 1));
var temp = options[i];
options[i] = options[j];
options[j] = temp;
}
options.forEach(function(opt) { container.appendChild(opt); });
});
})();
@@ -0,0 +1,47 @@
(function () {
function switchTab(tabName, updateHash) {
const tab = document.querySelector('.content-tab-bar__item[data-tab="' + tabName + '"]');
const content = document.getElementById('tab-' + tabName);
if (!tab || !content) return;
document.querySelectorAll('.content-tab-bar__item').forEach(function (item) {
item.classList.toggle('active', item === tab);
});
document.querySelectorAll('.sb-tab-content').forEach(function (item) {
item.classList.toggle('active', item === content);
});
if (updateHash) {
history.replaceState(null, '', '#' + tabName);
}
}
document.addEventListener('DOMContentLoaded', function () {
document.querySelectorAll('.content-tab-bar__item[data-tab]').forEach(function (tab) {
tab.addEventListener('click', function () {
switchTab(tab.dataset.tab, true);
});
});
const hash = window.location.hash.slice(1);
if (hash) {
switchTab(hash, false);
}
document.querySelectorAll('[data-confirm]').forEach(function (button) {
button.addEventListener('click', function (event) {
if (!window.confirm(button.dataset.confirm || 'Wirklich fortfahren?')) {
event.preventDefault();
}
});
});
document.querySelectorAll('[data-upload-form]').forEach(function (form) {
form.addEventListener('submit', function () {
form.classList.add('sb-uploading');
const button = form.querySelector('.sb-upload-button');
if (button) button.disabled = true;
});
});
});
})();
@@ -169,17 +169,13 @@ function showHideText(element) {
// Per Taste Overlay verlassen
document.addEventListener('keydown', function(event) {
const profileVisitOverlay = document.getElementById('profile_visit_overlay');
const profileCalendarOverlay = document.getElementById('profile_calendar_overlay');
const seelsorgeProfileOverlay = document.getElementById('profile_seelsorge_overlay');
const addNewUser = document.getElementById('addNewUser');
const showMessage = document.getElementById('showMessage');
if (event.key === 'Escape') {
if (profileVisitOverlay && profileVisitOverlay.style.display === 'flex') {
openCloseProfile(0, 'profile_visit', 'profile_visit_overlay');
}
if (profileCalendarOverlay && profileCalendarOverlay.style.display === 'flex') {
openCloseProfile(0, 'profile_calendar', 'profile_calendar_overlay');
if (seelsorgeProfileOverlay && seelsorgeProfileOverlay.style.display === 'flex') {
openCloseProfile(0, 'profile_seelsorge', 'profile_seelsorge_overlay');
}
if (addNewUser && addNewUser.style.display === 'flex') {
openCloseOverlay('addNewUser');
@@ -0,0 +1,173 @@
(function () {
'use strict';
var currentStep = 1;
function byId(id) {
return document.getElementById(id);
}
function selectedGroup(form) {
return form.querySelector('input[name="group"]:checked');
}
function showAlert(message) {
window.alert(message);
return false;
}
function validateStepOne(form) {
var selected = selectedGroup(form);
if (!selected) {
return showAlert('Bitte wählen Sie eine Terminart aus.');
}
if (selected.dataset.kalliCustom === '1' && !byId('kalliUserInput').value.trim()) {
return showAlert('Bitte geben Sie einen Titel für den benutzerdefinierten Termin ein.');
}
return true;
}
function validateStepTwo() {
var dateStart = byId('kalliDateS').value;
var dateEnd = byId('kalliDateE').value;
var timeStart = byId('kalliTimeS').value;
var timeEnd = byId('kalliTimeE').value;
if (!dateStart) {
return showAlert('Bitte geben Sie ein Datum ein.');
}
if (dateEnd && dateEnd < dateStart) {
return showAlert('Das Enddatum darf nicht vor dem Startdatum liegen.');
}
if (timeStart && timeEnd && (!dateEnd || dateEnd === dateStart) && timeEnd < timeStart) {
return showAlert('Die Endzeit darf am selben Tag nicht vor der Startzeit liegen.');
}
return true;
}
function updateProgress(step) {
document.querySelectorAll('.kalli-progress-step').forEach(function (element, index) {
var elementStep = index + 1;
element.classList.toggle('active', elementStep === step);
element.classList.toggle('done', elementStep < step);
});
document.querySelectorAll('.kalli-progress-line').forEach(function (element, index) {
element.classList.toggle('done', index < step - 1);
});
}
function showHeader() {
var rootStyle = getComputedStyle(document.documentElement);
var headerHeight = parseFloat(rootStyle.getPropertyValue('--header-height')) || 64;
var menu = byId('header_menue');
var title = byId('header_title');
if (menu) menu.style.top = '0';
if (title) title.style.top = headerHeight + 'px';
}
function goToStep(step) {
for (var index = 1; index <= 4; index++) {
byId('kalliStep' + index).hidden = index !== step;
}
currentStep = step;
updateProgress(step);
window.scrollTo({ top: 0, behavior: 'smooth' });
showHeader();
}
function formatDate(isoDate) {
if (!isoDate) return '–';
var parts = isoDate.split('-');
return parts.length === 3 ? parts[2] + '.' + parts[1] + '.' + parts[0] : isoDate;
}
function showSummaryRow(rowId, valueId, value, displayValue) {
var row = byId(rowId);
row.hidden = !value;
if (value) byId(valueId).textContent = displayValue;
}
function buildSummary(form) {
var selected = selectedGroup(form);
var groupLabel = selected ? selected.value : '–';
if (selected && selected.dataset.kalliCustom === '1') {
var customTitle = byId('kalliUserInput').value.trim();
var visibility = selected.value === 'oeffentlich' ? 'Öffentlich' : 'Intern';
groupLabel = customTitle ? customTitle + ' (' + visibility + ')' : groupLabel;
}
byId('sumGroup').textContent = groupLabel;
byId('sumDateS').textContent = formatDate(byId('kalliDateS').value);
showSummaryRow('sumDateERow', 'sumDateE', byId('kalliDateE').value, formatDate(byId('kalliDateE').value));
showSummaryRow('sumTimeSRow', 'sumTimeS', byId('kalliTimeS').value, byId('kalliTimeS').value);
showSummaryRow('sumTimeERow', 'sumTimeE', byId('kalliTimeE').value, byId('kalliTimeE').value);
showSummaryRow('sumTextRow', 'sumText', byId('kalliFurtherText').value.trim(), byId('kalliFurtherText').value.trim());
showSummaryRow('sumLocRow', 'sumLoc', byId('kalliLocation').value.trim(), byId('kalliLocation').value.trim());
showSummaryRow('sumRespRow', 'sumResp', byId('kalliResponsibility').value.trim(), byId('kalliResponsibility').value.trim());
}
function validateForStep(form, nextStep) {
if (nextStep === 2) return validateStepOne(form);
if (nextStep === 3) return validateStepTwo();
if (nextStep === 4) {
if (!validateStepOne(form) || !validateStepTwo()) return false;
buildSummary(form);
}
return true;
}
function initialise() {
var form = byId('kalliForm');
if (!form) return;
form.querySelectorAll('[data-kalli-next]').forEach(function (button) {
button.addEventListener('click', function () {
var nextStep = Number(button.dataset.kalliNext);
if (validateForStep(form, nextStep)) goToStep(nextStep);
});
});
form.querySelectorAll('[data-kalli-prev]').forEach(function (button) {
button.addEventListener('click', function () {
goToStep(Number(button.dataset.kalliPrev));
});
});
form.addEventListener('submit', function (event) {
if (currentStep !== 4) {
event.preventDefault();
var nextStep = Math.min(currentStep + 1, 4);
if (validateForStep(form, nextStep)) goToStep(nextStep);
return;
}
if (!validateStepOne(form)) {
event.preventDefault();
goToStep(1);
return;
}
if (!validateStepTwo()) {
event.preventDefault();
goToStep(2);
return;
}
var submitButton = form.querySelector('button[type="submit"]');
if (submitButton) submitButton.disabled = true;
});
updateProgress(1);
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initialise);
} else {
initialise();
}
}());
@@ -38,6 +38,11 @@ document.addEventListener('DOMContentLoaded', function() {
// Zeige den entsprechenden Inhalt an
var contentId = item.getAttribute('data-target');
document.getElementById(contentId).classList.add('active');
const url = new URL(window.location.href);
if (contentId === 'content_visit') url.searchParams.set('tab', 'visit');
else url.searchParams.delete('tab');
window.history.replaceState(null, '', url);
});
});
@@ -68,130 +73,108 @@ function switchCalendar(type) {
}
// Tages-Modal oeffnen
async function openDayModal(calendarDate) {
if (!calendarDate) return;
let _seelsorgeConfig = null;
function getSeelsorgeConfig() {
if (_seelsorgeConfig) return _seelsorgeConfig;
const element = document.getElementById('seelsorge-config');
_seelsorgeConfig = element ? JSON.parse(element.textContent) : { csrfToken: '' };
return _seelsorgeConfig;
}
async function loadCalendarDay(calendarDate, action = 'view') {
if (!calendarDate) return null;
const modal = document.getElementById('seelsorge-day-modal');
const body = document.getElementById('modal-day-body');
const title = document.getElementById('modal-day-title');
const subtitle = document.getElementById('modal-day-subtitle');
if (!modal || !body || !title || !subtitle) return null;
if (!modal || !body || !title || !subtitle) {
console.error('openDayModal: Modal-Elemente nicht gefunden.');
return;
if (action === 'view') {
title.textContent = 'Laden...';
subtitle.innerHTML = '';
}
// Modal sofort anzeigen mit Ladezustand
title.textContent = 'Laden...';
subtitle.innerHTML = '';
body.innerHTML = '<div class="seelsorge-modal-loading"><div class="loading-spinner"></div></div>';
body.dataset.calendarDate = calendarDate;
modal.style.display = 'flex';
try {
const response = await fetch('content/calendar_day.php', {
const url = 'content/calendar_day.php?date=' + encodeURIComponent(calendarDate);
const options = action === 'toggle'
? {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: 'calendarDate=' + encodeURIComponent(calendarDate)
});
const html = await response.text();
if (response.ok) {
body.innerHTML = html;
// Metadaten aus dem eingebetteten JSON lesen
const metaEl = body.querySelector('#modal-meta');
if (metaEl) {
const meta = JSON.parse(metaEl.textContent);
title.textContent = meta.title;
let chips = '<span class="seelsorge-modal-chip">KW ' + meta.kw + '</span>';
if (meta.feiertag) {
chips += '<span class="seelsorge-modal-chip seelsorge-chip-holiday">' + meta.feiertag + '</span>';
}
subtitle.innerHTML = chips;
metaEl.remove();
}
} else {
// PHP-Fehler direkt im Modal anzeigen
title.textContent = 'Fehler ' + response.status;
body.innerHTML = '<div style="padding:16px;font-family:monospace;font-size:0.85em;color:#d21400;white-space:pre-wrap;word-break:break-word;background:#fff5f5;border:1px solid #fcc;">'
+ (html || 'Keine Antwort vom Server.') + '</div>';
body: new URLSearchParams({
date: calendarDate,
action: 'toggle',
csrf_token: getSeelsorgeConfig().csrfToken
})
}
: { method: 'GET' };
try {
const response = await fetch(url, options);
const html = await response.text();
if (!response.ok) {
throw new Error(html || ('HTTP Fehler: ' + response.status));
}
body.innerHTML = html;
const metaEl = body.querySelector('#modal-meta');
if (!metaEl) throw new Error('Tagesdaten sind unvollständig.');
const meta = JSON.parse(metaEl.textContent);
title.textContent = meta.title;
let chips = '<span class="seelsorge-modal-chip">KW ' + meta.kw + '</span>';
if (meta.feiertag) {
chips += '<span class="seelsorge-modal-chip seelsorge-chip-holiday">' + escHtml(meta.feiertag) + '</span>';
}
subtitle.innerHTML = chips;
metaEl.remove();
return meta;
} catch (error) {
body.innerHTML = '<div class="seelsorge-modal-empty">Fehler beim Laden der Daten: ' + error.message + '</div>';
title.textContent = 'Fehler';
body.innerHTML = '<div class="seelsorge-modal-empty">' + escHtml(error.message || 'Fehler beim Laden der Daten.') + '</div>';
console.error('Fehler beim Laden der Tagesdetails:', error);
return null;
}
}
function openDayModal(calendarDate) {
return loadCalendarDay(calendarDate, 'view');
}
// Modal schliessen
function closeDayModal() {
const modal = document.getElementById('seelsorge-day-modal');
if (modal) modal.style.display = 'none';
}
// Backdrop-Klick schliesst Modal
function closeDayModalOnBackdrop(event) {
if (event.target === event.currentTarget) {
closeDayModal();
}
if (event.target === event.currentTarget) closeDayModal();
}
// Verfuegbarkeit im Modal togglen (Modal bleibt offen)
async function toggleAvailabilityModal(date) {
const btn = document.getElementById('modal-toggle-btn');
if (!btn) return;
const button = document.getElementById('modal-toggle-btn');
if (button) button.disabled = true;
const originalText = btn.textContent;
btn.disabled = true;
btn.textContent = 'Wird gespeichert\u2026';
try {
const response = await fetch("toggle_availability.php", {
method: "POST",
headers: { "Content-type": "application/x-www-form-urlencoded" },
body: "date=" + encodeURIComponent(date)
});
const data = await response.json();
if (data.ok) {
// Modal-Inhalt neu laden um aktuellen Stand zu zeigen
await openDayModal(date);
// Kalender-Tageszelle visuell aktualisieren
updateCalendarCell(date, data.nowAvailable, data.availCount);
} else {
btn.textContent = originalText;
btn.disabled = false;
console.error("Fehler:", data.error);
}
} catch (error) {
btn.textContent = originalText;
btn.disabled = false;
console.error("Fehler beim Aktualisieren der Verfuegbarkeit:", error);
}
const meta = await loadCalendarDay(date, 'toggle');
if (meta) updateCalendarCell(date, Number(meta.availCount) || 0);
}
// Kalender-Tageszelle visuell aktualisieren ohne Page-Reload
function updateCalendarCell(date, nowAvailable, availCount) {
var cells = document.querySelectorAll('#calendar_day_item');
function updateCalendarCell(date, availCount) {
const cells = document.querySelectorAll(`[data-calendar-date="${date}"]`);
cells.forEach(function(cell) {
var onclick = cell.getAttribute('onclick') || '';
if (onclick.indexOf(date) === -1) return;
// Alle Verfuegbarkeits-Klassen entfernen
cell.classList.remove('available_single', 'available_multiple', 'normalday');
cell.classList.remove(
'available_single', 'available_multiple', 'normalday',
'today', 'sunday', 'localholiday', 'saturday'
);
if (availCount >= 2) {
cell.classList.add('available_multiple');
} else if (availCount === 1) {
cell.classList.add('available_single');
} else {
cell.classList.add('normalday');
cell.classList.add(cell.dataset.emptyClass || 'normalday');
}
});
}
@@ -201,26 +184,16 @@ function updateCalendarCell(date, nowAvailable, availCount) {
var _lastCalendarDate = null;
function openCalendarProfile(userId) {
// Datum merken, damit wir beim Schliessen zurueckkehren koennen
var metaEl = document.getElementById('modal-meta');
var bodyEl = document.getElementById('modal-day-body');
if (!_lastCalendarDate && bodyEl) {
// Datum aus dem Toggle-Button extrahieren
var toggleBtn = bodyEl.querySelector('#modal-toggle-btn');
if (toggleBtn) {
var onclickAttr = toggleBtn.getAttribute('onclick') || '';
var match = onclickAttr.match(/'(\d{4}-\d{2}-\d{2})'/);
if (match) _lastCalendarDate = match[1];
}
}
const body = document.getElementById('modal-day-body');
_lastCalendarDate = body?.dataset.calendarDate || null;
closeDayModal();
openCloseProfile(userId, 'profile_calendar', 'profile_calendar_overlay');
openCloseProfile(userId, 'profile_seelsorge', 'profile_seelsorge_overlay');
}
// Globalen Listener fuer Kalender-Profil-Overlay: bei Schliessen Tagesmodal wieder oeffnen
document.addEventListener('DOMContentLoaded', function() {
// MutationObserver auf das Kalender-Profil-Overlay
var calOverlay = document.getElementById('profile_calendar_overlay');
var calOverlay = document.getElementById('profile_seelsorge_overlay');
if (calOverlay) {
var observer = new MutationObserver(function(mutations) {
mutations.forEach(function(m) {
@@ -244,4 +217,297 @@ function openCalendarHelp() {
function closeCalendarHelp() {
var modal = document.getElementById('calendar-help-modal');
if (modal) modal.style.display = 'none';
}
}
// Besuchsstellen ------------------------------------------------------------
let _visitConfig = null;
function getVisitConfig() {
if (_visitConfig) return _visitConfig;
const element = document.getElementById('visit-config');
_visitConfig = element ? JSON.parse(element.textContent) : { entries: [], currentUserId: 0 };
return _visitConfig;
}
function openVisitProfile(userId) {
closeHouseholdDetail();
openCloseProfile(userId, 'profile_seelsorge', 'profile_seelsorge_overlay');
}
// --- Detail Modal ---
function openHouseholdDetail(data) {
const overlay = document.getElementById('visitDetailOverlay');
const title = document.getElementById('detailHeroTitle');
const sub = document.getElementById('detailHeroSub');
const body = document.getElementById('detailBody');
const members = data.members || [];
const lastName = members[0]?.last_name || '';
const firstNames = members.map(m => m.first_name).join(' & ');
title.textContent = lastName + ', ' + firstNames;
sub.textContent = data.address_street + ' · ' + data.address_plz;
let html = '';
// Members section
html += '<div class="visit-detail-section-title">Haushaltsmitglieder</div>';
members.forEach(m => {
html += `<div class="visit-detail-member">
<div class="visit-detail-member-info">
${escHtml(m.first_name)} ${escHtml(m.last_name)}
<span class="age">geb. ${escHtml(m.birth_formatted)} (${m.age} Jahre)</span>
</div>
<button class="visit-detail-member-edit" onclick="event.stopPropagation(); editMemberFromDetail(${m.besuch_id})" title="Bearbeiten"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" width="16" height="16"><path d="M3 17.25V21h3.75L17.81 9.94l-3.75-3.75L3 17.25zM20.71 7.04c.39-.39.39-1.02 0-1.41l-2.34-2.34c-.39-.39-1.02-.39-1.41 0l-1.83 1.83 3.75 3.75 1.83-1.83z"/></svg></button>
</div>`;
});
// Info grid
html += '<div class="visit-detail-section-title">Details</div>';
html += '<div class="visit-detail-info-grid">';
html += `<div class="visit-detail-info-item"><div class="visit-detail-info-label">Adresse</div><div class="visit-detail-info-value"><a href="${escHtml(data.address_link)}" target="_blank" style="color:var(--theme-color);text-decoration:none;">${escHtml(data.address_street)}, ${escHtml(data.address_plz)}</a></div></div>`;
const phone = members[0]?.phone || data.phone || '';
const phoneFormatted = phone ? ('0' + phone) : '—';
html += `<div class="visit-detail-info-item"><div class="visit-detail-info-label">Telefon</div><div class="visit-detail-info-value">${phone ? '<a href="tel:0'+escHtml(phone)+'" style="color:var(--theme-color);text-decoration:none;">'+escHtml(phoneFormatted)+'</a>' : '—'}</div></div>`;
const priestName = members[0]?.priest_name || '—';
const priestId = members[0]?.priest_id || 0;
const priestImg = members[0]?.priest_img || '/intranet_repo/general_files/profile_pictures/default.jpg';
if (priestId > 0) {
html += `<div class="visit-detail-info-item"><div class="visit-detail-info-label">Priester</div><div class="visit-detail-info-value">`;
html += `<span style="display:inline-flex;align-items:center;gap:8px;cursor:pointer;" onclick="openVisitProfile(${priestId})">`;
html += `<img src="${escHtml(priestImg)}" alt="" style="width:28px;height:28px;border-radius:50%;object-fit:cover;">`;
html += `<span style="color:var(--theme-color);text-decoration:none;">${escHtml(priestName)}</span>`;
html += `</span></div></div>`;
} else {
html += `<div class="visit-detail-info-item"><div class="visit-detail-info-label">Priester</div><div class="visit-detail-info-value">${escHtml(priestName)}</div></div>`;
}
const comment = members.map(m => m.comment).filter(c => c).join('; ') || '—';
html += `<div class="visit-detail-info-item"><div class="visit-detail-info-label">Hinweis</div><div class="visit-detail-info-value">${escHtml(comment)}</div></div>`;
html += '</div>';
// Reservation info
if (data.reservation) {
html += '<div class="visit-detail-section-title">Geplanter Besuch</div>';
const resName = data.reservation_name || ('Nutzer #' + data.reservation);
const resImg = data.reservation_img || '/intranet_repo/general_files/profile_pictures/default.jpg';
const resId = parseInt(data.reservation);
html += '<div class="visit-detail-info-item" style="grid-column:1/-1;"><div class="visit-detail-info-label">Geplant von</div><div class="visit-detail-info-value">';
html += `<span style="display:inline-flex;align-items:center;gap:8px;cursor:pointer;" onclick="openVisitProfile(${resId})">`;
html += `<img src="${escHtml(resImg)}" alt="" style="width:28px;height:28px;border-radius:50%;object-fit:cover;">`;
html += `<span style="color:var(--theme-color);text-decoration:none;">${escHtml(resName)}</span>`;
html += `</span>`;
html += '</div></div>';
}
// Actions
const firstBesuchId = members[0]?.besuch_id || 0;
html += '<div class="visit-detail-actions">';
if (!data.reservation) {
html += `<button class="visit-action-btn visit-action-btn-primary" onclick="doReservation(${firstBesuchId})">Reservieren</button>`;
} else if (parseInt(data.reservation) === getVisitConfig().currentUserId) {
html += `<button class="visit-action-btn" style="color:hsl(145,55%,34%);border-color:hsl(145,55%,80%);background:hsl(145,55%,96%);" onclick="doComplete(${firstBesuchId})"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" width="16" height="16" style="vertical-align:middle;margin-right:4px;"><path d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z"/></svg>Abschließen</button>`;
}
html += `<button class="visit-action-btn" onclick="doPrioritize(${firstBesuchId})">Priorisieren</button>`;
html += '</div>';
body.innerHTML = html;
overlay.style.display = 'flex';
}
function closeHouseholdDetail() {
document.getElementById('visitDetailOverlay').style.display = 'none';
}
function escHtml(str) {
if (!str) return '';
const div = document.createElement('div');
div.textContent = str;
return div.innerHTML;
}
function editMemberFromDetail(besuchId) {
closeHouseholdDetail();
const entry = getVisitConfig().entries.find(e => e.besuch_id === besuchId);
if (entry) openVisitEditModalFromEntry(entry);
}
function submitVisitAction(action, besuchId) {
const form = document.getElementById('visitActionForm');
const command = document.getElementById('visit_action_command');
const id = document.getElementById('visit_action_id');
if (!form || !command || !id) return;
command.value = action;
id.value = besuchId;
form.submit();
}
function doReservation(besuchId) { submitVisitAction('reserve', besuchId); }
function doComplete(besuchId) { submitVisitAction('complete', besuchId); }
function doPrioritize(besuchId) { submitVisitAction('prioritize', besuchId); }
// --- Picker Modal ---
function openVisitPickerModal() {
renderVisitPickerList('');
document.getElementById('visitPickerModalOverlay').style.display = 'flex';
const inp = document.getElementById('visitPickerSearch');
inp.value = '';
setTimeout(() => inp.focus(), 30);
}
function closeVisitPickerModal() {
document.getElementById('visitPickerModalOverlay').style.display = 'none';
}
function onVisitPickerSearchInput() {
renderVisitPickerList(document.getElementById('visitPickerSearch').value || '');
}
function normalizeStr(s) { return (s || '').toString().toLowerCase(); }
function matchesQuery(entry, query) {
const q = normalizeStr(query).trim();
if (!q) return true;
const ln = normalizeStr(entry.last_name);
const fn = normalizeStr(entry.first_name);
const a = (ln + ' ' + fn).trim();
const b = (fn + ' ' + ln).trim();
const tokens = q.split(/\s+/).filter(Boolean);
return tokens.every(t => a.includes(t) || b.includes(t));
}
function renderVisitPickerList(query) {
const list = document.getElementById('visitPickerList');
list.innerHTML = '';
if (!Array.isArray(getVisitConfig().entries) || getVisitConfig().entries.length === 0) {
list.innerHTML = '<div style="opacity:.75;padding:8px;">Keine Einträge vorhanden.</div>';
return;
}
const filtered = getVisitConfig().entries.filter(e => matchesQuery(e, query));
if (filtered.length === 0) {
list.innerHTML = '<div style="opacity:.75;padding:8px;">Keine Treffer.</div>';
return;
}
filtered.forEach(entry => {
const item = document.createElement('div');
item.className = 'visit-picker-item';
item.onclick = () => { closeVisitPickerModal(); openVisitEditModalFromEntry(entry); };
const left = document.createElement('div');
const name = document.createElement('div');
name.className = 'visit-picker-name';
name.textContent = (entry.last_name || '') + ', ' + (entry.first_name || '');
left.appendChild(name);
const sub = document.createElement('div');
sub.className = 'visit-picker-sub';
sub.textContent = (entry.address_street || '') + (entry.address_plz ? (' · ' + entry.address_plz) : '');
left.appendChild(sub);
const right = document.createElement('div');
right.style.cssText = 'opacity:0.5;font-size:0.8em;font-weight:700;';
right.textContent = '#' + entry.besuch_id;
item.appendChild(left);
item.appendChild(right);
list.appendChild(item);
});
}
// --- Edit Modal ---
const EMPTY_VISIT_ENTRY = {
besuch_id: '',
last_name: '',
first_name: '',
birth_date: '',
address_street: '',
address_plz: '',
address_link: '',
phone: '',
comment: '',
letzter_besuch: '2024-07-11',
priority: 0,
pr_bezirk: ''
};
function openVisitEntryModal(entry = null, errorMessage = '') {
const values = Object.assign({}, EMPTY_VISIT_ENTRY, entry || {});
const isEdit = Number(values.besuch_id) > 0;
document.getElementById('visitModalTitle').innerText = isEdit ? 'Eintrag bearbeiten' : 'Neue Besuchsstelle anlegen';
document.getElementById('modal_besuch_id').value = isEdit ? values.besuch_id : '';
document.getElementById('modal_last_name').value = values.last_name || '';
document.getElementById('modal_first_name').value = values.first_name || '';
document.getElementById('modal_birth_date').value = values.birth_date || '';
document.getElementById('modal_address_street').value = values.address_street || '';
document.getElementById('modal_address_plz').value = values.address_plz || '';
document.getElementById('modal_address_link').value = values.address_link || '';
document.getElementById('modal_phone').value = values.phone || '';
document.getElementById('modal_comment').value = values.comment || '';
document.getElementById('modal_letzter_besuch').value = values.letzter_besuch || EMPTY_VISIT_ENTRY.letzter_besuch;
document.getElementById('modal_priority').value = values.priority ?? 0;
document.getElementById('modal_pr_bezirk').value = values.pr_bezirk ?? '';
document.getElementById('modal_delete_btn').style.display = isEdit ? 'inline-block' : 'none';
const error = document.getElementById('visitFormError');
if (error) {
error.textContent = errorMessage;
error.style.display = errorMessage ? 'block' : 'none';
}
showVisitModal();
}
function openVisitEditModalFromEntry(entry) {
openVisitEntryModal(entry);
}
function openVisitAddModal() {
openVisitEntryModal();
}
function showVisitModal() {
const overlay = document.getElementById('visitEntryModalOverlay');
overlay.style.display = 'flex';
overlay.setAttribute('aria-hidden', 'false');
setTimeout(() => document.getElementById('modal_last_name')?.focus(), 30);
}
function closeVisitModal() {
const overlay = document.getElementById('visitEntryModalOverlay');
if (overlay && overlay.contains(document.activeElement)) document.activeElement.blur();
overlay.setAttribute('aria-hidden', 'true');
overlay.style.display = 'none';
}
// Close on backdrop/escape
document.addEventListener('click', function(e) {
const picker = document.getElementById('visitPickerModalOverlay');
const edit = document.getElementById('visitEntryModalOverlay');
if (picker && picker.style.display === 'flex' && e.target === picker) closeVisitPickerModal();
if (edit && edit.style.display === 'flex' && e.target === edit) closeVisitModal();
});
document.addEventListener('keydown', function(e) {
if (e.key !== 'Escape') return;
const detail = document.getElementById('visitDetailOverlay');
const edit = document.getElementById('visitEntryModalOverlay');
const picker = document.getElementById('visitPickerModalOverlay');
if (edit && edit.style.display === 'flex') return closeVisitModal();
if (picker && picker.style.display === 'flex') return closeVisitPickerModal();
if (detail && detail.style.display === 'flex') return closeHouseholdDetail();
});
function deleteVisitEntryFromModal() {
const besuchId = document.getElementById('modal_besuch_id').value;
if (besuchId && confirm('Eintrag wirklich löschen?')) {
submitVisitAction('delete', besuchId);
}
}
// Server-seitige Validierungsfehler nach Post/Redirect/Get im Formular wieder anzeigen.
document.addEventListener('DOMContentLoaded', function() {
const config = getVisitConfig();
if (config.formError && config.formData) {
openVisitEntryModal(config.formData, config.formError);
} else if (config.formError) {
alert(config.formError);
}
});