Compare commits
4 Commits
f3686c43c5
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| f6b2247cc6 | |||
| e6d65bb368 | |||
| 8aeb613286 | |||
| 22d7f255b0 |
@@ -5,6 +5,7 @@ import globals from 'globals';
|
|||||||
import { fileURLToPath } from 'node:url';
|
import { fileURLToPath } from 'node:url';
|
||||||
import ts from 'typescript-eslint';
|
import ts from 'typescript-eslint';
|
||||||
import svelteConfig from './svelte.config.js';
|
import svelteConfig from './svelte.config.js';
|
||||||
|
|
||||||
const gitignorePath = fileURLToPath(new URL("./.gitignore", import.meta.url));
|
const gitignorePath = fileURLToPath(new URL("./.gitignore", import.meta.url));
|
||||||
|
|
||||||
export default ts.config(
|
export default ts.config(
|
||||||
@@ -14,23 +15,32 @@ export default ts.config(
|
|||||||
...svelte.configs.recommended,
|
...svelte.configs.recommended,
|
||||||
{
|
{
|
||||||
languageOptions: {
|
languageOptions: {
|
||||||
globals: {
|
globals: {
|
||||||
...globals.browser,
|
...globals.browser,
|
||||||
...globals.node
|
...globals.node
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
files: ["**/*.svelte", "**/*.svelte.ts", "**/*.svelte.js"],
|
files: ["**/*.svelte", "**/*.svelte.ts", "**/*.svelte.js"],
|
||||||
ignores: ["eslint.config.js", "svelte.config.js"],
|
ignores: ["eslint.config.js", "svelte.config.js"],
|
||||||
|
|
||||||
languageOptions: {
|
languageOptions: {
|
||||||
parserOptions: {
|
parserOptions: {
|
||||||
projectService: true,
|
projectService: true,
|
||||||
extraFileExtensions: ['.svelte'],
|
extraFileExtensions: ['.svelte'],
|
||||||
parser: ts.parser,
|
parser: ts.parser,
|
||||||
svelteConfig
|
svelteConfig
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
rules: {
|
||||||
|
"@typescript-eslint/no-unused-vars": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
"varsIgnorePattern": "^_",
|
||||||
|
"argsIgnorePattern": "^_"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -34,5 +34,8 @@
|
|||||||
"onlyBuiltDependencies": [
|
"onlyBuiltDependencies": [
|
||||||
"esbuild"
|
"esbuild"
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"date-fns": "^4.1.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
9
pnpm-lock.yaml
generated
9
pnpm-lock.yaml
generated
@@ -7,6 +7,10 @@ settings:
|
|||||||
importers:
|
importers:
|
||||||
|
|
||||||
.:
|
.:
|
||||||
|
dependencies:
|
||||||
|
date-fns:
|
||||||
|
specifier: ^4.1.0
|
||||||
|
version: 4.1.0
|
||||||
devDependencies:
|
devDependencies:
|
||||||
'@eslint/compat':
|
'@eslint/compat':
|
||||||
specifier: ^1.2.5
|
specifier: ^1.2.5
|
||||||
@@ -659,6 +663,9 @@ packages:
|
|||||||
daisyui@5.0.0:
|
daisyui@5.0.0:
|
||||||
resolution: {integrity: sha512-U0K9Bac3Bi3zZGm6ojrw12F0vBHTpEgf46zv/BYxLe07hF0Xnx7emIQliwaRBgJuYhY0BhwQ6wSnq5cJXHA2yA==}
|
resolution: {integrity: sha512-U0K9Bac3Bi3zZGm6ojrw12F0vBHTpEgf46zv/BYxLe07hF0Xnx7emIQliwaRBgJuYhY0BhwQ6wSnq5cJXHA2yA==}
|
||||||
|
|
||||||
|
date-fns@4.1.0:
|
||||||
|
resolution: {integrity: sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==}
|
||||||
|
|
||||||
debug@4.4.0:
|
debug@4.4.0:
|
||||||
resolution: {integrity: sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==}
|
resolution: {integrity: sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==}
|
||||||
engines: {node: '>=6.0'}
|
engines: {node: '>=6.0'}
|
||||||
@@ -1768,6 +1775,8 @@ snapshots:
|
|||||||
|
|
||||||
daisyui@5.0.0: {}
|
daisyui@5.0.0: {}
|
||||||
|
|
||||||
|
date-fns@4.1.0: {}
|
||||||
|
|
||||||
debug@4.4.0:
|
debug@4.4.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
ms: 2.1.3
|
ms: 2.1.3
|
||||||
|
|||||||
@@ -19,6 +19,9 @@
|
|||||||
<ul class="menu bg-base-200 text-base-content min-h-full w-80 p-4">
|
<ul class="menu bg-base-200 text-base-content min-h-full w-80 p-4">
|
||||||
<li><a href="/1_counter">1. Counter</a></li>
|
<li><a href="/1_counter">1. Counter</a></li>
|
||||||
<li><a href="/2_temperature_converter">2. Temperature Converter</a></li>
|
<li><a href="/2_temperature_converter">2. Temperature Converter</a></li>
|
||||||
|
<li><a href="/3_flight_broker">3. Flight Broker</a></li>
|
||||||
|
<li><a href="/4_timer">4. Timer</a></li>
|
||||||
|
<li><a href="/5_crud">5. CRUD</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
51
src/routes/3_flight_broker/+page.svelte
Normal file
51
src/routes/3_flight_broker/+page.svelte
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { parse, format } from "date-fns";
|
||||||
|
|
||||||
|
const initialDate = format(new Date(), 'dd/MM/yyyy');
|
||||||
|
let flightType = $state("one-way");
|
||||||
|
let outbound = $state(initialDate);
|
||||||
|
let retflight = $state(initialDate);
|
||||||
|
|
||||||
|
function isValidDate(d: Date) {
|
||||||
|
return !Number.isNaN(d.getDate());
|
||||||
|
}
|
||||||
|
|
||||||
|
let outboundDate = $derived(parse(outbound, 'dd/MM/yyyy', new Date()));
|
||||||
|
let retflightDate = $derived(parse(retflight, 'dd/MM/yyyy', new Date()));
|
||||||
|
let outboundValid = $derived(isValidDate(outboundDate));
|
||||||
|
let retflightValid = $derived(isValidDate(retflightDate));
|
||||||
|
|
||||||
|
function isBookDisabled() {
|
||||||
|
const valid = outboundValid && (flightType === "return" ? retflightValid && retflightDate > outboundDate : true);
|
||||||
|
return !valid;
|
||||||
|
}
|
||||||
|
|
||||||
|
function onClickBook() {
|
||||||
|
switch (flightType) {
|
||||||
|
case "one-way":
|
||||||
|
alert(`You have booked a one-way flight on ${outbound}.`);
|
||||||
|
break;
|
||||||
|
case "return":
|
||||||
|
alert(`You have booked a return flight on ${outbound} and ${retflight}.`);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
alert("Unknown Status");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="flex flex-col items-center gap-4 w-80 mx-auto">
|
||||||
|
<select class="select" bind:value={flightType}>
|
||||||
|
<option value="one-way">One-way flight</option>
|
||||||
|
<option value="return">Return flight</option>
|
||||||
|
</select>
|
||||||
|
<label class={["input w-full", !outboundValid && "input-error"]}>
|
||||||
|
<span class="label">Outbound Flight</span>
|
||||||
|
<input type="text" placeholder="27/03/2025" bind:value={outbound}/>
|
||||||
|
</label>
|
||||||
|
<label class={["input w-full", flightType === 'return' && !retflightValid && "input-error"]}>
|
||||||
|
<span class="label">Return Flight</span>
|
||||||
|
<input type="text" placeholder="31/03/2025" bind:value={retflight} disabled={flightType !== 'return'}/>
|
||||||
|
</label>
|
||||||
|
<button class="btn btn-primary w-full" disabled={isBookDisabled()} onclick={onClickBook}>Book</button>
|
||||||
|
</div>
|
||||||
32
src/routes/4_timer/+page.svelte
Normal file
32
src/routes/4_timer/+page.svelte
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { onMount } from "svelte";
|
||||||
|
|
||||||
|
let duration = $state(300);
|
||||||
|
let elapsed = $state(0);
|
||||||
|
let elapsedSec = $derived((Math.min(elapsed, duration) / 10).toFixed(1));
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
const interval = setInterval(() => {
|
||||||
|
if (elapsed < duration) elapsed++;
|
||||||
|
}, 100);
|
||||||
|
return () => {
|
||||||
|
clearInterval(interval);
|
||||||
|
};
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="card card-border shadow-xl w-96 mx-auto">
|
||||||
|
<div class="card-body">
|
||||||
|
<h2 class="card-title">4. Timer</h2>
|
||||||
|
<div class="grid grid-cols-[min-content_auto] gap-2">
|
||||||
|
<p class="text-nowrap">Elapsed Time:</p>
|
||||||
|
<progress class="progress my-1 h-auto" value={elapsed} max={duration}></progress>
|
||||||
|
<p class="col-span-2">{elapsedSec}s</p>
|
||||||
|
<p>Duration:</p>
|
||||||
|
<input type="range" class="range range-xs" min="0" max="300" bind:value={duration}/>
|
||||||
|
</div>
|
||||||
|
<div class="card-actions">
|
||||||
|
<button class="btn btn-block btn-sm">Reset</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
79
src/routes/5_crud/+page.svelte
Normal file
79
src/routes/5_crud/+page.svelte
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import type { AppState, Entry } from "./types";
|
||||||
|
// APP STATE
|
||||||
|
let appState: AppState = $state({
|
||||||
|
entries: {
|
||||||
|
"1": { name: "Hans", surname: "Wittenberg" },
|
||||||
|
"2": { name: "Max", surname: "Neumann" },
|
||||||
|
"3": { name: "Romano", surname: "Prodi" }
|
||||||
|
},
|
||||||
|
nextIndex: 4
|
||||||
|
});
|
||||||
|
|
||||||
|
function createEntry(entry: Entry) {
|
||||||
|
appState.entries[appState.nextIndex++] = entry; // Post-Increment! Yay!
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateEntry(entryId: string, newEntry: Entry) {
|
||||||
|
appState.entries[entryId] = newEntry;
|
||||||
|
}
|
||||||
|
|
||||||
|
function deleteEntry(entryId: string) {
|
||||||
|
delete appState.entries[entryId];
|
||||||
|
}
|
||||||
|
|
||||||
|
let entryOptions = $derived(Object.entries(appState.entries).filter(([_, e]) => {
|
||||||
|
return e.name.includes(filterStr) || e.surname.includes(filterStr);
|
||||||
|
}));
|
||||||
|
|
||||||
|
// GUI STATE
|
||||||
|
let selectedId = $state("none");
|
||||||
|
let inputEntry: Entry = $state({ name: "", surname: "" });
|
||||||
|
let filterStr: string = $state("");
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="card card-border shadow-xl w-200 mx-auto">
|
||||||
|
<div class="card-body">
|
||||||
|
<h2 class="card-title">5. CRUD</h2>
|
||||||
|
<div class="grid grid-cols-2 gap-2">
|
||||||
|
<div class="inline-flex">
|
||||||
|
<span class="label text-nowrap mr-2">Filter prefix:</span>
|
||||||
|
<input type="text" class="input w-fill" bind:value={filterStr}/>
|
||||||
|
</div>
|
||||||
|
<p></p>
|
||||||
|
<div>
|
||||||
|
<span class="label">Surname, Name</span>
|
||||||
|
<select class="input h-auto cursor-auto w-full" size="10" bind:value={
|
||||||
|
() => selectedId,
|
||||||
|
(v) => {
|
||||||
|
selectedId = v;
|
||||||
|
inputEntry = $state.snapshot(appState.entries[v]); // BRUTTO RISCHIO!
|
||||||
|
}
|
||||||
|
}>
|
||||||
|
<option hidden value="none">None</option>
|
||||||
|
{#each entryOptions as [key, e] (key)}
|
||||||
|
<option value={key}>{e.surname}, {e.name}</option>
|
||||||
|
{/each}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="grid grid-cols-[min-content_auto] gap-2 mb-auto">
|
||||||
|
<span class="label">Name:</span>
|
||||||
|
<input class="input" type="text" bind:value={inputEntry.name}/>
|
||||||
|
<span class="label">Surname:</span>
|
||||||
|
<input class="input" type="text" bind:value={inputEntry.surname}/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card-actions">
|
||||||
|
<button class="btn btn-primary" onclick={() => createEntry(inputEntry)}>
|
||||||
|
Create
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-warning" disabled={selectedId === "none"}
|
||||||
|
onclick={() => updateEntry(selectedId, inputEntry)}>
|
||||||
|
Update
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-error" disabled={selectedId === "none"} onclick={() => deleteEntry(selectedId)}>
|
||||||
|
Delete
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
13
src/routes/5_crud/types.ts
Normal file
13
src/routes/5_crud/types.ts
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
export interface Entry {
|
||||||
|
name: string,
|
||||||
|
surname: string,
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DB {
|
||||||
|
[entryId: string]: Entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type AppState = {
|
||||||
|
entries: DB;
|
||||||
|
nextIndex: number;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user