2024-12-25 01:48:49 +01:00
|
|
|
<script lang="ts">
|
2025-01-03 01:04:30 +01:00
|
|
|
import { untrack } from "svelte";
|
|
|
|
|
import { enhance } from "$app/forms";
|
|
|
|
|
import { page } from "$app/state"
|
2024-12-25 01:48:49 +01:00
|
|
|
|
2025-01-03 01:04:30 +01:00
|
|
|
import type { RecordEntry } from "$lib/db_types"
|
|
|
|
|
import { toInt, parseDate, calculateDuration } from "$lib/util";
|
2024-12-26 00:14:02 +01:00
|
|
|
|
2025-01-03 01:04:30 +01:00
|
|
|
import type { RowState } from "./record_input_row.svelte"
|
|
|
|
|
import RecordInputRow from "./record_input_row.svelte"
|
2024-12-25 01:48:49 +01:00
|
|
|
|
2024-12-26 00:14:02 +01:00
|
|
|
|
2025-01-03 01:04:30 +01:00
|
|
|
let { data, form } : { data: { records: RecordEntry[] }, form: any } = $props();
|
2024-12-25 01:48:49 +01:00
|
|
|
|
2025-01-03 01:04:30 +01:00
|
|
|
const WEEKDAYS: string[] = [ "Sonntag", "Montag", "Dienstag", "Mittwoch", "Donnerstag", "Freitag", "Samstag" ]
|
2024-12-26 00:14:02 +01:00
|
|
|
|
2025-01-03 01:04:30 +01:00
|
|
|
const HEADERS: string[] = [ "Datum", "Wochentag", "Beginn", "Ende", "Dauer", "Anmerkung" ];
|
2024-12-26 00:14:02 +01:00
|
|
|
|
2025-01-03 01:04:30 +01:00
|
|
|
const status_ok = "ok";
|
|
|
|
|
const status_missing = "missing";
|
|
|
|
|
const status_invalid = "invalid";
|
2024-12-26 00:14:02 +01:00
|
|
|
|
2025-01-03 01:04:30 +01:00
|
|
|
let new_state = $state() as RowState;
|
2024-12-26 00:14:02 +01:00
|
|
|
|
2025-01-03 01:04:30 +01:00
|
|
|
let editing: RecordEntry | null = $state(null);
|
|
|
|
|
let edit_state = $state() as RowState;
|
2024-12-26 00:14:02 +01:00
|
|
|
|
2025-01-03 01:04:30 +01:00
|
|
|
setNewState();
|
|
|
|
|
setEditing();
|
2024-12-26 00:14:02 +01:00
|
|
|
|
2025-01-03 01:04:30 +01:00
|
|
|
$effect(() => {
|
|
|
|
|
if (form) {
|
|
|
|
|
untrack(() => setNewState());
|
2024-12-26 00:14:02 +01:00
|
|
|
}
|
2025-01-03 01:04:30 +01:00
|
|
|
})
|
|
|
|
|
$effect(() => {
|
|
|
|
|
if (form) {
|
|
|
|
|
untrack(() => setEditing(null));
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
function setNewState() {
|
|
|
|
|
new_state = {
|
|
|
|
|
date: {
|
|
|
|
|
valid: form?.new_entry?.date?.value !== "" || form?.new_entry?.date?.valid,
|
|
|
|
|
value: form?.new_entry?.date?.value ?? "",
|
|
|
|
|
},
|
|
|
|
|
start: {
|
|
|
|
|
valid: form?.new_entry?.start?.value !== "" || form?.new_entry?.start?.valid,
|
|
|
|
|
value: form?.new_entry?.start?.value ?? "",
|
|
|
|
|
},
|
|
|
|
|
end: {
|
|
|
|
|
valid: form?.new_entry?.end?.value !== "" || form?.new_entry?.end?.valid,
|
|
|
|
|
value: form?.new_entry?.end?.value ?? "",
|
|
|
|
|
},
|
|
|
|
|
comment: {
|
|
|
|
|
value: form?.new_entry?.date?.comment ?? "",
|
|
|
|
|
},
|
2024-12-25 01:48:49 +01:00
|
|
|
}
|
|
|
|
|
}
|
2024-12-26 00:14:02 +01:00
|
|
|
|
2025-01-03 01:04:30 +01:00
|
|
|
function setEditing(entry: RecordEntry | null = null) {
|
|
|
|
|
if (entry) {
|
|
|
|
|
editing = entry;
|
|
|
|
|
} else if (form?.edit_entry) {
|
|
|
|
|
editing = form.edit_entry
|
|
|
|
|
} else if (page.url.searchParams.get("edit")) {
|
|
|
|
|
let id = toInt(page.url.searchParams.get("edit")!)
|
|
|
|
|
let entry = data.records.find((entry) => { return entry.id == id; })
|
|
|
|
|
editing = entry ?? null;
|
|
|
|
|
} else {
|
|
|
|
|
editing = null;
|
|
|
|
|
}
|
2024-12-26 00:14:02 +01:00
|
|
|
|
2025-01-03 01:04:30 +01:00
|
|
|
edit_state = {
|
|
|
|
|
date: {
|
|
|
|
|
valid: editing?.id != form?.edit_entry?.id || !(form?.edit_entry?.date?.status && form.edit_entry.date.status != status_ok),
|
|
|
|
|
value: editing?.date ?? ""
|
|
|
|
|
},
|
|
|
|
|
start: {
|
|
|
|
|
valid: editing?.id != form?.edit_entry?.id || !(form?.edit_entry?.start?.status && form.edit_entry.start.status != status_ok),
|
|
|
|
|
value: editing?.start ?? ""
|
|
|
|
|
},
|
|
|
|
|
end: {
|
|
|
|
|
valid: editing?.id != form?.edit_entry?.id || !(form?.edit_entry?.end?.status && form.edit_entry.end.status != status_ok),
|
|
|
|
|
value: editing?.end ?? ""
|
|
|
|
|
},
|
|
|
|
|
comment: {
|
|
|
|
|
value: editing?.comment ?? ""
|
|
|
|
|
},
|
2024-12-26 00:14:02 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-01-03 01:04:30 +01:00
|
|
|
function validateForm(event: Event, state: RowState): boolean {
|
|
|
|
|
let valid = state.date.valid && state.date.value.length !== 0
|
|
|
|
|
&& state.start.valid && state.start.value.length !== 0
|
|
|
|
|
&& state.end.valid && state.end.value.length !== 0;
|
2024-12-26 00:14:02 +01:00
|
|
|
|
2025-01-03 01:04:30 +01:00
|
|
|
if (!valid) {
|
|
|
|
|
event.preventDefault();
|
2024-12-26 00:14:02 +01:00
|
|
|
}
|
2025-01-03 01:04:30 +01:00
|
|
|
return valid;
|
2024-12-25 01:48:49 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
</script>
|
|
|
|
|
|
2024-12-26 00:14:02 +01:00
|
|
|
<div class="wrap">
|
|
|
|
|
|
2025-01-03 01:04:30 +01:00
|
|
|
<form id="form_new_entry" method="POST" action="?/new_entry" onsubmit={(e) => validateForm(e, new_state)} use:enhance></form>
|
|
|
|
|
<form id="form_edit_entry" method="POST" action="?/edit_entry" onsubmit={(e) => validateForm(e, edit_state)} use:enhance></form>
|
2024-12-25 01:48:49 +01:00
|
|
|
|
|
|
|
|
<table class="list">
|
|
|
|
|
<caption>Stundenliste</caption>
|
2024-12-26 00:14:02 +01:00
|
|
|
|
2024-12-25 01:48:49 +01:00
|
|
|
<thead>
|
|
|
|
|
<tr>
|
2024-12-26 00:14:02 +01:00
|
|
|
<th style:width="20ch">Datum</th>
|
|
|
|
|
<th style:width="25ch">Wochentag</th>
|
|
|
|
|
<th style:width="12ch">Beginn</th>
|
|
|
|
|
<th style:width="12ch">Ende</th>
|
|
|
|
|
<th style:width="12ch">Dauer</th>
|
2024-12-25 01:48:49 +01:00
|
|
|
<th>Anmerkung</th>
|
2025-01-03 01:04:30 +01:00
|
|
|
<th style:width="12ch">Actions</th>
|
2024-12-25 01:48:49 +01:00
|
|
|
</tr>
|
|
|
|
|
</thead>
|
|
|
|
|
<tbody>
|
2025-01-03 01:04:30 +01:00
|
|
|
<RecordInputRow targetForm="form_new_entry" bind:states={new_state} enabled={editing == null}>
|
|
|
|
|
<button
|
|
|
|
|
type="submit"
|
|
|
|
|
form="form_new_entry"
|
|
|
|
|
disabled={editing != null}>
|
|
|
|
|
+
|
|
|
|
|
</button>
|
|
|
|
|
</RecordInputRow>
|
|
|
|
|
|
|
|
|
|
{#each data.records as entry}
|
|
|
|
|
{#if editing?.id != entry.id }
|
|
|
|
|
{@const weekday = parseDate(entry.date)?.getDay()}
|
2024-12-25 01:48:49 +01:00
|
|
|
<tr>
|
2025-01-03 01:04:30 +01:00
|
|
|
<td>{entry.date}</td>
|
|
|
|
|
{#if weekday != null }
|
|
|
|
|
<td>{WEEKDAYS[weekday]}</td>
|
|
|
|
|
{:else}
|
|
|
|
|
<td></td>
|
|
|
|
|
{/if}
|
|
|
|
|
<td>{entry.start}</td>
|
|
|
|
|
<td>{entry.end}</td>
|
|
|
|
|
<td>{calculateDuration(entry.start, entry.end)}</td>
|
|
|
|
|
<td>{entry.comment ?? ""}</td>
|
2024-12-25 01:48:49 +01:00
|
|
|
<td>
|
2025-01-03 01:04:30 +01:00
|
|
|
<form
|
|
|
|
|
id="form_edit"
|
|
|
|
|
method="GET"
|
|
|
|
|
onsubmit={(event) => {
|
|
|
|
|
event.preventDefault();
|
|
|
|
|
setEditing(entry);
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
<button type="submit" name="edit" value={entry.id}>Edit</button>
|
|
|
|
|
</form>
|
2024-12-25 01:48:49 +01:00
|
|
|
</td>
|
|
|
|
|
</tr>
|
2025-01-03 01:04:30 +01:00
|
|
|
{:else}
|
|
|
|
|
<RecordInputRow targetForm="form_edit_entry" bind:states={edit_state} enabled={true} >
|
|
|
|
|
<input type="hidden" form="form_edit_entry" name="id" value={entry.id} />
|
|
|
|
|
<button
|
|
|
|
|
type="submit"
|
|
|
|
|
form="form_edit_entry">
|
|
|
|
|
Save
|
|
|
|
|
</button>
|
|
|
|
|
<form
|
|
|
|
|
id="form_edit_cancel"
|
|
|
|
|
method="GET"
|
|
|
|
|
onsubmit={(event) => {
|
|
|
|
|
event.preventDefault();
|
|
|
|
|
setEditing(null);
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
<button type="submit">Cancel</button>
|
|
|
|
|
</form>
|
|
|
|
|
</RecordInputRow>
|
|
|
|
|
{/if}
|
2024-12-25 01:48:49 +01:00
|
|
|
{/each}
|
|
|
|
|
<tr></tr>
|
|
|
|
|
</tbody>
|
2025-01-03 01:04:30 +01:00
|
|
|
{#if data.records === undefined || data.records.length === 0}
|
2024-12-25 01:48:49 +01:00
|
|
|
<tfoot>
|
|
|
|
|
<tr>
|
2025-01-03 01:04:30 +01:00
|
|
|
<td class="td-no-elements" colspan="999">No records</td>
|
2024-12-25 01:48:49 +01:00
|
|
|
</tr>
|
|
|
|
|
</tfoot>
|
|
|
|
|
{/if}
|
|
|
|
|
</table>
|
|
|
|
|
|
2024-12-26 00:14:02 +01:00
|
|
|
</div>
|
|
|
|
|
|
2024-12-25 01:48:49 +01:00
|
|
|
<style>
|
|
|
|
|
|
2025-01-03 01:04:30 +01:00
|
|
|
form {
|
|
|
|
|
width: fit-content;
|
|
|
|
|
border: none;
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-25 01:48:49 +01:00
|
|
|
table {
|
2024-12-26 00:14:02 +01:00
|
|
|
width: 80%;
|
|
|
|
|
margin: auto;
|
2024-12-25 01:48:49 +01:00
|
|
|
|
|
|
|
|
border-collapse: collapse;
|
|
|
|
|
border: 1px solid;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
table caption {
|
|
|
|
|
font-size: 25px;
|
|
|
|
|
font-weight: bold;
|
|
|
|
|
}
|
|
|
|
|
|
2025-01-03 01:04:30 +01:00
|
|
|
tbody tr:nth-child(odd) {
|
2024-12-25 01:48:49 +01:00
|
|
|
background: gray;
|
|
|
|
|
}
|
|
|
|
|
|
2025-01-03 01:04:30 +01:00
|
|
|
tbody tr:nth-child(even) {
|
2024-12-25 01:48:49 +01:00
|
|
|
background: lightgray;
|
|
|
|
|
}
|
|
|
|
|
|
2025-01-03 01:04:30 +01:00
|
|
|
tr td:last-of-type {
|
|
|
|
|
display: inline-flex;
|
2024-12-25 01:48:49 +01:00
|
|
|
}
|
|
|
|
|
|
2024-12-26 00:14:02 +01:00
|
|
|
.td-no-elements {
|
|
|
|
|
text-align: center;
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-25 01:48:49 +01:00
|
|
|
</style>
|