finish 5th practical
This commit is contained in:
@@ -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 />
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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
21
client/src/Toaster.svelte
Normal 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>
|
||||
@@ -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>
|
||||
|
||||
|
||||
52
client/src/toastManager.svelte.js
Normal file
52
client/src/toastManager.svelte.js
Normal 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();
|
||||
Reference in New Issue
Block a user