Stundenaufzeichnung/src/routes/schaetzung/+page.svelte

234 lines
6.0 KiB
Svelte

<script lang="ts">
import type { PageProps } from "./$types";
import { enhance } from "$app/forms";
import { MONTHS, toInt, toFloat, padInt } from "$lib/util";
let { data }: PageProps = $props();
let next = $state((() => {
if (data.estimates.size == 0) {
return { year: (new Date()).getFullYear(), quarter: (new Date()).getMonth() / 3 + 1 };
}
const max_year = Math.max(...Array.from(data.estimates.keys()))
const max_quart = Math.max(...Array.from(data.estimates.get(max_year)?.keys() ?? [0]));
return { year: max_quart < 4 ? max_year : max_year + 1, quarter: max_quart < 4 ? max_quart + 1 : 1 };
})());
let new_quart = $state(next.quarter);
let estimate_store = $state({
estimate_0: { value: "" },
estimate_1: { value: "" },
estimate_2: { value: "" },
editing: { value: "" }
})
function validate_year(event: InputEvent) {
const input = event.target as HTMLInputElement;
if (event.inputType.startsWith("delete") || !isNaN(toInt(event.data ?? ""))) {
next.year = toInt(input.value);
} else {
input.value = next.year.toFixed(0);
}
}
function validate_quarter(event: InputEvent) {
const input = event.target as HTMLInputElement;
let in_num = event.data != null ? toInt(event.data) : NaN;
if (event.inputType.startsWith("delete") || (!isNaN(in_num) && 1 <= in_num && in_num <= 4)) {
next.quarter = toInt(input.value);
} else {
input.value = next.quarter.toFixed(0);
}
}
function validate_estimate(event: InputEvent, estimate: { value: string }) {
const input = event.target as HTMLInputElement;
if (event.inputType.startsWith("delete") || !isNaN(toFloat(input.value))) {
estimate.value = input.value;
} else {
input.value = estimate.value;
}
}
</script>
<form id="form_add_quart" method="POST" action="?/add_quarter" use:enhance></form>
<form id="form_create_pdf" method="POST" action="{base}/dokumente?/create_estimate" use:enhance></form>
<h1>Stundenschätzung</h1>
<table class="add">
<colgroup>
<col style:min-width="30ch" />
<col style:width="12ch" />
<col style:width="10ch" />
<col style:width="7ch" />
</colgroup>
<tbody>
<tr>
<td rowspan="3">Neue Schätzung: <input form="form_add_quart" style:width="7ch" type="number" name="year" oninput={validate_year as any} value={next.year} tabindex="8"/> - <input form="form_add_quart" style:width="4ch" type="number" name="quarter" oninput={validate_quarter as any} value={new_quart} tabindex="9"/>. Quartal</td>
<td>{MONTHS[(new_quart - 1) * 3 + 0]}</td>
<td><input form="form_add_quart" type="text" name="estimate_0" oninput={(e: any) => validate_estimate(e, estimate_store.estimate_0)} tabindex="10" /></td>
<td rowspan="3"><button form="form_add_quart" type="submit" tabindex="13">Erstellen</button></td>
</tr>
<tr>
<td>{MONTHS[(new_quart - 1) * 3 + 1]}</td>
<td><input form="form_add_quart" type="text" name="estimate_1" oninput={(e: any) => validate_estimate(e, estimate_store.estimate_1)} tabindex="11" /></td>
</tr>
<tr>
<td>{MONTHS[(new_quart - 1) * 3 + 2]}</td>
<td><input form="form_add_quart" type="text" name="estimate_2" oninput={(e: any) => validate_estimate(e, estimate_store.estimate_2)} tabindex="12" /></td>
</tr>
</tbody>
</table>
<div style:width="100%" style:height="40px"></div>
<table class="list">
<thead>
<tr>
<th style:width="5em"></th>
<th style:width="10ch">Quartal</th>
<th style:width="20ch">Monat</th>
<th style:width="10ch">Soll</th>
<th style:width="12ch" colspan="2">Aktion</th>
</tr>
</thead>
<tbody>
{#each data.estimates as year_pair}
{@const year = year_pair[0]}
{@const quarter_map = year_pair[1]}
{#each quarter_map as quarter_pair, quarter_i}
{@const quarter = quarter_pair[0]}
{@const months = quarter_pair[1]}
{@const fill_str = "\u2014\u2007\u2007\u2007"}
<tr>
{#if quarter_i === 0}
<td class="year" rowspan={quarter_map.size * 3}>{year}</td>
{/if}
<td class="quarter" rowspan="3">{quarter}.</td>
<td>{months[0].month}</td>
<td class="estimate">{months[0].estimate != null ? padInt(months[0].estimate, 3, 2, "\u2007", false, false) : fill_str}</td>
<td class="action" rowspan="3">
{#if data.documents?.get(year)?.get(quarter)?.[0] != null}
{@const document = data.documents!.get(year)!.get(quarter)![0]}
<form method="GET" action={`/dokumente/${document.path}`}>
<button type="submit">Download PDF</button>
</form>
{:else}
<form method="POST" action="{base}/dokumente?/create_estimate" use:enhance>
<input type="hidden" name="year" value={year} />
<input type="hidden" name="quarter" value={quarter} />
<button type="submit">Create PDF</button>
</form>
{/if}
</td>
</tr>
<tr>
<td>{months[1].month}</td>
<td class="estimate">{months[1].estimate != null ? padInt(months[1].estimate, 3, 2, "\u2007", false, false) : fill_str}</td>
</tr>
<tr>
<td>{months[2].month}</td>
<td class="estimate">{months[2].estimate != null ? padInt(months[2].estimate, 3, 2, "\u2007", false, false) : fill_str}</td>
</tr>
{/each}
{/each}
</tbody>
{#if data.estimates.size === 0}
<tfoot>
<tr>
<td class="td-no-elements" colspan="999">No records</td>
</tr>
</tfoot>
{/if}
</table>
<style>
form {
width: fit-content;
border: none;
}
table {
margin: auto;
border-collapse: collapse;
border: none;
}
.add {
tr {
border-bottom: solid 1px black;
}
tr:last-of-type {
border-bottom: none;
}
td {
text-align: center;
padding-left: 10px;
padding-right: 5px;
}
input {
width: 80%;
}
}
.list {
/*width: 50%;*/
margin: auto;
tbody > tr:has(> td.year):has(td.quarter) {
border-top: 3px black double;
}
tbody > tr:has(> td.quarter) {
border-top: 1px black solid;
}
tbody .year {
vertical-align: middle;
writing-mode: sideways-lr;
text-orientation: mixed;
}
tbody .estimate {
padding-right: 1ch;
text-align: right;
}
tbody > tr > td {
text-align: center;
}
tfoot {
border-top: 1px solid black;
}
.td-no-elements {
text-align: center;
}
}
</style>