Stundenaufzeichnung/src/routes/+page.svelte

230 lines
5.2 KiB
Svelte
Raw Normal View History

2024-12-25 01:48:49 +01:00
<script lang="ts">
import { untrack } from "svelte";
import { enhance } from "$app/forms";
import { page } from "$app/state"
2024-12-25 01:48:49 +01:00
import type { RecordEntry } from "$lib/db_types"
2025-01-17 13:32:28 +01:00
import { toInt, parseDate, calculateDuration, weekday_of } from "$lib/util";
import type { RowState } from "./record_input_row.svelte"
import RecordInputRow from "./record_input_row.svelte"
2024-12-25 01:48:49 +01:00
let { data, form } : { data: { records: RecordEntry[] }, form: any } = $props();
2024-12-25 01:48:49 +01:00
const HEADERS: string[] = [ "Datum", "Wochentag", "Beginn", "Ende", "Dauer", "Anmerkung" ];
const status_ok = "ok";
const status_missing = "missing";
const status_invalid = "invalid";
let new_state = $state() as RowState;
let editing: RecordEntry | null = $state(null);
let edit_state = $state() as RowState;
setNewState();
setEditing();
$effect(() => {
if (form) {
untrack(() => setNewState());
}
})
$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
}
}
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;
}
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 ?? ""
},
}
}
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;
if (!valid) {
event.preventDefault();
}
return valid;
2024-12-25 01:48:49 +01:00
}
</script>
<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
2025-01-17 13:32:28 +01:00
<table>
2024-12-25 01:48:49 +01:00
<caption>Stundenliste</caption>
2024-12-25 01:48:49 +01:00
<thead>
<tr>
<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>
<th style:width="12ch">Actions</th>
2024-12-25 01:48:49 +01:00
</tr>
</thead>
<tbody>
<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 }
2024-12-25 01:48:49 +01:00
<tr>
<td>{entry.date}</td>
2025-01-17 13:32:28 +01:00
<td>{weekday_of(parseDate(entry.date))}</td>
<td>{entry.start}</td>
<td>{entry.end}</td>
2025-01-17 13:32:28 +01:00
<td>{calculateDuration(entry.start, entry.end).toFixed(2)}</td>
<td>{entry.comment ?? ""}</td>
2024-12-25 01:48:49 +01:00
<td>
<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>
{: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>
{#if data.records === undefined || data.records.length === 0}
2024-12-25 01:48:49 +01:00
<tfoot>
<tr>
<td class="td-no-elements" colspan="999">No records</td>
2024-12-25 01:48:49 +01:00
</tr>
</tfoot>
{/if}
</table>
2025-01-17 13:32:28 +01:00
2024-12-25 01:48:49 +01:00
<style>
form {
width: fit-content;
border: none;
}
2024-12-25 01:48:49 +01:00
table {
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-17 13:32:28 +01:00
tbody > tr:nth-child(odd) {
2024-12-25 01:48:49 +01:00
background: gray;
}
2025-01-17 13:32:28 +01:00
tbody > tr:nth-child(even) {
2024-12-25 01:48:49 +01:00
background: lightgray;
}
2025-01-17 13:32:28 +01:00
tbody > tr > td:last-of-type {
display: inline-flex;
2024-12-25 01:48:49 +01:00
}
2025-01-17 13:32:28 +01:00
tfoot > tr {
border-top: 1px solid black;
}
.td-no-elements {
text-align: center;
}
2024-12-25 01:48:49 +01:00
</style>