practical5 wip

This commit is contained in:
2025-12-23 05:38:09 +02:00
parent 8ad2bc0d2d
commit b5cd1ada7c
4 changed files with 429 additions and 132 deletions

View File

@@ -1,5 +1,7 @@
{
"plugins": ["prettier-plugin-svelte"],
"pluginSearchDirs": ["."], // should be removed in v3
"overrides": [{ "files": "*.svelte", "options": { "parser": "svelte" } }]
"overrides": [{ "files": "*.svelte", "options": { "parser": "svelte" } }],
"printWidth": 120,
"singleAttributePerLine": true
}

View File

@@ -26,7 +26,18 @@
description: "",
weight_kg: 0,
dimensions: { length_cm: 0, width_cm: 0, height_cm: 0 },
status: "PENDING",
status: "",
});
let errors = $derived.by(() => {
const e = {};
if (!formData.name.trim()) e.name = "Name is required";
if (formData.weight_kg <= 0) e.weight_kg = "Weight must be positive";
if (formData.dimensions.length_cm <= 0) e.length_cm = "Length must be positive";
if (formData.dimensions.width_cm <= 0) e.width_cm = "Width must be positive";
if (formData.dimensions.height_cm <= 0) e.height_cm = "Height must be positive";
if (!formData.status.trim()) e.status = "Status is required";
return e;
});
const BASE_URL = "http://localhost:8079/freight-service/freights";
@@ -75,7 +86,7 @@
description: "",
weight_kg: 0,
dimensions: { length_cm: 0, width_cm: 0, height_cm: 0 },
status: "PENDING",
status: "",
};
}
isModalOpen = true;
@@ -94,11 +105,24 @@
<div class="p-8 space-y-4">
<div class="flex justify-between items-center">
<Heading tag="h2" class="text-2xl font-bold">Freights</Heading>
<Button color="blue" onclick={() => openModal()}>+ Add Freight</Button>
<Heading
tag="h2"
class="text-2xl font-bold"
>
Freights
</Heading>
<Button
color="blue"
onclick={() => openModal()}
>
+ Add Freight
</Button>
</div>
<Table shadow hoverable={true}>
<Table
shadow
hoverable={true}
>
<TableHead>
<TableHeadCell>ID</TableHeadCell>
<TableHeadCell>Cargo Name</TableHeadCell>
@@ -117,13 +141,10 @@
</TableBodyCell>
<TableBodyCell>{item.weight_kg} kg</TableBodyCell>
<TableBodyCell>
{item.dimensions.length_cm}×{item.dimensions.width_cm}×{item
.dimensions.height_cm} cm
{item.dimensions.length_cm}×{item.dimensions.width_cm}×{item.dimensions.height_cm} cm
</TableBodyCell>
<TableBodyCell>
<Badge color={statusMap[item.status].color}
>{statusMap[item.status].label}</Badge
>
<Badge color={statusMap[item.status].color}>{statusMap[item.status].label}</Badge>
</TableBodyCell>
<TableBodyCell class="space-x-2">
<Button
@@ -131,8 +152,10 @@
color="alternative"
onclick={() => openModal(item)}>Edit</Button
>
<Button size="xs" color="red" onclick={() => deleteFreight(item.id)}
>Delete</Button
<Button
size="xs"
color="red"
onclick={() => deleteFreight(item.id)}>Delete</Button
>
</TableBodyCell>
</TableBodyRow>
@@ -148,49 +171,158 @@
>
<div class="grid grid-cols-2 gap-4">
<div class="col-span-2">
<Label class="mb-2">Item Name</Label>
<Input bind:value={formData.name} placeholder="e.g. Electronics" />
<Label
class="mb-2"
color={errors.name ? "red" : "default"}
>
Item Name
</Label>
<Input
bind:value={formData.name}
placeholder="e.g. Electronics"
color={errors.name ? "red" : "default"}
/>
{#if errors.name}
<Helper
class="mt-2"
color="red"
>
{errors.name}
</Helper>
{/if}
</div>
<div class="col-span-2">
<Label class="mb-2">Description</Label>
<Input bind:value={formData.description} placeholder="Cargo details..." />
<Input
bind:value={formData.description}
placeholder="Cargo details (optional)"
/>
</div>
<div>
<Label class="mb-2">Weight (kg)</Label>
<Input type="number" bind:value={formData.weight_kg} />
<Label
class="mb-2"
color={errors.weight_kg ? "red" : "default"}
>
Weight (kg)
</Label>
<Input
type="number"
bind:value={formData.weight_kg}
color={errors.weight_kg ? "red" : "default"}
/>
{#if errors.weight_kg}
<Helper
class="mt-2"
color="red"
>
{errors.weight_kg}
</Helper>
{/if}
</div>
<div>
<Label class="mb-2">Status</Label>
<Label
class="mb-2"
color={errors.status ? "red" : "default"}
>
Status
</Label>
<Select
items={Object.keys(statusMap).map((k) => ({
value: k,
name: statusMap[k].label,
}))}
bind:value={formData.status}
color={errors.status ? "red" : "default"}
/>
{#if errors.status}
<Helper
class="mt-2"
color="red"
>
{errors.status}
</Helper>
{/if}
</div>
<div class="col-span-2 border-t pt-4 mt-2">
<Label class="mb-2 font-bold">Dimensions (cm)</Label>
<div class="grid grid-cols-3 gap-2">
<div>
<Helper>Length (cm.)</Helper>
<Input type="number" bind:value={formData.dimensions.length_cm} />
<Helper
class="mb-2"
color={errors.length_cm ? "red" : "default"}
>
Length (cm.)
</Helper>
<Input
type="number"
bind:value={formData.dimensions.length_cm}
color={errors.width_cm ? "red" : "default"}
/>
{#if errors.length_cm}
<Helper
class="mt-2"
color="red"
>
{errors.length_cm}
</Helper>
{/if}
</div>
<div>
<Helper>Width (cm.)</Helper>
<Input type="number" bind:value={formData.dimensions.width_cm} />
<Helper
class="mb-2"
color={errors.width_cm ? "red" : "default"}
>
Width (cm.)
</Helper>
<Input
type="number"
bind:value={formData.dimensions.width_cm}
color={errors.width_cm ? "red" : "default"}
/>
{#if errors.width_cm}
<Helper
class="mt-2"
color="red"
>
{errors.width_cm}
</Helper>
{/if}
</div>
<div>
<Helper>Height (cm.)</Helper>
<Input type="number" bind:value={formData.dimensions.height_cm} />
<Helper
class="mb-2"
color={errors.height_cm ? "red" : "default"}
>
Height (cm.)
</Helper>
<Input
type="number"
bind:value={formData.dimensions.height_cm}
color={errors.height_cm ? "red" : "default"}
/>
{#if errors.height_cm}
<Helper
class="mt-2"
color="red"
>
{errors.height_cm}
</Helper>
{/if}
</div>
</div>
</div>
</div>
<div class="flex justify-end space-x-3 mt-6">
<Button color="alternative" onclick={closeModal}>Cancel</Button>
<Button color="blue" onclick={saveFreight}>Confirm</Button>
<Button
color="alternative"
onclick={closeModal}>Cancel</Button
>
<Button
color="blue"
disabled={!(Object.keys(errors).length === 0)}
onclick={saveFreight}>Confirm</Button
>
</div>
</Modal>

View File

@@ -16,6 +16,7 @@
Heading,
MultiSelect,
Helper,
Toast,
} from "flowbite-svelte";
let routes = $state([]);
@@ -31,7 +32,7 @@
end_location: "",
distance_km: 0,
estimated_duration_hours: 0,
status: "PLANNED",
status: "",
});
const BASE_URL = "http://localhost:8079/route-service/routes";
@@ -62,13 +63,12 @@
let errors = $derived.by(() => {
const e = {};
if (!formData.vehicle_id) e.vehicle_id = "Vehicle is required";
if (formData.freight_id.length === 0)
e.freight_id = "Select at least one freight";
if (!formData.status.trim()) e.status = "Status is required";
if (formData.freight_id.length === 0) e.freight_id = "Select at least one freight";
if (!formData.start_location.trim()) e.start_location = "Origin required";
if (!formData.end_location.trim()) e.end_location = "Destination required";
if (formData.distance_km <= 0) e.distance_km = "Distance must be > 0";
if (formData.estimated_duration_hours <= 0)
e.duration = "Duration must be > 0";
if (formData.estimated_duration_hours <= 0) e.duration = "Duration must be > 0";
return e;
});
@@ -106,7 +106,7 @@
end_location: "",
distance_km: 0,
estimated_duration_hours: 0,
status: "PLANNED",
status: "",
};
}
isModalOpen = true;
@@ -125,11 +125,20 @@
<div class="p-8 space-y-4">
<div class="flex justify-between items-center">
<Heading tag="h2" class="text-2xl font-bold text-gray-800">Routes</Heading>
<Button color="blue" onclick={() => openModal()}>+ Add Route</Button>
<Heading
tag="h2"
class="text-2xl font-bold text-gray-800">Routes</Heading
>
<Button
color="blue"
onclick={() => openModal()}>+ Add Route</Button
>
</div>
<Table shadow hoverable>
<Table
shadow
hoverable
>
<TableHead>
<TableHeadCell>ID</TableHeadCell>
<TableHeadCell>Path</TableHeadCell>
@@ -155,9 +164,7 @@
</div>
</TableBodyCell>
<TableBodyCell>
<Badge color={statusMap[route.status].color}
>{statusMap[route.status].label}</Badge
>
<Badge color={statusMap[route.status].color}>{statusMap[route.status].label}</Badge>
</TableBodyCell>
<TableBodyCell class="space-x-2">
<Button
@@ -165,8 +172,10 @@
color="alternative"
onclick={() => openModal(route)}>Edit</Button
>
<Button size="xs" color="red" onclick={() => deleteRoute(route.id)}
>Delete</Button
<Button
size="xs"
color="red"
onclick={() => deleteRoute(route.id)}>Delete</Button
>
</TableBodyCell>
</TableBodyRow>
@@ -182,111 +191,172 @@
>
<div class="grid grid-cols-2 gap-4">
<div>
<Label class="mb-2">Assign Vehicle</Label>
<Label
color={errors.vehicle_id ? "red" : "default"}
class="mb-2">Assign Vehicle</Label
>
<Select
items={vehicles}
bind:value={formData.vehicleId}
bind:value={formData.vehicle_id}
color={errors.vehicle_id ? "red" : "default"}
placeholder="Select vehicle..."
/>
{#if errors.vehicle_id}
<Helper
class="mt-2"
color="red"
>
{errors.vehicle_id}
</Helper>
{/if}
</div>
<div>
<Label class="mb-2">Status</Label>
<Label
class="mb-2"
color={errors.status ? "red" : "default"}
>
Status
</Label>
<Select
items={Object.keys(statusMap).map((k) => ({
value: k,
name: statusMap[k].label,
}))}
bind:value={formData.status}
color={errors.status ? "red" : "default"}
/>
{#if errors.status}
<Helper
class="mt-2"
color="red"
>
{errors.status}
</Helper>
{/if}
</div>
<div class="col-span-2">
<Label class="mb-2">Freights (Multi-select)</Label>
<MultiSelect items={freights} bind:value={formData.freight_id} />
<Label
class="mb-2"
color={errors.freight_id ? "red" : "default"}
>
Freights
</Label>
<MultiSelect
items={freights}
bind:value={formData.freight_id}
color={errors.freight_id ? "red" : "default"}
/>
{#if errors.freight_id}
<Helper
class="mt-2"
color="red"
>
{errors.freight_id}
</Helper>
{/if}
</div>
<div>
<Label class="mb-2">Start Location</Label>
<Input bind:value={formData.start_location} placeholder="City A" />
<Label
class="mb-2"
color={errors.start_location ? "red" : "default"}
>
Start Location
</Label>
<Input
bind:value={formData.start_location}
color={errors.start_location ? "red" : "default"}
placeholder="City A"
/>
{#if errors.start_location}
<Helper
class="mt-2"
color="red"
>
{errors.start_location}
</Helper>
{/if}
</div>
<div>
<Label class="mb-2">End Location</Label>
<Input bind:value={formData.end_location} placeholder="City B" />
<Label
class="mb-2"
color={errors.end_location ? "red" : "default"}
>
End Location
</Label>
<Input
bind:value={formData.end_location}
placeholder="City B"
color={errors.end_location ? "red" : "default"}
/>
{#if errors.end_location}
<Helper
class="mt-2"
color="red"
>
{errors.end_location}
</Helper>
{/if}
</div>
<div>
<Label class="mb-2">Distance (km)</Label>
<Input type="number" bind:value={formData.distance_km} />
<Label
class="mb-2"
color={errors.distance_km ? "red" : "default"}
>
Distance (km)
</Label>
<Input
type="number"
bind:value={formData.distance_km}
color={errors.distance_km ? "red" : "default"}
/>
{#if errors.distance_km}
<Helper
class="mt-2"
color="red"
>
{errors.distance_km}
</Helper>
{/if}
</div>
<div>
<Label class="mb-2">Est. Time (hours)</Label>
<Label
class="mb-2"
color={errors.estimated_duration_hours ? "red" : "default"}>Est. Time (hours)</Label
>
<Input
type="number"
step="0.5"
bind:value={formData.estimated_duration_hours}
color={errors.estimated_duration_hours ? "red" : "default"}
/>
</div>
</div>
<div class="grid grid-cols-2 gap-4">
<div>
<Label color={errors.vehicle_id ? "red" : "base"} class="mb-2"
>Assign Vehicle</Label
>
<Select
items={vehicles}
bind:value={formData.vehicle_id}
color={errors.vehicle_id ? "red" : "base"}
/>
{#if errors.vehicle_id}<Helper class="mt-2" color="red"
>{errors.vehicle_id}</Helper
>{/if}
</div>
<div class="col-span-2">
<Label color={errors.freight_id ? "red" : "base"} class="mb-2"
>Freights</Label
>
<MultiSelect items={freights} bind:value={formData.freight_id} />
{#if errors.freight_id}<Helper class="mt-2" color="red"
>{errors.freight_id}</Helper
>{/if}
</div>
<div>
<Label color={errors.start_location ? "red" : "base"} class="mb-2"
>Start Location</Label
>
<Input
bind:value={formData.start_location}
color={errors.start_location ? "red" : "base"}
/>
{#if errors.start_location}<Helper class="mt-2" color="red"
>{errors.start_location}</Helper
>{/if}
</div>
<div>
<Label color={errors.distance_km ? "red" : "base"} class="mb-2"
>Distance (km)</Label
>
<Input
type="number"
bind:value={formData.distance_km}
color={errors.distance_km ? "red" : "base"}
/>
{#if errors.distance_km}<Helper class="mt-2" color="red"
>{errors.distance_km}</Helper
>{/if}
{#if errors.estimated_duration_hours}
<Helper
class="mt-2"
color="red"
>
{errors.estimated_duration_hours}
</Helper>
{/if}
</div>
</div>
<div class="flex justify-end space-x-3 mt-6">
<Button color="alternative" onclick={closeModal}>Cancel</Button>
<Button
color="alternative"
onclick={closeModal}
>
Cancel
</Button>
<Button
color="blue"
disabled={!(Object.keys(errors).length === 0)}
onclick={saveRoute}>Save Route</Button
onclick={saveRoute}
>
Confirm
</Button>
</div>
</Modal>

View File

@@ -14,6 +14,7 @@
Select,
Badge,
Heading,
Helper,
} from "flowbite-svelte";
let vehicles = $state([]);
@@ -24,9 +25,9 @@
brand: "",
model: "",
license_plate: "",
year: 2024,
year: 0,
capacity_kg: 0,
status: "AVAILABLE",
status: "",
});
const BASE_URL = "http://localhost:8079/vehicle-service/vehicles";
@@ -46,6 +47,17 @@
}
}
let errors = $derived.by(() => {
const e = {};
if (!formData.brand.trim()) e.brand = "Manufacturer is required";
if (!formData.model.trim()) e.model = "Model is required";
if (!formData.status.trim()) e.status = "Status is required";
if (!formData.license_plate.trim()) e.license_plate = "License plate required";
if (formData.capacity_kg <= 0) e.capacity_kg = "Capacity must be > 0";
if (formData.year <= 1980) e.year = "Year must be > 1980";
return e;
});
async function saveVehicle() {
const method = editingId ? "PUT" : "POST";
const url = editingId ? `${BASE_URL}/${editingId}` : BASE_URL;
@@ -85,9 +97,9 @@
brand: "",
model: "",
license_plate: "",
year: 2024,
year: 0,
capacity_kg: 0,
status: "AVAILABLE",
status: "",
};
}
isModalOpen = true;
@@ -116,11 +128,20 @@
<div class="p-8 space-y-4">
<div class="flex justify-between items-center">
<Heading tag="h2" class="text-2xl font-bold">Vehicles</Heading>
<Button color="blue" onclick={() => openModal()}>+ Add Vehicle</Button>
<Heading
tag="h2"
class="text-2xl font-bold">Vehicles</Heading
>
<Button
color="blue"
onclick={() => openModal()}>+ Add Vehicle</Button
>
</div>
<Table shadow hoverable={true}>
<Table
shadow
hoverable={true}
>
<TableHead>
<TableHeadCell>ID</TableHeadCell>
<TableHeadCell>Manufacturer/Model</TableHeadCell>
@@ -134,16 +155,13 @@
{#each vehicles as vehicle (vehicle.id)}
<TableBodyRow>
<TableBodyCell>{vehicle.id}</TableBodyCell>
<TableBodyCell class="font-medium"
>{vehicle.brand} {vehicle.model}</TableBodyCell
>
<TableBodyCell class="font-medium">{vehicle.brand} {vehicle.model}</TableBodyCell>
<TableBodyCell>{vehicle.license_plate}</TableBodyCell>
<TableBodyCell>{vehicle.year}</TableBodyCell>
<TableBodyCell>{vehicle.capacity_kg}</TableBodyCell>
<TableBodyCell>
<Badge color={getStatusColor(vehicle.status)}
>{statusOptions.filter((s) => s.value === vehicle.status).at(0)
.name}</Badge
>{statusOptions.filter((s) => s.value === vehicle.status).at(0).name}</Badge
>
</TableBodyCell>
<TableBodyCell class="space-x-2">
@@ -164,6 +182,17 @@
</Table>
</div>
{#snippet err(errs)}
{#if errs}
<Helper
class="mt-2"
color="red"
>
{errs}
</Helper>
{/if}
{/snippet}
<Modal
title={editingId ? "Update Vehicle" : "Add Vehicle"}
bind:open={isModalOpen}
@@ -172,53 +201,117 @@
>
<div class="space-y-4">
<div>
<Label for="brand" class="mb-2">Make</Label>
<Label
for="brand"
color={errors.brand ? "red" : "default"}
class="mb-2"
>
Manufacturer
</Label>
<Input
type="text"
id="brand"
color={errors.brand ? "red" : "default"}
bind:value={formData.brand}
placeholder="e.g. Mercedes"
required
/>
{@render err(errors.brand)}
</div>
<div>
<Label for="model" class="mb-2">Model</Label>
<Label
for="model"
color={errors.model ? "red" : "default"}
class="mb-2"
>
Model
</Label>
<Input
type="text"
id="model"
bind:value={formData.model}
color={errors.brand ? "red" : "default"}
placeholder="e.g. Actros"
required
/>
{@render err(errors.model)}
</div>
<div class="grid grid-cols-3 gap-4">
<div>
<Label for="plate" class="mb-2">License Plate</Label>
<Label
for="plate"
color={errors.license_plate ? "red" : "default"}
class="mb-2"
>
License Plate
</Label>
<Input
type="text"
id="plate"
color={errors.license_plate ? "red" : "default"}
bind:value={formData.license_plate}
placeholder="AA1234BB"
/>
{@render err(errors.license_plate)}
</div>
<div>
<Label for="year" class="mb-2">Year</Label>
<Input type="number" id="year" bind:value={formData.year} />
<Label
for="year"
color={errors.year ? "red" : "default"}
class="mb-2"
>
Year
</Label>
<Input
type="number"
id="year"
color={errors.year ? "red" : "default"}
bind:value={formData.year}
/>
{@render err(errors.year)}
</div>
<div>
<Label for="capacity" class="mb-2">Capacity (kg.)</Label>
<Input type="number" id="capacity" bind:value={formData.capacity_kg} />
<Label
for="capacity"
color={errors.capacity_kg ? "red" : "default"}
class="mb-2">Capacity (kg.)</Label
>
<Input
type="number"
color={errors.capacity_kg ? "red" : "default"}
id="capacity"
bind:value={formData.capacity_kg}
/>
{@render err(errors.capacity_kg)}
</div>
</div>
<div>
<Label for="status" class="mb-2">Status</Label>
<Select items={statusOptions} bind:value={formData.status} />
<Label
for="status"
color={errors.status ? "red" : "default"}
class="mb-2"
>
Status
</Label>
<Select
items={statusOptions}
bind:value={formData.status}
color={errors.status ? "red" : "default"}
/>
{@render err(errors.status)}
</div>
<div class="flex justify-end space-x-3 pt-4">
<Button color="alternative" onclick={closeModal}>Cancel</Button>
<Button color="blue" onclick={saveVehicle}
>{editingId ? "Update" : "Create"}</Button
<Button
color="alternative"
onclick={closeModal}>Cancel</Button
>
<Button
color="blue"
disabled={!(Object.keys(errors).length === 0)}
onclick={saveVehicle}
>
Confirm
</Button>
</div>
</div>
</Modal>