practical5 wip
This commit is contained in:
+3
-1
@@ -1,5 +1,7 @@
|
|||||||
{
|
{
|
||||||
"plugins": ["prettier-plugin-svelte"],
|
"plugins": ["prettier-plugin-svelte"],
|
||||||
"pluginSearchDirs": ["."], // should be removed in v3
|
"pluginSearchDirs": ["."], // should be removed in v3
|
||||||
"overrides": [{ "files": "*.svelte", "options": { "parser": "svelte" } }]
|
"overrides": [{ "files": "*.svelte", "options": { "parser": "svelte" } }],
|
||||||
|
"printWidth": 120,
|
||||||
|
"singleAttributePerLine": true
|
||||||
}
|
}
|
||||||
|
|||||||
+158
-26
@@ -26,7 +26,18 @@
|
|||||||
description: "",
|
description: "",
|
||||||
weight_kg: 0,
|
weight_kg: 0,
|
||||||
dimensions: { length_cm: 0, width_cm: 0, height_cm: 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";
|
const BASE_URL = "http://localhost:8079/freight-service/freights";
|
||||||
@@ -75,7 +86,7 @@
|
|||||||
description: "",
|
description: "",
|
||||||
weight_kg: 0,
|
weight_kg: 0,
|
||||||
dimensions: { length_cm: 0, width_cm: 0, height_cm: 0 },
|
dimensions: { length_cm: 0, width_cm: 0, height_cm: 0 },
|
||||||
status: "PENDING",
|
status: "",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
isModalOpen = true;
|
isModalOpen = true;
|
||||||
@@ -94,11 +105,24 @@
|
|||||||
|
|
||||||
<div class="p-8 space-y-4">
|
<div class="p-8 space-y-4">
|
||||||
<div class="flex justify-between items-center">
|
<div class="flex justify-between items-center">
|
||||||
<Heading tag="h2" class="text-2xl font-bold">Freights</Heading>
|
<Heading
|
||||||
<Button color="blue" onclick={() => openModal()}>+ Add Freight</Button>
|
tag="h2"
|
||||||
|
class="text-2xl font-bold"
|
||||||
|
>
|
||||||
|
Freights
|
||||||
|
</Heading>
|
||||||
|
<Button
|
||||||
|
color="blue"
|
||||||
|
onclick={() => openModal()}
|
||||||
|
>
|
||||||
|
+ Add Freight
|
||||||
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Table shadow hoverable={true}>
|
<Table
|
||||||
|
shadow
|
||||||
|
hoverable={true}
|
||||||
|
>
|
||||||
<TableHead>
|
<TableHead>
|
||||||
<TableHeadCell>ID</TableHeadCell>
|
<TableHeadCell>ID</TableHeadCell>
|
||||||
<TableHeadCell>Cargo Name</TableHeadCell>
|
<TableHeadCell>Cargo Name</TableHeadCell>
|
||||||
@@ -117,13 +141,10 @@
|
|||||||
</TableBodyCell>
|
</TableBodyCell>
|
||||||
<TableBodyCell>{item.weight_kg} kg</TableBodyCell>
|
<TableBodyCell>{item.weight_kg} kg</TableBodyCell>
|
||||||
<TableBodyCell>
|
<TableBodyCell>
|
||||||
{item.dimensions.length_cm}×{item.dimensions.width_cm}×{item
|
{item.dimensions.length_cm}×{item.dimensions.width_cm}×{item.dimensions.height_cm} cm
|
||||||
.dimensions.height_cm} cm
|
|
||||||
</TableBodyCell>
|
</TableBodyCell>
|
||||||
<TableBodyCell>
|
<TableBodyCell>
|
||||||
<Badge color={statusMap[item.status].color}
|
<Badge color={statusMap[item.status].color}>{statusMap[item.status].label}</Badge>
|
||||||
>{statusMap[item.status].label}</Badge
|
|
||||||
>
|
|
||||||
</TableBodyCell>
|
</TableBodyCell>
|
||||||
<TableBodyCell class="space-x-2">
|
<TableBodyCell class="space-x-2">
|
||||||
<Button
|
<Button
|
||||||
@@ -131,8 +152,10 @@
|
|||||||
color="alternative"
|
color="alternative"
|
||||||
onclick={() => openModal(item)}>Edit</Button
|
onclick={() => openModal(item)}>Edit</Button
|
||||||
>
|
>
|
||||||
<Button size="xs" color="red" onclick={() => deleteFreight(item.id)}
|
<Button
|
||||||
>Delete</Button
|
size="xs"
|
||||||
|
color="red"
|
||||||
|
onclick={() => deleteFreight(item.id)}>Delete</Button
|
||||||
>
|
>
|
||||||
</TableBodyCell>
|
</TableBodyCell>
|
||||||
</TableBodyRow>
|
</TableBodyRow>
|
||||||
@@ -148,49 +171,158 @@
|
|||||||
>
|
>
|
||||||
<div class="grid grid-cols-2 gap-4">
|
<div class="grid grid-cols-2 gap-4">
|
||||||
<div class="col-span-2">
|
<div class="col-span-2">
|
||||||
<Label class="mb-2">Item Name</Label>
|
<Label
|
||||||
<Input bind:value={formData.name} placeholder="e.g. Electronics" />
|
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>
|
||||||
<div class="col-span-2">
|
<div class="col-span-2">
|
||||||
<Label class="mb-2">Description</Label>
|
<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>
|
||||||
<div>
|
<div>
|
||||||
<Label class="mb-2">Weight (kg)</Label>
|
<Label
|
||||||
<Input type="number" bind:value={formData.weight_kg} />
|
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>
|
||||||
<div>
|
<div>
|
||||||
<Label class="mb-2">Status</Label>
|
<Label
|
||||||
|
class="mb-2"
|
||||||
|
color={errors.status ? "red" : "default"}
|
||||||
|
>
|
||||||
|
Status
|
||||||
|
</Label>
|
||||||
<Select
|
<Select
|
||||||
items={Object.keys(statusMap).map((k) => ({
|
items={Object.keys(statusMap).map((k) => ({
|
||||||
value: k,
|
value: k,
|
||||||
name: statusMap[k].label,
|
name: statusMap[k].label,
|
||||||
}))}
|
}))}
|
||||||
bind:value={formData.status}
|
bind:value={formData.status}
|
||||||
|
color={errors.status ? "red" : "default"}
|
||||||
/>
|
/>
|
||||||
|
{#if errors.status}
|
||||||
|
<Helper
|
||||||
|
class="mt-2"
|
||||||
|
color="red"
|
||||||
|
>
|
||||||
|
{errors.status}
|
||||||
|
</Helper>
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-span-2 border-t pt-4 mt-2">
|
<div class="col-span-2 border-t pt-4 mt-2">
|
||||||
<Label class="mb-2 font-bold">Dimensions (cm)</Label>
|
<Label class="mb-2 font-bold">Dimensions (cm)</Label>
|
||||||
<div class="grid grid-cols-3 gap-2">
|
<div class="grid grid-cols-3 gap-2">
|
||||||
<div>
|
<div>
|
||||||
<Helper>Length (cm.)</Helper>
|
<Helper
|
||||||
<Input type="number" bind:value={formData.dimensions.length_cm} />
|
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>
|
||||||
<div>
|
<div>
|
||||||
<Helper>Width (cm.)</Helper>
|
<Helper
|
||||||
<Input type="number" bind:value={formData.dimensions.width_cm} />
|
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>
|
||||||
<div>
|
<div>
|
||||||
<Helper>Height (cm.)</Helper>
|
<Helper
|
||||||
<Input type="number" bind:value={formData.dimensions.height_cm} />
|
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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex justify-end space-x-3 mt-6">
|
<div class="flex justify-end space-x-3 mt-6">
|
||||||
<Button color="alternative" onclick={closeModal}>Cancel</Button>
|
<Button
|
||||||
<Button color="blue" onclick={saveFreight}>Confirm</Button>
|
color="alternative"
|
||||||
|
onclick={closeModal}>Cancel</Button
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
color="blue"
|
||||||
|
disabled={!(Object.keys(errors).length === 0)}
|
||||||
|
onclick={saveFreight}>Confirm</Button
|
||||||
|
>
|
||||||
</div>
|
</div>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|||||||
+151
-81
@@ -16,6 +16,7 @@
|
|||||||
Heading,
|
Heading,
|
||||||
MultiSelect,
|
MultiSelect,
|
||||||
Helper,
|
Helper,
|
||||||
|
Toast,
|
||||||
} from "flowbite-svelte";
|
} from "flowbite-svelte";
|
||||||
|
|
||||||
let routes = $state([]);
|
let routes = $state([]);
|
||||||
@@ -31,7 +32,7 @@
|
|||||||
end_location: "",
|
end_location: "",
|
||||||
distance_km: 0,
|
distance_km: 0,
|
||||||
estimated_duration_hours: 0,
|
estimated_duration_hours: 0,
|
||||||
status: "PLANNED",
|
status: "",
|
||||||
});
|
});
|
||||||
|
|
||||||
const BASE_URL = "http://localhost:8079/route-service/routes";
|
const BASE_URL = "http://localhost:8079/route-service/routes";
|
||||||
@@ -62,13 +63,12 @@
|
|||||||
let errors = $derived.by(() => {
|
let errors = $derived.by(() => {
|
||||||
const e = {};
|
const e = {};
|
||||||
if (!formData.vehicle_id) e.vehicle_id = "Vehicle is required";
|
if (!formData.vehicle_id) e.vehicle_id = "Vehicle is required";
|
||||||
if (formData.freight_id.length === 0)
|
if (!formData.status.trim()) e.status = "Status is required";
|
||||||
e.freight_id = "Select at least one freight";
|
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.start_location.trim()) e.start_location = "Origin required";
|
||||||
if (!formData.end_location.trim()) e.end_location = "Destination 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.distance_km <= 0) e.distance_km = "Distance must be > 0";
|
||||||
if (formData.estimated_duration_hours <= 0)
|
if (formData.estimated_duration_hours <= 0) e.duration = "Duration must be > 0";
|
||||||
e.duration = "Duration must be > 0";
|
|
||||||
return e;
|
return e;
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -106,7 +106,7 @@
|
|||||||
end_location: "",
|
end_location: "",
|
||||||
distance_km: 0,
|
distance_km: 0,
|
||||||
estimated_duration_hours: 0,
|
estimated_duration_hours: 0,
|
||||||
status: "PLANNED",
|
status: "",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
isModalOpen = true;
|
isModalOpen = true;
|
||||||
@@ -125,11 +125,20 @@
|
|||||||
|
|
||||||
<div class="p-8 space-y-4">
|
<div class="p-8 space-y-4">
|
||||||
<div class="flex justify-between items-center">
|
<div class="flex justify-between items-center">
|
||||||
<Heading tag="h2" class="text-2xl font-bold text-gray-800">Routes</Heading>
|
<Heading
|
||||||
<Button color="blue" onclick={() => openModal()}>+ Add Route</Button>
|
tag="h2"
|
||||||
|
class="text-2xl font-bold text-gray-800">Routes</Heading
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
color="blue"
|
||||||
|
onclick={() => openModal()}>+ Add Route</Button
|
||||||
|
>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Table shadow hoverable>
|
<Table
|
||||||
|
shadow
|
||||||
|
hoverable
|
||||||
|
>
|
||||||
<TableHead>
|
<TableHead>
|
||||||
<TableHeadCell>ID</TableHeadCell>
|
<TableHeadCell>ID</TableHeadCell>
|
||||||
<TableHeadCell>Path</TableHeadCell>
|
<TableHeadCell>Path</TableHeadCell>
|
||||||
@@ -155,9 +164,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</TableBodyCell>
|
</TableBodyCell>
|
||||||
<TableBodyCell>
|
<TableBodyCell>
|
||||||
<Badge color={statusMap[route.status].color}
|
<Badge color={statusMap[route.status].color}>{statusMap[route.status].label}</Badge>
|
||||||
>{statusMap[route.status].label}</Badge
|
|
||||||
>
|
|
||||||
</TableBodyCell>
|
</TableBodyCell>
|
||||||
<TableBodyCell class="space-x-2">
|
<TableBodyCell class="space-x-2">
|
||||||
<Button
|
<Button
|
||||||
@@ -165,8 +172,10 @@
|
|||||||
color="alternative"
|
color="alternative"
|
||||||
onclick={() => openModal(route)}>Edit</Button
|
onclick={() => openModal(route)}>Edit</Button
|
||||||
>
|
>
|
||||||
<Button size="xs" color="red" onclick={() => deleteRoute(route.id)}
|
<Button
|
||||||
>Delete</Button
|
size="xs"
|
||||||
|
color="red"
|
||||||
|
onclick={() => deleteRoute(route.id)}>Delete</Button
|
||||||
>
|
>
|
||||||
</TableBodyCell>
|
</TableBodyCell>
|
||||||
</TableBodyRow>
|
</TableBodyRow>
|
||||||
@@ -182,111 +191,172 @@
|
|||||||
>
|
>
|
||||||
<div class="grid grid-cols-2 gap-4">
|
<div class="grid grid-cols-2 gap-4">
|
||||||
<div>
|
<div>
|
||||||
<Label class="mb-2">Assign Vehicle</Label>
|
<Label
|
||||||
|
color={errors.vehicle_id ? "red" : "default"}
|
||||||
|
class="mb-2">Assign Vehicle</Label
|
||||||
|
>
|
||||||
<Select
|
<Select
|
||||||
items={vehicles}
|
items={vehicles}
|
||||||
bind:value={formData.vehicleId}
|
bind:value={formData.vehicle_id}
|
||||||
|
color={errors.vehicle_id ? "red" : "default"}
|
||||||
placeholder="Select vehicle..."
|
placeholder="Select vehicle..."
|
||||||
/>
|
/>
|
||||||
|
{#if errors.vehicle_id}
|
||||||
|
<Helper
|
||||||
|
class="mt-2"
|
||||||
|
color="red"
|
||||||
|
>
|
||||||
|
{errors.vehicle_id}
|
||||||
|
</Helper>
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<Label class="mb-2">Status</Label>
|
<Label
|
||||||
|
class="mb-2"
|
||||||
|
color={errors.status ? "red" : "default"}
|
||||||
|
>
|
||||||
|
Status
|
||||||
|
</Label>
|
||||||
<Select
|
<Select
|
||||||
items={Object.keys(statusMap).map((k) => ({
|
items={Object.keys(statusMap).map((k) => ({
|
||||||
value: k,
|
value: k,
|
||||||
name: statusMap[k].label,
|
name: statusMap[k].label,
|
||||||
}))}
|
}))}
|
||||||
bind:value={formData.status}
|
bind:value={formData.status}
|
||||||
|
color={errors.status ? "red" : "default"}
|
||||||
/>
|
/>
|
||||||
|
{#if errors.status}
|
||||||
|
<Helper
|
||||||
|
class="mt-2"
|
||||||
|
color="red"
|
||||||
|
>
|
||||||
|
{errors.status}
|
||||||
|
</Helper>
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-span-2">
|
<div class="col-span-2">
|
||||||
<Label class="mb-2">Freights (Multi-select)</Label>
|
<Label
|
||||||
<MultiSelect items={freights} bind:value={formData.freight_id} />
|
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>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<Label class="mb-2">Start Location</Label>
|
<Label
|
||||||
<Input bind:value={formData.start_location} placeholder="City A" />
|
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>
|
||||||
<div>
|
<div>
|
||||||
<Label class="mb-2">End Location</Label>
|
<Label
|
||||||
<Input bind:value={formData.end_location} placeholder="City B" />
|
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>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<Label class="mb-2">Distance (km)</Label>
|
<Label
|
||||||
<Input type="number" bind:value={formData.distance_km} />
|
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>
|
||||||
|
|
||||||
<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
|
<Input
|
||||||
type="number"
|
type="number"
|
||||||
step="0.5"
|
step="0.5"
|
||||||
bind:value={formData.estimated_duration_hours}
|
bind:value={formData.estimated_duration_hours}
|
||||||
|
color={errors.estimated_duration_hours ? "red" : "default"}
|
||||||
/>
|
/>
|
||||||
</div>
|
{#if errors.estimated_duration_hours}
|
||||||
</div>
|
<Helper
|
||||||
|
class="mt-2"
|
||||||
<div class="grid grid-cols-2 gap-4">
|
color="red"
|
||||||
<div>
|
>
|
||||||
<Label color={errors.vehicle_id ? "red" : "base"} class="mb-2"
|
{errors.estimated_duration_hours}
|
||||||
>Assign Vehicle</Label
|
</Helper>
|
||||||
>
|
{/if}
|
||||||
<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}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex justify-end space-x-3 mt-6">
|
<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
|
<Button
|
||||||
color="blue"
|
color="blue"
|
||||||
disabled={!(Object.keys(errors).length === 0)}
|
disabled={!(Object.keys(errors).length === 0)}
|
||||||
onclick={saveRoute}>Save Route</Button
|
onclick={saveRoute}
|
||||||
>
|
>
|
||||||
|
Confirm
|
||||||
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|||||||
+117
-24
@@ -14,6 +14,7 @@
|
|||||||
Select,
|
Select,
|
||||||
Badge,
|
Badge,
|
||||||
Heading,
|
Heading,
|
||||||
|
Helper,
|
||||||
} from "flowbite-svelte";
|
} from "flowbite-svelte";
|
||||||
|
|
||||||
let vehicles = $state([]);
|
let vehicles = $state([]);
|
||||||
@@ -24,9 +25,9 @@
|
|||||||
brand: "",
|
brand: "",
|
||||||
model: "",
|
model: "",
|
||||||
license_plate: "",
|
license_plate: "",
|
||||||
year: 2024,
|
year: 0,
|
||||||
capacity_kg: 0,
|
capacity_kg: 0,
|
||||||
status: "AVAILABLE",
|
status: "",
|
||||||
});
|
});
|
||||||
|
|
||||||
const BASE_URL = "http://localhost:8079/vehicle-service/vehicles";
|
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() {
|
async function saveVehicle() {
|
||||||
const method = editingId ? "PUT" : "POST";
|
const method = editingId ? "PUT" : "POST";
|
||||||
const url = editingId ? `${BASE_URL}/${editingId}` : BASE_URL;
|
const url = editingId ? `${BASE_URL}/${editingId}` : BASE_URL;
|
||||||
@@ -85,9 +97,9 @@
|
|||||||
brand: "",
|
brand: "",
|
||||||
model: "",
|
model: "",
|
||||||
license_plate: "",
|
license_plate: "",
|
||||||
year: 2024,
|
year: 0,
|
||||||
capacity_kg: 0,
|
capacity_kg: 0,
|
||||||
status: "AVAILABLE",
|
status: "",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
isModalOpen = true;
|
isModalOpen = true;
|
||||||
@@ -116,11 +128,20 @@
|
|||||||
|
|
||||||
<div class="p-8 space-y-4">
|
<div class="p-8 space-y-4">
|
||||||
<div class="flex justify-between items-center">
|
<div class="flex justify-between items-center">
|
||||||
<Heading tag="h2" class="text-2xl font-bold">Vehicles</Heading>
|
<Heading
|
||||||
<Button color="blue" onclick={() => openModal()}>+ Add Vehicle</Button>
|
tag="h2"
|
||||||
|
class="text-2xl font-bold">Vehicles</Heading
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
color="blue"
|
||||||
|
onclick={() => openModal()}>+ Add Vehicle</Button
|
||||||
|
>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Table shadow hoverable={true}>
|
<Table
|
||||||
|
shadow
|
||||||
|
hoverable={true}
|
||||||
|
>
|
||||||
<TableHead>
|
<TableHead>
|
||||||
<TableHeadCell>ID</TableHeadCell>
|
<TableHeadCell>ID</TableHeadCell>
|
||||||
<TableHeadCell>Manufacturer/Model</TableHeadCell>
|
<TableHeadCell>Manufacturer/Model</TableHeadCell>
|
||||||
@@ -134,16 +155,13 @@
|
|||||||
{#each vehicles as vehicle (vehicle.id)}
|
{#each vehicles as vehicle (vehicle.id)}
|
||||||
<TableBodyRow>
|
<TableBodyRow>
|
||||||
<TableBodyCell>{vehicle.id}</TableBodyCell>
|
<TableBodyCell>{vehicle.id}</TableBodyCell>
|
||||||
<TableBodyCell class="font-medium"
|
<TableBodyCell class="font-medium">{vehicle.brand} {vehicle.model}</TableBodyCell>
|
||||||
>{vehicle.brand} {vehicle.model}</TableBodyCell
|
|
||||||
>
|
|
||||||
<TableBodyCell>{vehicle.license_plate}</TableBodyCell>
|
<TableBodyCell>{vehicle.license_plate}</TableBodyCell>
|
||||||
<TableBodyCell>{vehicle.year}</TableBodyCell>
|
<TableBodyCell>{vehicle.year}</TableBodyCell>
|
||||||
<TableBodyCell>{vehicle.capacity_kg}</TableBodyCell>
|
<TableBodyCell>{vehicle.capacity_kg}</TableBodyCell>
|
||||||
<TableBodyCell>
|
<TableBodyCell>
|
||||||
<Badge color={getStatusColor(vehicle.status)}
|
<Badge color={getStatusColor(vehicle.status)}
|
||||||
>{statusOptions.filter((s) => s.value === vehicle.status).at(0)
|
>{statusOptions.filter((s) => s.value === vehicle.status).at(0).name}</Badge
|
||||||
.name}</Badge
|
|
||||||
>
|
>
|
||||||
</TableBodyCell>
|
</TableBodyCell>
|
||||||
<TableBodyCell class="space-x-2">
|
<TableBodyCell class="space-x-2">
|
||||||
@@ -164,6 +182,17 @@
|
|||||||
</Table>
|
</Table>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{#snippet err(errs)}
|
||||||
|
{#if errs}
|
||||||
|
<Helper
|
||||||
|
class="mt-2"
|
||||||
|
color="red"
|
||||||
|
>
|
||||||
|
{errs}
|
||||||
|
</Helper>
|
||||||
|
{/if}
|
||||||
|
{/snippet}
|
||||||
|
|
||||||
<Modal
|
<Modal
|
||||||
title={editingId ? "Update Vehicle" : "Add Vehicle"}
|
title={editingId ? "Update Vehicle" : "Add Vehicle"}
|
||||||
bind:open={isModalOpen}
|
bind:open={isModalOpen}
|
||||||
@@ -172,53 +201,117 @@
|
|||||||
>
|
>
|
||||||
<div class="space-y-4">
|
<div class="space-y-4">
|
||||||
<div>
|
<div>
|
||||||
<Label for="brand" class="mb-2">Make</Label>
|
<Label
|
||||||
|
for="brand"
|
||||||
|
color={errors.brand ? "red" : "default"}
|
||||||
|
class="mb-2"
|
||||||
|
>
|
||||||
|
Manufacturer
|
||||||
|
</Label>
|
||||||
<Input
|
<Input
|
||||||
type="text"
|
type="text"
|
||||||
id="brand"
|
id="brand"
|
||||||
|
color={errors.brand ? "red" : "default"}
|
||||||
bind:value={formData.brand}
|
bind:value={formData.brand}
|
||||||
placeholder="e.g. Mercedes"
|
placeholder="e.g. Mercedes"
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
|
{@render err(errors.brand)}
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<Label for="model" class="mb-2">Model</Label>
|
<Label
|
||||||
|
for="model"
|
||||||
|
color={errors.model ? "red" : "default"}
|
||||||
|
class="mb-2"
|
||||||
|
>
|
||||||
|
Model
|
||||||
|
</Label>
|
||||||
<Input
|
<Input
|
||||||
type="text"
|
type="text"
|
||||||
id="model"
|
id="model"
|
||||||
bind:value={formData.model}
|
bind:value={formData.model}
|
||||||
|
color={errors.brand ? "red" : "default"}
|
||||||
placeholder="e.g. Actros"
|
placeholder="e.g. Actros"
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
|
{@render err(errors.model)}
|
||||||
</div>
|
</div>
|
||||||
<div class="grid grid-cols-3 gap-4">
|
<div class="grid grid-cols-3 gap-4">
|
||||||
<div>
|
<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
|
<Input
|
||||||
type="text"
|
type="text"
|
||||||
id="plate"
|
id="plate"
|
||||||
|
color={errors.license_plate ? "red" : "default"}
|
||||||
bind:value={formData.license_plate}
|
bind:value={formData.license_plate}
|
||||||
placeholder="AA1234BB"
|
placeholder="AA1234BB"
|
||||||
/>
|
/>
|
||||||
|
{@render err(errors.license_plate)}
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<Label for="year" class="mb-2">Year</Label>
|
<Label
|
||||||
<Input type="number" id="year" bind:value={formData.year} />
|
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>
|
||||||
<div>
|
<div>
|
||||||
<Label for="capacity" class="mb-2">Capacity (kg.)</Label>
|
<Label
|
||||||
<Input type="number" id="capacity" bind:value={formData.capacity_kg} />
|
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>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<Label for="status" class="mb-2">Status</Label>
|
<Label
|
||||||
<Select items={statusOptions} bind:value={formData.status} />
|
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>
|
||||||
<div class="flex justify-end space-x-3 pt-4">
|
<div class="flex justify-end space-x-3 pt-4">
|
||||||
<Button color="alternative" onclick={closeModal}>Cancel</Button>
|
<Button
|
||||||
<Button color="blue" onclick={saveVehicle}
|
color="alternative"
|
||||||
>{editingId ? "Update" : "Create"}</Button
|
onclick={closeModal}>Cancel</Button
|
||||||
>
|
>
|
||||||
|
<Button
|
||||||
|
color="blue"
|
||||||
|
disabled={!(Object.keys(errors).length === 0)}
|
||||||
|
onclick={saveVehicle}
|
||||||
|
>
|
||||||
|
Confirm
|
||||||
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|||||||
Reference in New Issue
Block a user