feat: extract logic into class
All checks were successful
Build and Deploy Svelte App / build-and-deploy (push) Successful in 2m5s

This commit is contained in:
itsscb 2025-04-15 23:17:46 +02:00
parent c13c0e3323
commit b34e73fa46
2 changed files with 109 additions and 23 deletions

View File

@ -0,0 +1,88 @@
import { SvelteSet } from "svelte/reactivity";
export interface Workout {
id?: number;
date: string;
type: string;
duration_minutes: number;
}
export class WorkoutLog {
#types = new SvelteSet<string>(['jogging', 'crosstrainer', 'bicycle', 'crossfit']);
#workouts = $state<Workout[]>([]);
get workouts(): Workout[] {
return Array.from(this.#workouts);
}
get types() {
return Array.from(this.#types.values());
}
#load_from_storage() {
if (typeof localStorage === 'undefined') {
return;
}
const storage = localStorage.getItem('workout_log');
if (!storage) {
return
}
const data: {
types: string[],
workouts: Workout[],
} = JSON.parse(storage);
this.#types = new SvelteSet<string>([...data.types]);
this.#workouts.length = 0;
this.#workouts.push(...data.workouts);
}
#save_to_storage() {
if (typeof localStorage !== 'undefined') {
localStorage.setItem('workout_log', this.#to_json());
}
}
#to_json(): string {
return JSON.stringify({
types: this.types,
workouts: this.workouts,
});
}
constructor() {
this.#load_from_storage();
}
add_workout(workout: Workout) {
while (this.#workouts.indexOf(workout) !== -1) {
if (!workout.id) {
workout.id = 1;
continue
}
workout.id += 5;
}
this.#workouts.push(workout);
this.#types.add(workout.type.toLowerCase())
this.#save_to_storage();
}
remove_workout(workout: Workout) {
//this.#workouts.delete(workout);
this.#workouts = this.#workouts.filter(i => i !== workout);
this.#save_to_storage();
}
remove_type(type: string) {
this.#types.delete(type);
this.#save_to_storage();
}
}
export default WorkoutLog;

View File

@ -1,10 +1,9 @@
<script lang="ts">
import { onMount } from "svelte";
import type { WorkoutEntry } from "$lib/types";
import { getWorkouts, saveWorkouts } from "$lib/storage";
import { Save, Trash2 } from "@lucide/svelte";
import { WorkoutLog } from "$lib/types/workouts.svelte";
let workouts: WorkoutEntry[] = $state([]);
const log = new WorkoutLog();
let date = $state("");
let workout_type = $state("");
@ -15,32 +14,22 @@
onMount(() => {
const today = new Date();
date = today.toISOString().split("T")[0];
workouts = getWorkouts();
});
function deleteRow(index: number) {
workouts = workouts.toSpliced(index, 1); // modern way to remove immutably
saveWorkouts(workouts);
}
function updateRow(index: number) {
alert(`Update row #${index + 1}`);
}
function on_save() {
const workout: WorkoutEntry = {
log.add_workout({
date: date,
workout_type: workout_type,
type: workout_type,
duration_minutes: duration_minutes,
};
workouts.push(workout);
saveWorkouts(workouts);
});
}
</script>
<h1>FitLog <i>(0.0.3)</i></h1>
<h1>FitLog <i>(0.0.4)</i></h1>
<form onsubmit={on_save}>
<table>
@ -64,12 +53,20 @@
</td>
<td
><input
id="workout_type"
list="workout_types"
type="text"
bind:value={workout_type}
placeholder="Enter workout type"
required
/></td
>
/>
<datalist id="workout_types">
{#each log.types as value}
<option {value}></option>
{/each}
</datalist>
</td>
<td
><input
type="number"
@ -86,27 +83,28 @@
</button>
</td>
</tr>
{#if workouts.length < 1}
{#if log.workouts.length < 1}
<tr
><td colspan="4" style="text-align: center;"
><p>No entries.. yet!</p></td
></tr
>
{:else}
{#each workouts as wk, i}
{#each log.workouts as wk, i}
<tr>
<td>{wk.date}</td>
<td>{wk.workout_type}</td>
<td>{wk.type}</td>
<td>{wk.duration_minutes}</td>
<td>
<a
href="/#"
class="button button-delete"
role="button"
tabindex={i}
onclick={(e) => {
e.stopPropagation();
e.preventDefault();
deleteRow(i);
log.remove_workout(wk);
}}
>
<Trash2 color="red" />