finish 5th practical

This commit is contained in:
2025-12-23 06:53:03 +02:00
parent b5cd1ada7c
commit ce452f8278
6 changed files with 141 additions and 23 deletions

View File

@@ -6,8 +6,11 @@
import Routes from "./Routes.svelte";
import { Route, Router } from "svelte5-router";
import { fly } from "svelte/transition";
import Toaster from "./Toaster.svelte";
</script>
<Toaster />
<Router>
<Header />

View File

@@ -1,5 +1,5 @@
<script>
import { onMount } from "svelte";
import { onDestroy, onMount } from "svelte";
import {
Button,
Table,
@@ -16,6 +16,7 @@
Heading,
Helper,
} from "flowbite-svelte";
import toastManager from "./toastManager.svelte";
let freights = $state([]);
let isModalOpen = $state(false);
@@ -47,7 +48,7 @@
const res = await fetch(BASE_URL);
freights = await res.json();
} catch (err) {
console.error("Fetch error:", err);
toastManager.addToast(err);
}
}
@@ -66,13 +67,21 @@
closeModal();
}
} catch (err) {
console.error("Save error:", err);
toastManager.addToast(err);
}
}
async function deleteFreight(id) {
await fetch(`${BASE_URL}/${id}`, { method: "DELETE" });
fetchFreights();
try {
const res = await fetch(`${BASE_URL}/${id}`, { method: "DELETE" });
if (!res.ok) {
const err = await res.json();
throw new Error(err.detail ?? res.statusText);
}
fetchFreights();
} catch (err) {
toastManager.addToast(err);
}
}
function openModal(freight = null) {
@@ -100,6 +109,8 @@
DELIVERED: { color: "green", label: "Delivered" },
};
onDestroy(() => toastManager.onDestroy());
onMount(fetchFreights);
</script>

View File

@@ -1,5 +1,5 @@
<script>
import { onMount } from "svelte";
import { onDestroy, onMount } from "svelte";
import {
Button,
Table,
@@ -16,8 +16,8 @@
Heading,
MultiSelect,
Helper,
Toast,
} from "flowbite-svelte";
import toastManager from "./toastManager.svelte";
let routes = $state([]);
let vehicles = $state([]); // For dropdown
@@ -25,6 +25,8 @@
let isModalOpen = $state(false);
let editingId = $state(null);
onDestroy(() => toastManager.onDestroy());
let formData = $state({
vehicle_id: "",
freight_id: [],
@@ -56,7 +58,7 @@
name: f.name,
}));
} catch (err) {
console.error("Load error:", err);
toastManager.addToast(err);
}
}
@@ -76,21 +78,36 @@
const method = editingId ? "PUT" : "POST";
const url = editingId ? `${BASE_URL}/${editingId}` : BASE_URL;
const res = await fetch(url, {
method,
headers: { "Content-Type": "application/json" },
body: JSON.stringify(formData),
});
try {
const res = await fetch(url, {
method,
headers: { "Content-Type": "application/json" },
body: JSON.stringify(formData),
});
if (res.ok) {
await fetchData();
closeModal();
if (res.ok) {
await fetchData();
closeModal();
} else {
const err = await res.json();
throw new Error(err.detail ?? res.statusText);
}
} catch (err) {
toastManager.addToast(err);
}
}
async function deleteRoute(id) {
await fetch(`${BASE_URL}/${id}`, { method: "DELETE" });
fetchData();
try {
const res = await fetch(`${BASE_URL}/${id}`, { method: "DELETE" });
if (!res.ok) {
const err = await res.json();
throw new Error(err.detail ?? res.statusText);
}
fetchData();
} catch (err) {
toastManager.addToast(err);
}
}
function openModal(route = null) {

21
client/src/Toaster.svelte Normal file
View File

@@ -0,0 +1,21 @@
<script>
import { Toast, ToastContainer } from "flowbite-svelte";
import { fly } from "svelte/transition";
import toastManager from "./toastManager.svelte";
</script>
<ToastContainer position="top-right">
{#each toastManager.toasts as toast (toast.id)}
<Toast
color={toast.color}
dismissable={true}
transition={fly}
params={{ x: 200, duration: 800 }}
class="w-64"
onclose={toastManager.handleClose(toast.id)}
bind:toastStatus={toast.visible}
>
{toast.message}
</Toast>
{/each}
</ToastContainer>

View File

@@ -1,5 +1,5 @@
<script>
import { onMount } from "svelte";
import { onDestroy, onMount } from "svelte";
import {
Button,
Table,
@@ -16,6 +16,7 @@
Heading,
Helper,
} from "flowbite-svelte";
import toastManager from "./toastManager.svelte";
let vehicles = $state([]);
let isModalOpen = $state(false);
@@ -41,9 +42,12 @@
async function fetchVehicles() {
try {
const res = await fetch(BASE_URL);
if (!res.ok) {
throw new Error(res.statusText);
}
vehicles = await res.json();
} catch (err) {
console.error("Error fetching vehicles:", err);
toastManager.addToast(err);
}
}
@@ -72,18 +76,26 @@
if (res.ok) {
await fetchVehicles();
closeModal();
} else {
const err = await res.json();
throw new Error(err.detail ?? res.statusText);
}
} catch (err) {
console.error("Error saving vehicle:", err);
toastManager.addToast(err);
}
}
async function deleteVehicle(id) {
try {
const res = await fetch(`${BASE_URL}/${id}`, { method: "DELETE" });
if (res.ok) fetchVehicles();
if (res.ok) {
fetchVehicles();
} else {
const err = await res.json();
throw new Error(err.detail ?? res.statusText);
}
} catch (err) {
console.error("Error deleting vehicle:", err);
toastManager.addToast(err);
}
}
@@ -123,6 +135,8 @@
}
}
onDestroy(() => toastManager.onDestroy());
onMount(fetchVehicles);
</script>

View File

@@ -0,0 +1,52 @@
class ToastManager {
toasts = $state([]);
#nextId = $state(1);
addToast(msg) {
const selectedColor = "red";
const newToast = {
id: this.#nextId,
message: msg,
color: selectedColor,
visible: true,
};
// Auto-dismiss after 5 seconds
const timeoutId = setTimeout(() => {
this.dismissToast(newToast.id);
}, 5000);
newToast.timeoutId = timeoutId;
this.toasts = [...this.toasts, newToast];
this.#nextId++;
}
dismissToast(id) {
const toast = this.toasts.find((t) => t.id === id);
if (toast?.timeoutId) {
clearTimeout(toast.timeoutId);
}
this.toasts = this.toasts.map((t) => (t.id === id ? { ...t, visible: false } : t));
setTimeout(() => {
this.toasts = this.toasts.filter((t) => t.id !== id);
}, 300);
}
handleClose(id) {
return () => {
this.dismissToast(id);
};
}
onDestroy() {
this.toasts.forEach((toast) => {
if (toast.timeoutId) {
clearTimeout(toast.timeoutId);
}
});
}
}
export default new ToastManager();