Skip to content

Commit

Permalink
fix: fix routes with no containers associated (#263)
Browse files Browse the repository at this point in the history
## Summary

Prevent routes from being created and updated without containers
associated in the UI.

## Changes

- Update validation logic on the route edit form
- Remove the disabled logic of the save button inside the select
containers modal, since it's now validated on the form
- Add custom message on the map page of a route if it does not have any
containers associated with it
  • Loading branch information
joaotomaspinheiro authored Jul 27, 2024
1 parent 60d4395 commit 88be493
Show file tree
Hide file tree
Showing 7 changed files with 62 additions and 31 deletions.
9 changes: 9 additions & 0 deletions web/src/lib/errors/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/**
* Error that indicates that a route does not have any containers associated
* with it.
*/
export class RouteWithNoContainersError extends Error {
constructor() {
super("Route does not have any containers associated");
}
}
2 changes: 2 additions & 0 deletions web/src/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,8 @@
"routes.update.conflict.description": "The route already has more employees associated with it than the new truck has capacity.",
"routes.notFound.title": "Route not found",
"routes.notFound.description": "The route you are looking for does not exist.",
"routes.noContainersAssociated.title": "Route has no containers associated with it",
"routes.noContainersAssociated.description": "Associate at least one container with the route to display it on the map.",
"routes.delete.success": "Route deleted successfully",
"routes.delete.conflict.title": "Conflict",
"routes.delete.conflict.description": "This route cannot be deleted because it's associated with a container or employee.",
Expand Down
2 changes: 2 additions & 0 deletions web/src/locales/pt.json
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,8 @@
"routes.update.conflict.description": "A rota já tem mais operadores associados do que a capacidade do novo camião.",
"routes.notFound.title": "Rota não encontrada",
"routes.notFound.description": "A rota que procura não existe.",
"routes.noContainersAssociated.title": "Rota não tem contentores associados",
"routes.noContainersAssociated.description": "Associe pelo menos um contentor à rota para a visualizar no mapa.",
"routes.delete.success": "Rota eliminada com sucesso",
"routes.delete.conflict.title": "Conflito",
"routes.delete.conflict.description": "Esta rota não pode ser eliminada porque está associada a um contentor ou a um operador.",
Expand Down
6 changes: 6 additions & 0 deletions web/src/locales/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,12 @@
"routes.notFound.description": {
"type": "string"
},
"routes.noContainersAssociated.title": {
"type": "string"
},
"routes.noContainersAssociated.description": {
"type": "string"
},
"routes.delete.success": {
"type": "string"
},
Expand Down
37 changes: 23 additions & 14 deletions web/src/routes/backOffice/routes/components/RouteForm.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -141,15 +141,13 @@
* @param truckInput Truck input field.
* @param departureWarehouseInput Departure warehouse input field.
* @param arrivalWarehouseInput Arrival warehouse input field.
* @param containersInput Containers input field.
* @returns `true` if form is valid, `false` otherwise.
*/
function validateForm(
nameInput: HTMLInputElement,
truckInput: HTMLSelectElement,
departureWarehouseInput: HTMLSelectElement,
arrivalWarehouseInput: HTMLSelectElement,
containersInput: HTMLInputElement,
) {
if (nameInput.validity.valueMissing) {
formErrorMessages.name = $t("error.valueMissing");
Expand All @@ -175,7 +173,12 @@
formErrorMessages.arrivalWarehouse = "";
}
if (containersInput.validity.valueMissing) {
const selectedContainerAmount = getSelectedContainerAmount(
containersMap.original.length,
containersMap.added.length,
containersMap.deleted.length,
);
if (!selectedContainerAmount) {
formErrorMessages.containers = $t("error.valueMissing");
} else {
formErrorMessages.containers = "";
Expand Down Expand Up @@ -204,14 +207,12 @@
const departureWarehouseInput =
formElements.namedItem("departureWarehouse");
const arrivalWarehouseInput = formElements.namedItem("arrivalWarehouse");
const containersInput = formElements.namedItem("containers");
if (
!(nameInput instanceof HTMLInputElement) ||
!(truckInput instanceof HTMLSelectElement) ||
!(departureWarehouseInput instanceof HTMLSelectElement) ||
!(arrivalWarehouseInput instanceof HTMLSelectElement) ||
!(containersInput instanceof HTMLInputElement)
!(arrivalWarehouseInput instanceof HTMLSelectElement)
) {
throw new Error("Form elements are not inputs");
}
Expand All @@ -223,7 +224,6 @@
truckInput,
departureWarehouseInput,
arrivalWarehouseInput,
containersInput,
)
) {
return;
Expand Down Expand Up @@ -333,19 +333,26 @@
}
/**
* Retrieves the value displayed in the container input.
* Retrieves the amount of selected containers of route.
* @param originalAmount The number of containers on the route.
* @param addedAmount The number of containers added to the route.
* @param deletedAmount The number of containers deleted from the route.
* @returns Container input value.
* @returns Amount of selected containers of route.
*/
function getContainersInputValue(
function getSelectedContainerAmount(
originalAmount: number,
addedAmount: number,
deletedAmount: number,
) {
const containerAmount = originalAmount + addedAmount - deletedAmount;
return originalAmount + addedAmount - deletedAmount;
}
/**
* Retrieves the value displayed in the container input.
* @param containerAmount The number of selected containers for the route.
* @returns Container input value.
*/
function getContainersInputValue(containerAmount: number) {
return `${containerAmount} ${$t(containerAmount === 1 ? "container" : "containers").toLowerCase()}`;
}
Expand Down Expand Up @@ -583,9 +590,11 @@
<LocationInput
required
value={getContainersInputValue(
containersMap.original.length,
containersMap.added.length,
containersMap.deleted.length,
getSelectedContainerAmount(
containersMap.original.length,
containersMap.added.length,
containersMap.deleted.length,
),
)}
name="containers"
placeholder={$t("routes.containers.placeholder")}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,12 +77,6 @@
*/
let selectedFeatures: Feature[] = [];
/**
* Indicates if save action is disabled.
* @default true
*/
let isSaveActionDisabled = true;
/**
* Retrieves the ID of a feature.
* @param coordinate Feature coordinate.
Expand Down Expand Up @@ -164,7 +158,6 @@
if (originalContainersMap.has(container.id)) {
containerFeature.set("selected", true);
selectedFeatures.push(containerFeature);
isSaveActionDisabled = false;
}
}
Expand Down Expand Up @@ -284,8 +277,6 @@
selectFeature(feature);
}
}
isSaveActionDisabled = !selectedFeatures.length;
}
/**
Expand Down Expand Up @@ -339,11 +330,7 @@
/>
<svelte:fragment slot="actions">
<Button variant="tertiary" onClick={handleCancel}>{$t("cancel")}</Button>
<Button
startIcon="check"
disabled={isSaveActionDisabled}
onClick={handleSave}
>
<Button startIcon="check" onClick={handleSave}>
{$t("save")}
</Button>
</svelte:fragment>
Expand Down
22 changes: 19 additions & 3 deletions web/src/routes/backOffice/routes/details/RouteMap.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import { convertToMapProjection } from "../../../../lib/utils/map";
import type { GeoJSONFeatureCollectionLineString } from "../../../../domain/geojson";
import { getCssVariable } from "../../../../lib/utils/cssVars";
import { RouteWithNoContainersError } from "../../../../lib/errors/route";
/**
* Route ID.
Expand Down Expand Up @@ -255,6 +256,15 @@
throw new Error("Failed to retrieve route details");
}
// Check if there are no containers associated with the route.
if (
containerFeaturesRes.status === "fulfilled" &&
!containerFeaturesRes.value.length
) {
isMapVisible = false;
throw new RouteWithNoContainersError();
}
const mapHelper = new MapHelper(map);
addRouteToMap(routeWaysRes.value);
Expand Down Expand Up @@ -334,12 +344,18 @@
</div>
</Link>
<RouteBottomSheet {route} />
{:catch}
{:catch error}
{@const errorType =
error instanceof RouteWithNoContainersError
? "noContainersAssociated"
: "notFound"}
<div
class="absolute left-1/2 top-1/2 flex -translate-x-1/2 -translate-y-1/2 flex-col items-center justify-center"
>
<h2 class="text-2xl font-semibold">{$t("routes.notFound.title")}</h2>
<p>{$t("routes.notFound.description")}</p>
<h2 class="text-2xl font-semibold">
{$t(`routes.${errorType}.title`)}
</h2>
<p>{$t(`routes.${errorType}.description`)}</p>
</div>
{/await}
</main>

0 comments on commit 88be493

Please sign in to comment.