Files
intranet/web/intranet_repo/projects/kalli/kalli_modal.php
T

747 lines
22 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!-- Kalli Eintragungsassistent – Mehrstufiges Modal -->
<div class="standard_overlay" id="kalliModal" onclick="if(event.target.id==='kalliModal')closeKalliModal();" style="display:none;position:fixed;top:0;left:0;width:100%;height:100%;justify-content:center;align-items:center;background-color:hsla(210,57%,25%,0.8);z-index:9999;">
<div class="kalli-modal-card">
<!-- Close Button -->
<button type="button" class="kalli-modal-exit" onclick="closeKalliModal()" aria-label="Schließen">&times;</button>
<!-- Hero Header -->
<div class="kalli-modal-hero">
<div>
<div class="kalli-modal-title">Kalli – Eintragungsassistent</div>
<div class="kalli-modal-subtitle">Neuen Termin im CKalender eintragen</div>
</div>
</div>
<!-- Progress Bar -->
<div class="kalli-progress">
<div class="kalli-progress-step active" data-step="1">
<div class="kalli-progress-circle">1</div>
<span class="kalli-progress-label">Terminart</span>
</div>
<div class="kalli-progress-line"></div>
<div class="kalli-progress-step" data-step="2">
<div class="kalli-progress-circle">2</div>
<span class="kalli-progress-label">Zeitraum</span>
</div>
<div class="kalli-progress-line"></div>
<div class="kalli-progress-step" data-step="3">
<div class="kalli-progress-circle">3</div>
<span class="kalli-progress-label">Informationen</span>
</div>
<div class="kalli-progress-line"></div>
<div class="kalli-progress-step" data-step="4">
<div class="kalli-progress-circle">4</div>
<span class="kalli-progress-label">Übersicht</span>
</div>
</div>
<!-- Form wraps all steps -->
<form id="kalliForm" action="/intranet_repo/projects/kalli/kalli.php" method="post">
<!-- STEP 1: Terminart -->
<div class="kalli-step" id="kalliStep1">
<div class="kalli-modal-body">
<div class="kalli-section-title">Terminart auswählen</div>
<div class="kalli-category-grid">
<?php
$isAdminUser = (isset($groups["Admin"]) && $groups["Admin"] == 1);
// Kategorien und Terminarten aus der Datenbank laden
$catQueryModal = $pdo->query("SELECT * FROM kalli_categories ORDER BY sort_order, name");
$kalliCategoriesModal = [];
while ($catM = $catQueryModal->fetch(PDO::FETCH_ASSOC)) {
$kalliCategoriesModal[] = $catM;
}
$typeQueryModal = $pdo->query("SELECT * FROM kalli_appointment_types ORDER BY sort_order, name");
$kalliTypesByCategoryModal = [];
while ($typeM = $typeQueryModal->fetch(PDO::FETCH_ASSOC)) {
$kalliTypesByCategoryModal[$typeM['category_id']][] = $typeM;
}
$kalliAllRolesModal = [];
foreach ($kalliCategoriesModal as $catM):
$roleM = $catM['permission_role'];
$kalliAllRolesModal[$roleM] = 1;
if (!$isAdminUser && !(isset($groups[$roleM]) && $groups[$roleM] == 1)) continue;
$typesM = $kalliTypesByCategoryModal[$catM['id']] ?? [];
if (empty($typesM)) continue;
?>
<div class="kalli-category">
<div class="kalli-category-label"><?= htmlspecialchars($catM['name']) ?></div>
<?php foreach ($typesM as $typeM):
$displayLabelM = !empty($typeM['display_label']) ? $typeM['display_label'] : $typeM['name'];
?>
<label class="kalli-radio-item"><input type="radio" name="group" value="<?= htmlspecialchars($typeM['name']) ?>"><span><?= htmlspecialchars($displayLabelM) ?></span></label>
<?php endforeach; ?>
</div>
<?php endforeach; ?>
</div>
<?php
$hasAnyKalliRoleModal = $isAdminUser || count(array_intersect_key($groups, $kalliAllRolesModal)) > 0;
if ($hasAnyKalliRoleModal): ?>
<div class="kalli-custom-section">
<div class="kalli-category-label" style="color:var(--nak-red);">Terminart nicht aufgelistet?</div>
<p style="margin:0 0 var(--space-sm) 0;font-size:var(--font-size-sm);color:var(--gray-600);">Nur ausfüllen, wenn die Terminart oben nicht vorhanden ist.</p>
<div style="display:flex;gap:var(--space-lg);flex-wrap:wrap;margin-bottom:var(--space-sm);">
<label class="kalli-radio-item"><input type="radio" name="group" value="oeffentlich"><span>Öffentlich</span></label>
<label class="kalli-radio-item"><input type="radio" name="group" value="intern"><span>Intern</span></label>
</div>
<input class="kalli-field" type="text" name="userinput" id="kalliUserInput" placeholder="Titel (nur für benutzerdefinierte Termine)">
</div>
<?php endif; ?>
</div>
<div class="kalli-modal-actions">
<div></div>
<div class="kalli-modal-actions-right">
<button type="button" class="kalli-btn" onclick="closeKalliModal()">Abbrechen</button>
<button type="button" class="kalli-btn kalli-btn-primary" onclick="kalliNextStep(2)">Weiter</button>
</div>
</div>
</div>
<!-- STEP 2: Zeitraum -->
<div class="kalli-step" id="kalliStep2" style="display:none;">
<div class="kalli-modal-body">
<div class="kalli-section-title">Zeitraum festlegen</div>
<div class="kalli-field-grid">
<div class="kalli-field-group">
<label class="kalli-label">Datum <span style="color:var(--nak-red);">*</span></label>
<input class="kalli-field" type="date" name="date_s" id="kalliDateS" required>
</div>
<div class="kalli-field-group">
<label class="kalli-label">Enddatum <span style="font-weight:300;color:var(--gray-500);">(falls mehrtägig)</span></label>
<input class="kalli-field" type="date" name="date_e" id="kalliDateE">
</div>
<div class="kalli-field-group">
<label class="kalli-label">Zeit von <span style="font-weight:300;color:var(--gray-500);">(optional)</span></label>
<input class="kalli-field" type="time" name="time_s" id="kalliTimeS">
</div>
<div class="kalli-field-group">
<label class="kalli-label">Zeit bis <span style="font-weight:300;color:var(--gray-500);">(optional)</span></label>
<input class="kalli-field" type="time" name="time_e" id="kalliTimeE">
</div>
</div>
</div>
<div class="kalli-modal-actions">
<div class="kalli-modal-actions-left">
<button type="button" class="kalli-btn" onclick="kalliPrevStep(1)">Zurück</button>
</div>
<div class="kalli-modal-actions-right">
<button type="button" class="kalli-btn kalli-btn-primary" onclick="kalliNextStep(3)">Weiter</button>
</div>
</div>
</div>
<!-- STEP 3: Informationen -->
<div class="kalli-step" id="kalliStep3" style="display:none;">
<div class="kalli-modal-body">
<div class="kalli-section-title">Zusätzliche Informationen</div>
<div class="kalli-field-grid kalli-field-grid-single">
<div class="kalli-field-group">
<label class="kalli-label">Textfeld <span style="font-weight:300;color:var(--gray-500);">(optional)</span></label>
<input class="kalli-field" type="text" name="further_text" id="kalliFurtherText" placeholder="z.B. betreffende Person, etc.">
</div>
<div class="kalli-field-group">
<label class="kalli-label">Ort <span style="font-weight:300;color:var(--gray-500);">(optional)</span></label>
<input class="kalli-field" type="text" name="location" id="kalliLocation">
</div>
<div class="kalli-field-group">
<label class="kalli-label">Verantwortliche/r <span style="font-weight:300;color:var(--gray-500);">(optional)</span></label>
<input class="kalli-field" type="text" name="responsibility" id="kalliResponsibility">
</div>
</div>
</div>
<div class="kalli-modal-actions">
<div class="kalli-modal-actions-left">
<button type="button" class="kalli-btn" onclick="kalliPrevStep(2)">Zurück</button>
</div>
<div class="kalli-modal-actions-right">
<button type="button" class="kalli-btn kalli-btn-primary" onclick="kalliNextStep(4)">Weiter</button>
</div>
</div>
</div>
<!-- STEP 4: Übersicht -->
<div class="kalli-step" id="kalliStep4" style="display:none;">
<div class="kalli-modal-body">
<div class="kalli-section-title">Übersicht – Bitte prüfen Sie Ihre Eingaben</div>
<div class="kalli-summary">
<div class="kalli-summary-row">
<span class="kalli-summary-label">Terminart</span>
<span class="kalli-summary-value" id="sumGroup">–</span>
</div>
<div class="kalli-summary-row">
<span class="kalli-summary-label">Datum</span>
<span class="kalli-summary-value" id="sumDateS">–</span>
</div>
<div class="kalli-summary-row" id="sumDateERow" style="display:none;">
<span class="kalli-summary-label">Enddatum</span>
<span class="kalli-summary-value" id="sumDateE">–</span>
</div>
<div class="kalli-summary-row" id="sumTimeSRow" style="display:none;">
<span class="kalli-summary-label">Zeit von</span>
<span class="kalli-summary-value" id="sumTimeS">–</span>
</div>
<div class="kalli-summary-row" id="sumTimeERow" style="display:none;">
<span class="kalli-summary-label">Zeit bis</span>
<span class="kalli-summary-value" id="sumTimeE">–</span>
</div>
<div class="kalli-summary-row" id="sumTextRow" style="display:none;">
<span class="kalli-summary-label">Textfeld</span>
<span class="kalli-summary-value" id="sumText">–</span>
</div>
<div class="kalli-summary-row" id="sumLocRow" style="display:none;">
<span class="kalli-summary-label">Ort</span>
<span class="kalli-summary-value" id="sumLoc">–</span>
</div>
<div class="kalli-summary-row" id="sumRespRow" style="display:none;">
<span class="kalli-summary-label">Verantwortlich</span>
<span class="kalli-summary-value" id="sumResp">–</span>
</div>
</div>
<div id="kalliValidationError" class="kalli-validation-error" style="display:none;"></div>
</div>
<div class="kalli-modal-actions">
<div class="kalli-modal-actions-left">
<button type="button" class="kalli-btn" onclick="kalliPrevStep(3)">Zurück</button>
</div>
<div class="kalli-modal-actions-right">
<button type="submit" name="submit" class="kalli-btn kalli-btn-submit">Eintragung absenden</button>
</div>
</div>
</div>
</form>
</div>
</div>
<style>
/* ── Kalli Modal ── */
.kalli-modal-card {
position: relative;
width: min(720px, calc(100vw - 24px));
max-height: min(90vh, calc(100vh - 24px));
overflow-y: auto;
background: #ffffff;
box-shadow: 0 24px 70px rgba(0,0,0,0.36);
}
.kalli-modal-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;
z-index: 1;
}
.kalli-modal-hero {
padding: 24px 28px;
color: #ffffff;
background:
radial-gradient(circle at 10% 0%, rgba(255,255,255,0.24), transparent 32%),
linear-gradient(135deg, var(--nak-blue), var(--nak-blue-hover));
}
.kalli-modal-title {
font-size: 1.35em;
font-weight: 700;
margin-bottom: 4px;
}
.kalli-modal-subtitle {
font-size: var(--font-size-sm);
font-weight: 300;
color: rgba(255,255,255,0.8);
}
/* ── Progress Bar ── */
.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-direction: column;
align-items: center;
gap: 4px;
flex-shrink: 0;
}
.kalli-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;
}
.kalli-progress-step.active .kalli-progress-circle {
background: var(--nak-blue);
border-color: var(--nak-blue);
color: #ffffff;
box-shadow: 0 2px 8px rgba(39,134,207,0.35);
}
.kalli-progress-step.done .kalli-progress-circle {
background: #ffffff;
border-color: var(--nak-blue);
color: var(--nak-blue);
}
.kalli-progress-label {
font-size: var(--font-size-xs);
font-weight: 500;
color: var(--gray-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;
height: 2px;
min-width: 20px;
max-width: 60px;
background: var(--gray-300);
margin: 0 6px;
margin-bottom: 20px;
transition: background 250ms ease;
}
.kalli-progress-line.done {
background: var(--nak-blue);
}
/* ── Modal Body ── */
.kalli-modal-body {
padding: 22px 28px 16px;
}
.kalli-section-title {
margin: 0 0 16px 0;
font-size: var(--font-size-sm);
font-weight: 600;
letter-spacing: 0.04em;
text-transform: uppercase;
color: var(--gray-600);
}
/* ── Category Grid (Step 1) ── */
.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 {
font-size: var(--font-size-sm);
font-weight: 600;
color: var(--gray-700);
margin-bottom: 4px;
text-transform: uppercase;
letter-spacing: 0.03em;
}
label.kalli-radio-item {
display: flex;
align-items: center;
gap: 12px;
padding: 10px 20px;
cursor: pointer;
font-size: var(--font-size-sm);
font-weight: 400;
color: var(--gray-800);
border: 1px solid transparent;
transition: background 150ms ease, border-color 150ms ease;
text-transform: none;
letter-spacing: normal;
}
label.kalli-radio-item input[type="radio"],
label.kalli-radio-item input[type="checkbox"] {
width: 16px;
height: 16px;
accent-color: var(--nak-blue);
margin: 0;
flex-shrink: 0;
align-self: center;
}
label.kalli-radio-item span {
hyphens: auto;
-webkit-hyphens: auto;
-ms-hyphens: auto;
word-break: break-word;
overflow-wrap: break-word;
line-height: 1.4;
}
label.kalli-radio-item:hover {
background: var(--gray-50);
border-color: var(--gray-200);
}
.kalli-custom-section {
margin-top: var(--space-lg);
padding: var(--space-md);
background: hsl(0,60%,98%);
border: 1px solid hsl(0,60%,90%);
}
/* ── Field Grid (Steps 2 & 3) ── */
.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 {
font-size: var(--font-size-sm);
font-weight: 500;
color: var(--gray-700);
}
.kalli-field {
padding: 12px 14px;
font-size: var(--font-size-base);
font-family: var(--font-primary);
font-weight: 300;
border: 1px solid var(--gray-300);
background: var(--gray-50);
outline: none;
transition: border-color 150ms ease, box-shadow 150ms ease;
width: 100%;
}
.kalli-field:focus {
border-color: var(--nak-blue);
box-shadow: 0 0 0 3px var(--nak-blue-20);
background: #ffffff;
}
/* ── Summary (Step 4) ── */
.kalli-summary {
display: flex;
flex-direction: column;
gap: 0;
border: 1px solid var(--gray-200);
}
.kalli-summary-row {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px 16px;
border-bottom: 1px solid var(--gray-100);
}
.kalli-summary-row:last-child {
border-bottom: none;
}
.kalli-summary-label {
font-size: var(--font-size-sm);
font-weight: 500;
color: var(--gray-600);
}
.kalli-summary-value {
font-size: var(--font-size-sm);
font-weight: 500;
color: var(--gray-900);
text-align: right;
}
.kalli-validation-error {
margin-top: var(--space-md);
padding: 10px 14px;
background: hsl(0,78%,95%);
color: hsl(0,78%,40%);
font-weight: 600;
font-size: var(--font-size-sm);
}
/* ── Actions ── */
.kalli-modal-actions {
display: flex;
justify-content: space-between;
align-items: center;
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;
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;
}
.kalli-btn:hover {
background: var(--gray-100);
transform: translateY(-1px);
}
.kalli-btn-primary {
background: var(--nak-blue);
border-color: var(--nak-blue);
color: #ffffff;
}
.kalli-btn-primary:hover {
background: var(--nak-blue-hover);
border-color: var(--nak-blue-hover);
}
.kalli-btn-submit {
background: linear-gradient(135deg, var(--nak-blue), var(--nak-blue-hover));
border-color: var(--nak-blue-hover);
color: #ffffff;
padding: 10px 24px;
font-weight: 700;
}
.kalli-btn-submit:hover {
background: linear-gradient(135deg, var(--nak-blue-hover), #185d8f);
}
/* ── Responsive ── */
@media (max-width: 600px) {
.kalli-modal-card {
width: 100vw;
max-height: 100vh;
max-height: 100dvh;
}
.kalli-modal-hero {
padding: 18px 16px;
}
.kalli-modal-title {
font-size: 1.15em;
}
.kalli-progress {
padding: 14px 12px 10px;
gap: 0;
}
.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 {
grid-template-columns: 1fr;
}
.kalli-field-grid {
grid-template-columns: 1fr;
}
.kalli-modal-actions {
padding: 12px 16px 16px;
flex-wrap: wrap;
}
}
</style>
<script>
var kalliCurrentStep = 1;
function openKalliModal() {
document.getElementById('kalliModal').style.display = 'flex';
document.body.style.overflow = 'hidden';
kalliGoToStep(1);
}
function closeKalliModal() {
document.getElementById('kalliModal').style.display = 'none';
document.body.style.overflow = '';
}
function kalliGoToStep(step) {
for (var i = 1; i <= 4; i++) {
document.getElementById('kalliStep' + i).style.display = (i === step) ? '' : 'none';
}
kalliCurrentStep = step;
kalliUpdateProgress(step);
// Scroll modal body to top
document.querySelector('.kalli-modal-card').scrollTop = 0;
}
function kalliUpdateProgress(step) {
var steps = document.querySelectorAll('.kalli-progress-step');
var lines = document.querySelectorAll('.kalli-progress-line');
steps.forEach(function(el, idx) {
var s = idx + 1;
el.classList.remove('active', 'done');
if (s === step) el.classList.add('active');
else if (s < step) el.classList.add('done');
});
lines.forEach(function(el, idx) {
el.classList.toggle('done', idx < step - 1);
});
}
function kalliNextStep(step) {
var errEl = document.getElementById('kalliValidationError');
if (errEl) { errEl.style.display = 'none'; errEl.textContent = ''; }
if (step === 2) {
// Validate step 1: a group must be selected
var sel = document.querySelector('#kalliForm input[name="group"]:checked');
if (!sel) {
alert('Bitte wählen Sie eine Terminart aus.');
return;
}
// If custom group, require title
if ((sel.value === 'oeffentlich' || sel.value === 'intern') && !document.getElementById('kalliUserInput').value.trim()) {
alert('Bitte geben Sie einen Titel für den benutzerdefinierten Termin ein.');
return;
}
}
if (step === 3) {
// Validate step 2: date is required
if (!document.getElementById('kalliDateS').value) {
alert('Bitte geben Sie ein Datum ein.');
return;
}
}
if (step === 4) {
kalliBuildSummary();
}
kalliGoToStep(step);
}
function kalliPrevStep(step) {
kalliGoToStep(step);
}
function kalliBuildSummary() {
var grp = document.querySelector('#kalliForm input[name="group"]:checked');
var grpVal = grp ? grp.value : '–';
if ((grpVal === 'oeffentlich' || grpVal === 'intern') && document.getElementById('kalliUserInput')) {
var custom = document.getElementById('kalliUserInput').value.trim();
grpVal = custom ? custom + ' (' + (grpVal === 'oeffentlich' ? 'Öffentlich' : 'Intern') + ')' : grpVal;
}
document.getElementById('sumGroup').textContent = grpVal;
var dateS = document.getElementById('kalliDateS').value;
document.getElementById('sumDateS').textContent = dateS ? kalliFormatDate(dateS) : '–';
var dateE = document.getElementById('kalliDateE').value;
kalliShowSummaryRow('sumDateERow', dateE, 'sumDateE', kalliFormatDate(dateE));
var timeS = document.getElementById('kalliTimeS').value;
kalliShowSummaryRow('sumTimeSRow', timeS, 'sumTimeS', timeS);
var timeE = document.getElementById('kalliTimeE').value;
kalliShowSummaryRow('sumTimeERow', timeE, 'sumTimeE', timeE);
var text = document.getElementById('kalliFurtherText').value.trim();
kalliShowSummaryRow('sumTextRow', text, 'sumText', text);
var loc = document.getElementById('kalliLocation').value.trim();
kalliShowSummaryRow('sumLocRow', loc, 'sumLoc', loc);
var resp = document.getElementById('kalliResponsibility').value.trim();
kalliShowSummaryRow('sumRespRow', resp, 'sumResp', resp);
}
function kalliShowSummaryRow(rowId, value, valId, displayVal) {
var row = document.getElementById(rowId);
if (value) {
row.style.display = '';
document.getElementById(valId).textContent = displayVal;
} else {
row.style.display = 'none';
}
}
function kalliFormatDate(iso) {
if (!iso) return '–';
var parts = iso.split('-');
if (parts.length === 3) return parts[2] + '.' + parts[1] + '.' + parts[0];
return iso;
}
</script>