Migrate omsorgapp to browser SPA, add Docker/CI build setup
Docker-Images bauen und veröffentlichen / build (, omsorgCore/Dockerfile, omsorgcore) (push) Successful in 3s
Docker-Images bauen und veröffentlichen / build (, omsorgWeb/Dockerfile, omsorgweb) (push) Failing after 4s
Docker-Images bauen und veröffentlichen / build (VITE_OMSORG_CORE_URL=${{ vars.OMSORG_CORE_PUBLIC_URL }}, omsorgapp/Dockerfile, omsorgapp) (push) Successful in 3s
Docker-Images bauen und veröffentlichen / build (, omsorgCore/Dockerfile, omsorgcore) (push) Successful in 3s
Docker-Images bauen und veröffentlichen / build (, omsorgWeb/Dockerfile, omsorgweb) (push) Failing after 4s
Docker-Images bauen und veröffentlichen / build (VITE_OMSORG_CORE_URL=${{ vars.OMSORG_CORE_PUBLIC_URL }}, omsorgapp/Dockerfile, omsorgapp) (push) Successful in 3s
- omsorgapp: drop Electron, run as a plain Vite/React browser app; refresh token moves to an HttpOnly cookie (omsorgCore), CORS added for the new browser origin, document download/preview switched to Blob-based browser APIs. - Add Dockerfiles for omsorgCore, omsorgapp, and omsorgWeb, a docker-compose.yml wiring Postgres/MySQL/all three apps together, and a Gitea Actions workflow that builds and pushes images to the repo's container registry on push to main and on version tags. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
e9e96a57dc
commit
598dfcd38a
@@ -20,10 +20,21 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
if ($username === '') {
|
||||
$error = 'Bitte Benutzername eingeben.';
|
||||
} else {
|
||||
omsorgcore_forgot_password_request(_omsorgcore_config(), $username);
|
||||
// Bewusst immer weiter zu Schritt 2, unabhängig vom Ergebnis - kein Rückschluss darauf,
|
||||
// ob der Username existiert (siehe PasswordResetService.RequestResetAsync in omsorgCore).
|
||||
$step = 'verify';
|
||||
$result = omsorgcore_forgot_password_request(_omsorgcore_config(), $username);
|
||||
$status = $result['ok'] ? ($result['data']['status'] ?? null) : null;
|
||||
|
||||
if ($status === 'email_unavailable') {
|
||||
// Einzige Ausnahme vom "immer weiter"-Prinzip unten: der Code wurde zwar angelegt,
|
||||
// aber die E-Mail kam nachweislich nicht raus (z.B. SMTP down) - kein Sinn, den Nutzer
|
||||
// auf eine Code-Eingabe warten zu lassen, die nie ankommt. Kein zusätzliches
|
||||
// Enumeration-Risiko: "sent" vs. "cannot_reset" unterscheidet bereits, ob der Username
|
||||
// existiert (siehe AuthController.ForgotPasswordRequest in omsorgCore).
|
||||
$error = 'Der E-Mail-Versand ist gerade nicht verfügbar. Bitte später erneut versuchen oder einen Administrator kontaktieren.';
|
||||
} else {
|
||||
// Bewusst immer weiter zu Schritt 2, unabhängig vom Ergebnis - kein Rückschluss darauf,
|
||||
// ob der Username existiert (siehe PasswordResetService.RequestResetAsync in omsorgCore).
|
||||
$step = 'verify';
|
||||
}
|
||||
}
|
||||
} elseif ($step === 'verify') {
|
||||
$username = trim($_POST['username'] ?? '');
|
||||
|
||||
@@ -0,0 +1,292 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/../lib/auth.php';
|
||||
require_once __DIR__ . '/../lib/layout.php';
|
||||
require_login();
|
||||
|
||||
if (!has_permission('TimeEntries', 'View')) {
|
||||
header('Location: dashboard.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
$canCreate = has_permission('TimeEntries', 'Create');
|
||||
$canEdit = has_permission('TimeEntries', 'Edit');
|
||||
$error = '';
|
||||
$success = false;
|
||||
$editId = null;
|
||||
|
||||
$ordersResult = omsorgcore_orders_list(_omsorgcore_config(), $_SESSION['omsorgcore_access_token']);
|
||||
$orders = $ordersResult['ok'] ? ($ordersResult['data']['items'] ?? []) : [];
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
verify_csrf();
|
||||
|
||||
$mode = $_POST['mode'] ?? 'create';
|
||||
|
||||
if ($mode === 'submit') {
|
||||
if (!$canEdit) {
|
||||
$error = 'Keine Berechtigung, diese Zeiterfassung einzureichen.';
|
||||
} else {
|
||||
$timeEntryId = $_POST['time_entry_id'] ?? '';
|
||||
$result = omsorgcore_time_entries_submit(_omsorgcore_config(), $_SESSION['omsorgcore_access_token'], $timeEntryId);
|
||||
|
||||
if ($result['ok']) {
|
||||
header('Location: stundenerfassung.php?submitted=1');
|
||||
exit;
|
||||
}
|
||||
|
||||
$error = $result['status'] === 403
|
||||
? 'Keine Berechtigung, diese Zeiterfassung einzureichen.'
|
||||
: (is_string($result['data'] ?? null) ? $result['data'] : 'Zeiterfassung konnte nicht eingereicht werden.');
|
||||
}
|
||||
}
|
||||
|
||||
if ($mode !== 'submit') {
|
||||
$payload = [
|
||||
'orderId' => $_POST['order_id'] ?? '',
|
||||
'date' => $_POST['date'] ?? '',
|
||||
'start' => ($_POST['start'] ?? '') !== '' ? $_POST['start'] . ':00' : '',
|
||||
'end' => ($_POST['end'] ?? '') !== '' ? $_POST['end'] . ':00' : '',
|
||||
'breakDuration' => sprintf('%02d:%02d:00', intdiv((int)($_POST['break_minutes'] ?? 0), 60), (int)($_POST['break_minutes'] ?? 0) % 60),
|
||||
'nightHours' => (float)($_POST['night_hours'] ?? 0),
|
||||
'saturdayHours' => (float)($_POST['saturday_hours'] ?? 0),
|
||||
'sundayHours' => (float)($_POST['sunday_hours'] ?? 0),
|
||||
'holidayHours' => (float)($_POST['holiday_hours'] ?? 0),
|
||||
];
|
||||
|
||||
if ($payload['orderId'] === '' || $payload['date'] === '' || $payload['start'] === '' || $payload['end'] === '') {
|
||||
$error = 'Auftrag, Datum, Beginn und Ende sind erforderlich.';
|
||||
$editId = $mode === 'edit' ? ($_POST['time_entry_id'] ?? null) : null;
|
||||
} elseif ($mode === 'edit') {
|
||||
if (!$canEdit) {
|
||||
// Server lehnt das ohnehin über [RequirePermission(TimeEntries, Edit)] ab - dieser Zweig
|
||||
// greift nur, wenn jemand das Formular manuell postet, obwohl es clientseitig gar nicht
|
||||
// angezeigt wurde.
|
||||
$error = 'Keine Berechtigung, diese Zeiterfassung zu bearbeiten.';
|
||||
} else {
|
||||
$timeEntryId = $_POST['time_entry_id'] ?? '';
|
||||
$result = omsorgcore_time_entries_update(_omsorgcore_config(), $_SESSION['omsorgcore_access_token'], $timeEntryId, $payload);
|
||||
|
||||
if ($result['ok']) {
|
||||
header('Location: stundenerfassung.php?updated=1');
|
||||
exit;
|
||||
}
|
||||
|
||||
$editId = $timeEntryId;
|
||||
$error = $result['status'] === 403
|
||||
? 'Keine Berechtigung, diese Zeiterfassung zu bearbeiten.'
|
||||
: (is_string($result['data'] ?? null) ? $result['data'] : 'Zeiterfassung konnte nicht gespeichert werden.');
|
||||
}
|
||||
} elseif (!$canCreate) {
|
||||
$error = 'Keine Berechtigung, eine Zeiterfassung anzulegen.';
|
||||
} else {
|
||||
$result = omsorgcore_time_entries_create(_omsorgcore_config(), $_SESSION['omsorgcore_access_token'], $payload);
|
||||
|
||||
if ($result['ok']) {
|
||||
$success = true;
|
||||
} elseif ($result['status'] === 403) {
|
||||
$error = 'Keine Berechtigung, eine Zeiterfassung anzulegen.';
|
||||
} else {
|
||||
$error = is_string($result['data'] ?? null) ? $result['data'] : 'Zeiterfassung konnte nicht gespeichert werden.';
|
||||
}
|
||||
}
|
||||
}
|
||||
} elseif (isset($_GET['edit'])) {
|
||||
$editId = $_GET['edit'];
|
||||
}
|
||||
|
||||
$listResult = omsorgcore_time_entries_list(_omsorgcore_config(), $_SESSION['omsorgcore_access_token']);
|
||||
$timeEntries = $listResult['ok'] ? ($listResult['data']['items'] ?? []) : [];
|
||||
|
||||
// Statuswerte mit einer Selbst-Einreichungs-Kante (requiresApproval=false, siehe
|
||||
// TimeEntryService.GetSelfServiceTransitionAsync in omsorgCore) - nur für diese Einträge zeigt die
|
||||
// Liste den "Einreichen"-Button, damit er nicht auch bei bereits eingereichten Einträgen (die zwar
|
||||
// noch isEditableByOwner sind, aber keine ausgehende Selbst-Einreichungs-Kante mehr haben) erscheint.
|
||||
$transitionsResult = omsorgcore_value_list_transitions(_omsorgcore_config(), $_SESSION['omsorgcore_access_token'], 'TimeEntryStatus');
|
||||
$transitions = $transitionsResult['ok'] ? ($transitionsResult['data'] ?? []) : [];
|
||||
$submittableStatusIds = array_column(array_filter($transitions, fn($t) => empty($t['requiresApproval'])), 'fromItemId');
|
||||
|
||||
$editTimeEntry = null;
|
||||
if ($editId !== null) {
|
||||
foreach ($timeEntries as $timeEntry) {
|
||||
if ($timeEntry['id'] === $editId) {
|
||||
$editTimeEntry = $timeEntry;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Fremder/ungültiger/nicht mehr bearbeitbarer Eintrag - Own-Scope-Filterung passiert serverseitig
|
||||
// in omsorgcore_time_entries_list, hier nur zusätzlich: nicht (mehr) bearbeitbar zurück zu "Neuer Eintrag".
|
||||
if ($editTimeEntry === null || empty($editTimeEntry['isEditableByOwner'])) {
|
||||
$editId = null;
|
||||
$editTimeEntry = null;
|
||||
}
|
||||
}
|
||||
|
||||
function order_label(array $order): string
|
||||
{
|
||||
return substr($order['id'], 0, 8) . ' (' . ($order['requiredQualification'] ?? 'Auftrag') . ')';
|
||||
}
|
||||
|
||||
layout_start('Zeiterfassung – Mitarbeiter-App', 'stundenerfassung');
|
||||
?>
|
||||
<h1 class="main-title">Zeiterfassung</h1>
|
||||
|
||||
<div class="split-layout">
|
||||
<div class="glass" style="padding:24px">
|
||||
<h2 style="font-family:'KindelSerif',Georgia,serif;font-size:1.3rem;margin:0 0 18px">Meine Einträge</h2>
|
||||
|
||||
<?php if (empty($timeEntries)): ?>
|
||||
<p>Noch keine Zeiterfassung angelegt.</p>
|
||||
<?php else: ?>
|
||||
<?php foreach ($timeEntries as $timeEntry): ?>
|
||||
<div style="padding:12px 0;border-bottom:1px solid rgba(0,0,0,.08)">
|
||||
<b><?= e(substr($timeEntry['date'], 0, 10)) ?></b> — <?= e($timeEntry['statusName']) ?>
|
||||
<div class="hint">
|
||||
<?= e(substr($timeEntry['start'], 0, 5)) ?> – <?= e(substr($timeEntry['end'], 0, 5)) ?> Uhr, <?= e($timeEntry['facilityName']) ?>
|
||||
</div>
|
||||
<?php if (!empty($timeEntry['adminNote'])): ?>
|
||||
<div class="hint">Kommentar: <?= e($timeEntry['adminNote']) ?></div>
|
||||
<?php endif; ?>
|
||||
<div style="margin-top:6px;display:flex;gap:12px">
|
||||
<?php if ($canEdit && !empty($timeEntry['isEditableByOwner'])): ?>
|
||||
<a href="stundenerfassung.php?edit=<?= e($timeEntry['id']) ?>">Bearbeiten</a>
|
||||
<?php endif; ?>
|
||||
<?php if ($canEdit && in_array($timeEntry['statusId'], $submittableStatusIds, true)): ?>
|
||||
<form method="post" style="display:inline">
|
||||
<?= csrf_field() ?>
|
||||
<input type="hidden" name="mode" value="submit">
|
||||
<input type="hidden" name="time_entry_id" value="<?= e($timeEntry['id']) ?>">
|
||||
<button type="submit" style="background:none;border:none;padding:0;color:inherit;text-decoration:underline;cursor:pointer;font:inherit">Einreichen</button>
|
||||
</form>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<div class="glass" style="padding:24px">
|
||||
<h2 style="font-family:'KindelSerif',Georgia,serif;font-size:1.3rem;margin:0 0 18px">
|
||||
<?= $editTimeEntry ? 'Eintrag bearbeiten' : 'Neuer Eintrag' ?>
|
||||
</h2>
|
||||
|
||||
<?php if ($success): ?>
|
||||
<div class="hint" style="margin-bottom:16px">Eintrag wurde angelegt.</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (isset($_GET['updated'])): ?>
|
||||
<div class="hint" style="margin-bottom:16px">Eintrag wurde aktualisiert.</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (isset($_GET['submitted'])): ?>
|
||||
<div class="hint" style="margin-bottom:16px">Eintrag wurde eingereicht.</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($error): ?>
|
||||
<div class="error" style="margin-bottom:16px"><?= e($error) ?></div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($editTimeEntry): ?>
|
||||
<form method="post">
|
||||
<?= csrf_field() ?>
|
||||
<input type="hidden" name="mode" value="edit">
|
||||
<input type="hidden" name="time_entry_id" value="<?= e($editTimeEntry['id']) ?>">
|
||||
<label>
|
||||
Auftrag
|
||||
<select name="order_id" required>
|
||||
<option value="">— auswählen —</option>
|
||||
<?php foreach ($orders as $order): ?>
|
||||
<option value="<?= e($order['id']) ?>" <?= $order['id'] === $editTimeEntry['orderId'] ? 'selected' : '' ?>><?= e(order_label($order)) ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</label>
|
||||
<label>
|
||||
Datum
|
||||
<input type="date" name="date" value="<?= e(substr($editTimeEntry['date'], 0, 10)) ?>" required>
|
||||
</label>
|
||||
<label>
|
||||
Beginn
|
||||
<input type="time" name="start" value="<?= e(substr($editTimeEntry['start'], 0, 5)) ?>" required>
|
||||
</label>
|
||||
<label>
|
||||
Ende
|
||||
<input type="time" name="end" value="<?= e(substr($editTimeEntry['end'], 0, 5)) ?>" required>
|
||||
</label>
|
||||
<label>
|
||||
Pause (Minuten)
|
||||
<input type="number" min="0" name="break_minutes" value="<?= e((string)((int)substr($editTimeEntry['breakDuration'], 0, 2) * 60 + (int)substr($editTimeEntry['breakDuration'], 3, 2))) ?>">
|
||||
</label>
|
||||
<label>
|
||||
Nachtstunden
|
||||
<input type="number" min="0" step="0.25" name="night_hours" value="<?= e((string)$editTimeEntry['nightHours']) ?>">
|
||||
</label>
|
||||
<label>
|
||||
Samstagsstunden
|
||||
<input type="number" min="0" step="0.25" name="saturday_hours" value="<?= e((string)$editTimeEntry['saturdayHours']) ?>">
|
||||
</label>
|
||||
<label>
|
||||
Sonntagsstunden
|
||||
<input type="number" min="0" step="0.25" name="sunday_hours" value="<?= e((string)$editTimeEntry['sundayHours']) ?>">
|
||||
</label>
|
||||
<label>
|
||||
Feiertagsstunden
|
||||
<input type="number" min="0" step="0.25" name="holiday_hours" value="<?= e((string)$editTimeEntry['holidayHours']) ?>">
|
||||
</label>
|
||||
<button type="submit" class="btn" style="width:100%;margin-top:6px">Änderungen speichern</button>
|
||||
</form>
|
||||
<p class="hint" style="text-align:center;margin-top:14px">
|
||||
<a href="stundenerfassung.php">Abbrechen</a>
|
||||
</p>
|
||||
<?php elseif ($canCreate): ?>
|
||||
<form method="post">
|
||||
<?= csrf_field() ?>
|
||||
<input type="hidden" name="mode" value="create">
|
||||
<label>
|
||||
Auftrag
|
||||
<select name="order_id" required>
|
||||
<option value="">— auswählen —</option>
|
||||
<?php foreach ($orders as $order): ?>
|
||||
<option value="<?= e($order['id']) ?>"><?= e(order_label($order)) ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</label>
|
||||
<label>
|
||||
Datum
|
||||
<input type="date" name="date" required>
|
||||
</label>
|
||||
<label>
|
||||
Beginn
|
||||
<input type="time" name="start" required>
|
||||
</label>
|
||||
<label>
|
||||
Ende
|
||||
<input type="time" name="end" required>
|
||||
</label>
|
||||
<label>
|
||||
Pause (Minuten)
|
||||
<input type="number" min="0" name="break_minutes" value="0">
|
||||
</label>
|
||||
<label>
|
||||
Nachtstunden
|
||||
<input type="number" min="0" step="0.25" name="night_hours" value="0">
|
||||
</label>
|
||||
<label>
|
||||
Samstagsstunden
|
||||
<input type="number" min="0" step="0.25" name="saturday_hours" value="0">
|
||||
</label>
|
||||
<label>
|
||||
Sonntagsstunden
|
||||
<input type="number" min="0" step="0.25" name="sunday_hours" value="0">
|
||||
</label>
|
||||
<label>
|
||||
Feiertagsstunden
|
||||
<input type="number" min="0" step="0.25" name="holiday_hours" value="0">
|
||||
</label>
|
||||
<button type="submit" class="btn" style="width:100%;margin-top:6px">Eintrag anlegen</button>
|
||||
</form>
|
||||
<?php else: ?>
|
||||
<p class="hint">Keine Berechtigung, eine Zeiterfassung anzulegen.</p>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
layout_end();
|
||||
@@ -0,0 +1,235 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/../lib/auth.php';
|
||||
require_once __DIR__ . '/../lib/layout.php';
|
||||
require_login();
|
||||
|
||||
if (!has_permission('Absences', 'View')) {
|
||||
header('Location: dashboard.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
$canCreate = has_permission('Absences', 'Create');
|
||||
$canEdit = has_permission('Absences', 'Edit');
|
||||
$error = '';
|
||||
$success = false;
|
||||
$editId = null;
|
||||
|
||||
$typesResult = omsorgcore_value_list_items(_omsorgcore_config(), $_SESSION['omsorgcore_access_token'], 'AbsenceType');
|
||||
$types = $typesResult['ok'] ? $typesResult['data'] : [];
|
||||
|
||||
// Nicht auf den Anzeigetext "Eingereicht" hartkodieren - der ist über die Status-Verwaltung in
|
||||
// omsorgapp umbenennbar (siehe omsorgCore/CLAUDE.md, AbsenceService.GetInitialStatusValueAsync).
|
||||
$statusesResult = omsorgcore_value_list_items(_omsorgcore_config(), $_SESSION['omsorgcore_access_token'], 'AbsenceStatus');
|
||||
$statuses = $statusesResult['ok'] ? $statusesResult['data'] : [];
|
||||
$initialStatus = null;
|
||||
foreach ($statuses as $statusItem) {
|
||||
if (!empty($statusItem['isInitial'])) {
|
||||
$initialStatus = $statusItem['value'];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
verify_csrf();
|
||||
|
||||
$mode = $_POST['mode'] ?? 'create';
|
||||
|
||||
$payload = [
|
||||
'type' => $_POST['type'] ?? '',
|
||||
'startDate' => $_POST['start_date'] ?? '',
|
||||
'endDate' => $_POST['end_date'] ?? '',
|
||||
'reason' => trim($_POST['reason'] ?? ''),
|
||||
'substitute' => trim($_POST['substitute'] ?? ''),
|
||||
'note' => trim($_POST['note'] ?? ''),
|
||||
];
|
||||
|
||||
if ($payload['type'] === '' || $payload['startDate'] === '' || $payload['endDate'] === '') {
|
||||
$error = 'Art, Beginn und Ende sind erforderlich.';
|
||||
$editId = $mode === 'edit' ? ($_POST['absence_id'] ?? null) : null;
|
||||
} elseif ($payload['endDate'] < $payload['startDate']) {
|
||||
$error = 'Das Ende darf nicht vor dem Beginn liegen.';
|
||||
$editId = $mode === 'edit' ? ($_POST['absence_id'] ?? null) : null;
|
||||
} elseif ($mode === 'edit') {
|
||||
if (!$canEdit) {
|
||||
// Server lehnt das ohnehin über [RequirePermission(Absences, Edit)] ab - dieser Zweig
|
||||
// greift nur, wenn jemand das Formular manuell postet, obwohl es clientseitig gar nicht
|
||||
// angezeigt wurde.
|
||||
$error = 'Keine Berechtigung, diesen Antrag zu bearbeiten.';
|
||||
} else {
|
||||
$absenceId = $_POST['absence_id'] ?? '';
|
||||
$result = omsorgcore_absences_update(_omsorgcore_config(), $_SESSION['omsorgcore_access_token'], $absenceId, $payload);
|
||||
|
||||
if ($result['ok']) {
|
||||
header('Location: urlaubsantrag.php?updated=1');
|
||||
exit;
|
||||
}
|
||||
|
||||
$editId = $absenceId;
|
||||
$error = $result['status'] === 403
|
||||
? 'Keine Berechtigung, diesen Antrag zu bearbeiten.'
|
||||
: (is_string($result['data'] ?? null) ? $result['data'] : 'Antrag konnte nicht gespeichert werden.');
|
||||
}
|
||||
} elseif (!$canCreate) {
|
||||
$error = 'Keine Berechtigung, einen Antrag zu stellen.';
|
||||
} else {
|
||||
$result = omsorgcore_absences_create(_omsorgcore_config(), $_SESSION['omsorgcore_access_token'], $payload);
|
||||
|
||||
if ($result['ok']) {
|
||||
$success = true;
|
||||
} elseif ($result['status'] === 403) {
|
||||
$error = 'Keine Berechtigung, einen Antrag zu stellen.';
|
||||
} else {
|
||||
$error = is_string($result['data'] ?? null) ? $result['data'] : 'Antrag konnte nicht gespeichert werden.';
|
||||
}
|
||||
}
|
||||
} elseif (isset($_GET['edit'])) {
|
||||
$editId = $_GET['edit'];
|
||||
}
|
||||
|
||||
$listResult = omsorgcore_absences_list(_omsorgcore_config(), $_SESSION['omsorgcore_access_token']);
|
||||
$absences = $listResult['ok'] ? ($listResult['data']['items'] ?? []) : [];
|
||||
|
||||
$editAbsence = null;
|
||||
if ($editId !== null) {
|
||||
foreach ($absences as $absence) {
|
||||
if ($absence['id'] === $editId) {
|
||||
$editAbsence = $absence;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Fremder/ungültiger/schon entschiedener Antrag - Own-Scope-Filterung passiert serverseitig
|
||||
// in omsorgcore_absences_list, hier nur zusätzlich: nicht (mehr) bearbeitbar zurück zu "Neuer Antrag".
|
||||
if ($editAbsence === null || $editAbsence['status'] !== $initialStatus) {
|
||||
$editId = null;
|
||||
$editAbsence = null;
|
||||
}
|
||||
}
|
||||
|
||||
layout_start('Urlaub & Abwesenheit – Mitarbeiter-App', 'urlaubsantrag');
|
||||
?>
|
||||
<h1 class="main-title">Urlaub & Abwesenheit</h1>
|
||||
|
||||
<div class="split-layout">
|
||||
<div class="glass" style="padding:24px">
|
||||
<h2 style="font-family:'KindelSerif',Georgia,serif;font-size:1.3rem;margin:0 0 18px">Meine Anträge</h2>
|
||||
|
||||
<?php if (empty($absences)): ?>
|
||||
<p>Noch keine Anträge eingereicht.</p>
|
||||
<?php else: ?>
|
||||
<?php foreach ($absences as $absence): ?>
|
||||
<div style="padding:12px 0;border-bottom:1px solid rgba(0,0,0,.08)">
|
||||
<b><?= e($absence['type']) ?></b> — <?= e($absence['status']) ?>
|
||||
<div class="hint">
|
||||
<?= e(substr($absence['startDate'], 0, 10)) ?> – <?= e(substr($absence['endDate'], 0, 10)) ?>
|
||||
</div>
|
||||
<?php if (!empty($absence['adminNote'])): ?>
|
||||
<div class="hint">Kommentar: <?= e($absence['adminNote']) ?></div>
|
||||
<?php endif; ?>
|
||||
<?php if ($canEdit && $absence['status'] === $initialStatus): ?>
|
||||
<div style="margin-top:6px">
|
||||
<a href="urlaubsantrag.php?edit=<?= e($absence['id']) ?>">Bearbeiten</a>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<div class="glass" style="padding:24px">
|
||||
<h2 style="font-family:'KindelSerif',Georgia,serif;font-size:1.3rem;margin:0 0 18px">
|
||||
<?= $editAbsence ? 'Antrag bearbeiten' : 'Neuer Antrag' ?>
|
||||
</h2>
|
||||
|
||||
<?php if ($success): ?>
|
||||
<div class="hint" style="margin-bottom:16px">Antrag wurde eingereicht.</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (isset($_GET['updated'])): ?>
|
||||
<div class="hint" style="margin-bottom:16px">Antrag wurde aktualisiert.</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($error): ?>
|
||||
<div class="error" style="margin-bottom:16px"><?= e($error) ?></div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($editAbsence): ?>
|
||||
<form method="post">
|
||||
<?= csrf_field() ?>
|
||||
<input type="hidden" name="mode" value="edit">
|
||||
<input type="hidden" name="absence_id" value="<?= e($editAbsence['id']) ?>">
|
||||
<label>
|
||||
Art
|
||||
<select name="type" required>
|
||||
<option value="">— auswählen —</option>
|
||||
<?php foreach ($types as $type): ?>
|
||||
<option value="<?= e($type['value']) ?>" <?= $type['value'] === $editAbsence['type'] ? 'selected' : '' ?>><?= e($type['value']) ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</label>
|
||||
<label>
|
||||
Beginn
|
||||
<input type="date" name="start_date" value="<?= e(substr($editAbsence['startDate'], 0, 10)) ?>" required>
|
||||
</label>
|
||||
<label>
|
||||
Ende
|
||||
<input type="date" name="end_date" value="<?= e(substr($editAbsence['endDate'], 0, 10)) ?>" required>
|
||||
</label>
|
||||
<label>
|
||||
Grund (optional)
|
||||
<input type="text" name="reason" maxlength="500" value="<?= e($editAbsence['reason'] ?? '') ?>">
|
||||
</label>
|
||||
<label>
|
||||
Vertretung (optional)
|
||||
<input type="text" name="substitute" maxlength="200" value="<?= e($editAbsence['substitute'] ?? '') ?>">
|
||||
</label>
|
||||
<label>
|
||||
Nachricht (optional)
|
||||
<textarea name="note" maxlength="500"><?= e($editAbsence['note'] ?? '') ?></textarea>
|
||||
</label>
|
||||
<button type="submit" class="btn" style="width:100%;margin-top:6px">Änderungen speichern</button>
|
||||
</form>
|
||||
<p class="hint" style="text-align:center;margin-top:14px">
|
||||
<a href="urlaubsantrag.php">Abbrechen</a>
|
||||
</p>
|
||||
<?php elseif ($canCreate): ?>
|
||||
<form method="post">
|
||||
<?= csrf_field() ?>
|
||||
<input type="hidden" name="mode" value="create">
|
||||
<label>
|
||||
Art
|
||||
<select name="type" required>
|
||||
<option value="">— auswählen —</option>
|
||||
<?php foreach ($types as $type): ?>
|
||||
<option value="<?= e($type['value']) ?>"><?= e($type['value']) ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</label>
|
||||
<label>
|
||||
Beginn
|
||||
<input type="date" name="start_date" required>
|
||||
</label>
|
||||
<label>
|
||||
Ende
|
||||
<input type="date" name="end_date" required>
|
||||
</label>
|
||||
<label>
|
||||
Grund (optional)
|
||||
<input type="text" name="reason" maxlength="500">
|
||||
</label>
|
||||
<label>
|
||||
Vertretung (optional)
|
||||
<input type="text" name="substitute" maxlength="200">
|
||||
</label>
|
||||
<label>
|
||||
Nachricht (optional)
|
||||
<textarea name="note" maxlength="500"></textarea>
|
||||
</label>
|
||||
<button type="submit" class="btn" style="width:100%;margin-top:6px">Antrag einreichen</button>
|
||||
</form>
|
||||
<?php else: ?>
|
||||
<p class="hint">Keine Berechtigung, einen Antrag zu stellen.</p>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
layout_end();
|
||||
Reference in New Issue
Block a user