added other input fields and implemented form validation

This commit is contained in:
Patrick 2024-12-26 00:14:02 +01:00
parent 58591364f1
commit 828895e773
1 changed files with 232 additions and 32 deletions

View File

@ -1,15 +1,24 @@
<script lang="ts">
let { data } = $props();
const WEEKDAYS: string[] = [ "Sonntag", "Montag", "Dienstag", "Mittwoch", "Donnerstag", "Freitag", "Samstag" ]
const HEADERS: string[] = [ "Datum", "Wochentag", "Beginn", "Ende", "Dauer", "Anmerkung" ];
const TODAY: Date = new Date();
const CENTURY_PREF: number = Math.floor(TODAY.getFullYear() / 100);
const CENTURY_YEAR: number = CENTURY_PREF * 100;
let dateInput: HTMLInputElement;
let startInput: HTMLInputElement;
let endInput: HTMLInputElement;
let dateValid: boolean = $state(false);
let startValid: boolean = $state(false);
let endValid: boolean = $state(false);
let dateValid: boolean = $state(true);
let startValid: boolean = $state(true);
let endValid: boolean = $state(true);
let inWeekDay: string = $state("");
let inDuration: string = $state("");
function toInt(str: string): number {
let value = 0;
@ -25,6 +34,39 @@
return value;
}
function parseDate(str: string): Date | null {
if (str.length != 2+1+2+1+4) {
return null;
}
let day = toInt(str.slice(0, 2))
let month = toInt(str.slice(3, 5));
let year = toInt(str.slice(6, 10));
if (isNaN(day) || isNaN(month) || isNaN(year) || str.charAt(2) !== '.' || str.charAt(5) !== '.') {
return null;
}
let date = new Date(year, month-1, day);
if (isNaN(date.valueOf())) {
return null;
}
return date;
}
function validateForm(event: Event): boolean {
let valid = dateValid && dateInput.value.length !== 0
&& startValid && startInput.value.length !== 0
&& endValid && endInput.value.length !== 0;
if (!valid) {
event.preventDefault();
}
return valid;
}
function validateDate(element: HTMLInputElement) {
/*
@ -48,6 +90,7 @@
*/
switch (element.value.length) {
case 0: return true;
case 3: {
/*
* D.M
@ -284,56 +327,211 @@
return false;
}
function validateTime(element: HTMLInputElement): boolean {
/*
supports:
H:MM
HHMM
HH:MM
*/
switch(element.value.length) {
case 0: return true;
case 4: if (
(() => {
let h = toInt(element.value.slice(0, 2));
let m = toInt(element.value.slice(2, 4));
if (isNaN(h) || isNaN(m)) {
return false;
}
if (0 <= h && h <= 24 && 0 <= m && m <= 59) {
element.value = element.value.slice(0, 2) + ":" + element.value.slice(2, 4);
return true;
}
return false;
})() === true
|| (() => {
let h = toInt(element.value.charAt(0));
let m = toInt(element.value.slice(2, 4));
if (isNaN(h) || isNaN(m) || element.value.charAt(1) !== ':') {
return false;
}
if (0 <= m && m <= 59) {
element.value = "0" + element.value;
return true;
}
return false;
})() === true) {
return true;
}
case 5: {
let h = toInt(element.value.slice(0, 2));
let m = toInt(element.value.slice(3, 5));
if (isNaN(h) || isNaN(m) || element.value.charAt(2) !== ':') {
return false;
}
if (0 <= h && h <= 24 && 0 <= m && m <= 59) {
return true;
}
} break;
}
return false;
}
function calculateDuration(start: string, end: string): string {
if (start.length !== 5 || end.length !== 5) {
return "";
}
let start_h = toInt(start.slice(0, 2));
let start_m = toInt(start.slice(3, 5));
let end_h = toInt(end.slice(0, 2));
let end_m = toInt(end.slice(3, 5));
if (isNaN(start_h) || isNaN(start_m) || start.charAt(2) !== ':'
|| isNaN(end_h) || isNaN(end_m) || end.charAt(2) !== ':') {
return "";
}
let start_n = start_h * 60 + start_m;
let end_n = end_h * 60 + end_m;
let duration = (end_n - start_n) / 60;
return duration.toFixed(2);
}
let entries: number[][] = [];
for (let i = 0; i < 50; ++i) {
entries.push([i, i, i, i, i, i]);
entries.push([i, i, i, i, i, i, i*i]);
}
</script>
<form id="form_new_entry" method="POST" action="?/new_entry"></form>
<div class="wrap">
<form id="form_new_entry" method="POST" action="?/new_entry" onsubmit={validateForm}></form>
<table class="list">
<caption>Stundenliste</caption>
<thead>
<tr>
{#each HEADERS as header}
<th>{header}</th>
{/each}
<!--
<th>Datum</th>
<th>Wochentag</th>
<th>Beginn</th>
<th>Ende</th>
<th>Dauer</th>
<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>
<th>Anmerkung</th>
-->
<th style:width="fit-content">Actions</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<input
class="text-input"
bind:this={dateInput}
class:form-invalid={!dateValid}
name="Datum"
name="date"
type="text"
form="form_new_entry"
onfocusin={
(event) => {
(event.target as HTMLInputElement).select();
(_) => {
dateInput.select();
dateValid = true;
}
}
onfocusout={
(event) => {
dateValid = validateDate(event.target as HTMLInputElement);
(_) => {
dateValid = validateDate(dateInput);
if (dateValid) {
inWeekDay = WEEKDAYS[parseDate(dateInput.value)!.getDay()];
}
}
}
required>
</td>
<td>
{inWeekDay}
</td>
<td>
<input
bind:this={startInput}
class:form-invalid={!startValid}
name="starttime"
type="text"
form="form_new_entry"
onfocusin={
(_) => {
startInput.select();
startValid = true;
}
}
onfocusout={
(_) => {
startValid = validateTime(startInput);
inDuration = calculateDuration(startInput.value, endInput.value);
}
}
required>
</td>
<td>
<input
bind:this={endInput}
class:form-invalid={!endValid}
name="endtime"
type="text"
form="form_new_entry"
onfocusin={
(_) => {
endInput.select();
endValid = true;
}
}
onfocusout={
(_) => {
endValid = validateTime(endInput);
inDuration = calculateDuration(startInput.value, endInput.value);
}
}
required>
</td>
<td>
{inDuration}
</td>
<td>
<input
name="comment"
type="text"
form="form_new_entry">
</td>
<td>
<button
type="submit"
form="form_new_entry">
+
</button>
</td>
</tr>
{#each entries as entry}
<!--<tr><td>{entry}</td></tr>-->
@ -354,10 +552,13 @@
{/if}
</table>
</div>
<style>
table {
width: 100%;
width: 80%;
margin: auto;
border-collapse: collapse;
border: 1px solid;
@ -368,30 +569,29 @@ table caption {
font-weight: bold;
}
th {
width: 70px;
}
tbody * {
border: 1px solid;
}
tbody tr:nth-child(odd) {
tbody tr:nth-child(even) {
background: gray;
}
tbody tr:nth-child(even) {
tbody tr:nth-child(odd) {
background: lightgray;
}
td input {
box-sizing: border-box;
width: 100%;
border: none;
}
.td-no-elements {
text-align: center;
}
.text-input {
border: none;
}
.form-invalid {
background: #FF4444;
}