mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-29 14:08:11 +00:00
vehicle
This commit is contained in:
@@ -145,6 +145,18 @@ const buildSidebarSections = (demoItems: SidebarItem[]): SidebarSection[] => [
|
||||
icon: <Truck />,
|
||||
permission: FREIGHT_PERMS.fleet.view,
|
||||
},
|
||||
{
|
||||
label: "Vehicles",
|
||||
href: "/dashboard/vehicles",
|
||||
icon: <Truck />,
|
||||
permission: FREIGHT_PERMS.fleet.view,
|
||||
},
|
||||
{
|
||||
label: "Drivers",
|
||||
href: "/dashboard/drivers",
|
||||
icon: <Users />,
|
||||
permission: FREIGHT_PERMS.fleet.view,
|
||||
},
|
||||
// {
|
||||
// label: "Containers",
|
||||
// href: "/dashboard/containers",
|
||||
@@ -484,6 +496,22 @@ const App = () => {
|
||||
</RequirePermission>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="vehicles"
|
||||
element={
|
||||
<RequirePermission permission={FREIGHT_PERMS.fleet.view}>
|
||||
<FleetResourcePage />
|
||||
</RequirePermission>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="drivers"
|
||||
element={
|
||||
<RequirePermission permission={FREIGHT_PERMS.fleet.view}>
|
||||
<FleetResourcePage />
|
||||
</RequirePermission>
|
||||
}
|
||||
/>
|
||||
|
||||
{/* iframe-based user management module */}
|
||||
<Route path="um/*" element={<UserManagementHostPage />} />
|
||||
|
||||
@@ -336,4 +336,14 @@ export const URL_CONSTANTS = {
|
||||
FOR_BOOKING: (bookingId: string) => `/bookings/${bookingId}/warehouse-fee-invoices`,
|
||||
GATE_CLEARANCE: (inventoryId: string) => `/warehouse-inventory/${inventoryId}/gate-clearance`,
|
||||
},
|
||||
|
||||
VEHICLES: {
|
||||
BASE: '/vehicles',
|
||||
BY_ID: (id: string) => `/vehicles/${id}`,
|
||||
},
|
||||
|
||||
DRIVERS: {
|
||||
BASE: '/drivers',
|
||||
BY_ID: (id: string) => `/drivers/${id}`,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -6,7 +6,9 @@ export type FleetResourceSlug =
|
||||
| "trains"
|
||||
| "wagons"
|
||||
| "containers"
|
||||
| "cargoes";
|
||||
| "cargoes"
|
||||
| "vehicles"
|
||||
| "drivers";
|
||||
|
||||
export const FLEET_SELECT_NONE = "__none__";
|
||||
|
||||
@@ -73,6 +75,8 @@ export const FLEET_BASE_PATH_BY_SLUG: Record<FleetResourceSlug, string> = {
|
||||
wagons: "/dashboard/wagons",
|
||||
containers: "/dashboard/containers",
|
||||
cargoes: "/dashboard/cargoes",
|
||||
vehicles: "/dashboard/vehicles",
|
||||
drivers: "/dashboard/drivers",
|
||||
};
|
||||
|
||||
const LOCOMOTIVE_TYPE_OPTIONS = [
|
||||
@@ -94,6 +98,37 @@ const WAGON_STATUS_OPTIONS = [
|
||||
{ label: "Retired", value: Freight.WagonStatus.Retired },
|
||||
];
|
||||
|
||||
const VEHICLE_TYPE_OPTIONS = [
|
||||
{ label: "Truck", value: "TRUCK" },
|
||||
{ label: "Van", value: "VAN" },
|
||||
{ label: "Car", value: "CAR" },
|
||||
{ label: "Bus", value: "BUS" },
|
||||
{ label: "Trailer", value: "TRAILER" },
|
||||
{ label: "Tanker", value: "TANKER" },
|
||||
{ label: "Flatbed", value: "FLATBED" },
|
||||
];
|
||||
|
||||
const FUEL_TYPE_OPTIONS = [
|
||||
{ label: "Petrol", value: "PETROL" },
|
||||
{ label: "Diesel", value: "DIESEL" },
|
||||
{ label: "Electric", value: "ELECTRIC" },
|
||||
{ label: "Hybrid", value: "HYBRID" },
|
||||
];
|
||||
|
||||
const VEHICLE_STATUS_OPTIONS = [
|
||||
{ label: "Active", value: "ACTIVE" },
|
||||
{ label: "Maintenance", value: "MAINTENANCE" },
|
||||
{ label: "Retired", value: "RETIRED" },
|
||||
{ label: "Out of service", value: "OUT_OF_SERVICE" },
|
||||
];
|
||||
|
||||
const DRIVER_STATUS_OPTIONS = [
|
||||
{ label: "Active", value: "ACTIVE" },
|
||||
{ label: "Inactive", value: "INACTIVE" },
|
||||
{ label: "Suspended", value: "SUSPENDED" },
|
||||
{ label: "On leave", value: "ON_LEAVE" },
|
||||
];
|
||||
|
||||
|
||||
export const FLEET_RESOURCES: FleetResourceConfig[] = [
|
||||
{
|
||||
@@ -342,6 +377,120 @@ export const FLEET_RESOURCES: FleetResourceConfig[] = [
|
||||
status: "PENDING",
|
||||
},
|
||||
},
|
||||
{
|
||||
slug: "vehicles",
|
||||
label: "Vehicles",
|
||||
subtitle: "Manage vehicle master data for fleet operations",
|
||||
basePath: "/dashboard/vehicles",
|
||||
addLabel: "Add Vehicle",
|
||||
entityLabel: "Vehicle",
|
||||
searchPlaceholder: "Search vehicles…",
|
||||
supportsSearch: true,
|
||||
removeAction: "delete",
|
||||
cardTitleKey: "plateNumber",
|
||||
cardCodeKey: "plateNumber",
|
||||
cardSubtitleKey: "status",
|
||||
listFilters: [
|
||||
{
|
||||
key: "status",
|
||||
label: "Status",
|
||||
allLabel: "All statuses",
|
||||
options: VEHICLE_STATUS_OPTIONS,
|
||||
},
|
||||
],
|
||||
searchKeys: ["plateNumber", "registrationNumber", "manufacturer", "model", "vehicleType", "status"],
|
||||
columns: [
|
||||
{ id: "plateNumber", header: "Plate Number", accessorKey: "plateNumber", format: "code" },
|
||||
{ id: "vehicleType", header: "Type", accessorKey: "vehicleType" },
|
||||
{ id: "manufacturer", header: "Manufacturer", accessorKey: "manufacturer" },
|
||||
{ id: "model", header: "Model", accessorKey: "model" },
|
||||
{ id: "year", header: "Year", accessorKey: "year", format: "number" },
|
||||
{ id: "fuelType", header: "Fuel Type", accessorKey: "fuelType" },
|
||||
{ id: "capacity", header: "Capacity", accessorKey: "capacity", format: "number" },
|
||||
{ id: "status", header: "Status", accessorKey: "status", format: "statusBadge" },
|
||||
],
|
||||
formFields: [
|
||||
{ name: "plateNumber", label: "Plate Number", type: "text", required: true },
|
||||
{ name: "vehicleType", label: "Vehicle Type", type: "select", required: true, options: VEHICLE_TYPE_OPTIONS },
|
||||
{ name: "manufacturer", label: "Manufacturer", type: "text", required: true },
|
||||
{ name: "model", label: "Model", type: "text", required: true },
|
||||
{ name: "year", label: "Year", type: "number", required: true },
|
||||
{ name: "fuelType", label: "Fuel Type", type: "select", required: true, options: FUEL_TYPE_OPTIONS },
|
||||
{ name: "capacity", label: "Capacity", type: "number", required: true },
|
||||
{ name: "status", label: "Status", type: "select", required: true, options: VEHICLE_STATUS_OPTIONS },
|
||||
{ name: "description", label: "Description", type: "textarea" },
|
||||
],
|
||||
emptyValues: {
|
||||
plateNumber: "",
|
||||
vehicleType: "TRUCK",
|
||||
manufacturer: "",
|
||||
model: "",
|
||||
year: new Date().getFullYear(),
|
||||
fuelType: "DIESEL",
|
||||
capacity: 0,
|
||||
status: "ACTIVE",
|
||||
description: "",
|
||||
},
|
||||
},
|
||||
{
|
||||
slug: "drivers",
|
||||
label: "Drivers",
|
||||
subtitle: "Manage driver records and licenses",
|
||||
basePath: "/dashboard/drivers",
|
||||
addLabel: "Add Driver",
|
||||
entityLabel: "Driver",
|
||||
searchPlaceholder: "Search drivers…",
|
||||
supportsSearch: true,
|
||||
removeAction: "delete",
|
||||
cardTitleKey: "firstName",
|
||||
cardCodeKey: "licenseNumber",
|
||||
cardSubtitleKey: "status",
|
||||
listFilters: [
|
||||
{
|
||||
key: "status",
|
||||
label: "Status",
|
||||
allLabel: "All statuses",
|
||||
options: DRIVER_STATUS_OPTIONS,
|
||||
},
|
||||
],
|
||||
searchKeys: ["firstName", "lastName", "email", "phoneNumber", "licenseNumber", "status"],
|
||||
columns: [
|
||||
{ id: "licenseNumber", header: "License Number", accessorKey: "licenseNumber", format: "code" },
|
||||
{ id: "firstName", header: "First Name", accessorKey: "firstName" },
|
||||
{ id: "lastName", header: "Last Name", accessorKey: "lastName" },
|
||||
{ id: "email", header: "Email", accessorKey: "email" },
|
||||
{ id: "phoneNumber", header: "Phone", accessorKey: "phoneNumber" },
|
||||
{ id: "licenseExpiryDate", header: "License Expiry", accessorKey: "licenseExpiryDate" },
|
||||
{ id: "status", header: "Status", accessorKey: "status", format: "statusBadge" },
|
||||
],
|
||||
formFields: [
|
||||
{ name: "licenseNumber", label: "License Number", type: "text", required: true },
|
||||
{ name: "firstName", label: "First Name", type: "text", required: true },
|
||||
{ name: "lastName", label: "Last Name", type: "text", required: true },
|
||||
{ name: "email", label: "Email", type: "text", required: true },
|
||||
{ name: "phoneNumber", label: "Phone Number", type: "text", required: true },
|
||||
{ name: "dateOfBirth", label: "Date of Birth", type: "text", required: true },
|
||||
{ name: "licenseExpiryDate", label: "License Expiry Date", type: "text", required: true },
|
||||
{ name: "status", label: "Status", type: "select", required: true, options: DRIVER_STATUS_OPTIONS },
|
||||
{ name: "address", label: "Address", type: "textarea" },
|
||||
{ name: "emergencyContact", label: "Emergency Contact", type: "text" },
|
||||
{ name: "notes", label: "Notes", type: "textarea" },
|
||||
],
|
||||
emptyValues: {
|
||||
licenseNumber: "",
|
||||
firstName: "",
|
||||
lastName: "",
|
||||
email: "",
|
||||
phoneNumber: "",
|
||||
dateOfBirth: "",
|
||||
licenseExpiryDate: "",
|
||||
status: "ACTIVE",
|
||||
vehicleTypesAuthorized: [],
|
||||
address: "",
|
||||
emergencyContact: "",
|
||||
notes: "",
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
export const getFleetResource = (slug: string): FleetResourceConfig | undefined =>
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
import { api as apiClient } from '../auth/http';
|
||||
import { URL_CONSTANTS } from '@/constants/URLS';
|
||||
|
||||
export type DriverStatus = 'ACTIVE' | 'INACTIVE' | 'SUSPENDED' | 'ON_LEAVE';
|
||||
|
||||
export interface DriverListFilters {
|
||||
status?: DriverStatus;
|
||||
search?: string;
|
||||
page?: number;
|
||||
limit?: number;
|
||||
sortBy?: string;
|
||||
sortOrder?: 'ASC' | 'DESC';
|
||||
}
|
||||
|
||||
export interface Driver {
|
||||
id: string;
|
||||
licenseNumber: string;
|
||||
firstName: string;
|
||||
lastName: string;
|
||||
email: string;
|
||||
phoneNumber: string;
|
||||
dateOfBirth: string;
|
||||
licenseExpiryDate: string;
|
||||
status: DriverStatus;
|
||||
vehicleTypesAuthorized: string[];
|
||||
address?: string | null;
|
||||
emergencyContact?: string | null;
|
||||
notes?: string | null;
|
||||
totalTrips: number;
|
||||
rating: number;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
}
|
||||
|
||||
export type SaveDriverPayload = Omit<
|
||||
Driver,
|
||||
'id' | 'createdAt' | 'updatedAt' | 'totalTrips' | 'rating'
|
||||
>;
|
||||
|
||||
export const driversService = {
|
||||
getAll: (filters: DriverListFilters = {}) => {
|
||||
const params = new URLSearchParams();
|
||||
if (filters.status) params.set('status', filters.status);
|
||||
if (filters.search) params.set('search', filters.search);
|
||||
if (filters.page) params.set('page', filters.page.toString());
|
||||
if (filters.limit) params.set('limit', filters.limit.toString());
|
||||
if (filters.sortBy) params.set('sortBy', filters.sortBy);
|
||||
if (filters.sortOrder) params.set('sortOrder', filters.sortOrder);
|
||||
const qs = params.toString();
|
||||
return apiClient.get<Driver[]>(
|
||||
`${URL_CONSTANTS.DRIVERS.BASE}${qs ? `?${qs}` : ''}`,
|
||||
);
|
||||
},
|
||||
getById: (id: string) => apiClient.get<Driver>(URL_CONSTANTS.DRIVERS.BY_ID(id)),
|
||||
create: (data: Partial<SaveDriverPayload>) =>
|
||||
apiClient.post(URL_CONSTANTS.DRIVERS.BASE, data),
|
||||
update: (id: string, data: Partial<SaveDriverPayload>) =>
|
||||
apiClient.patch(URL_CONSTANTS.DRIVERS.BY_ID(id), data),
|
||||
delete: (id: string) => apiClient.delete(URL_CONSTANTS.DRIVERS.BY_ID(id)),
|
||||
};
|
||||
@@ -7,11 +7,13 @@ import {
|
||||
} from "@/services/locomotives.service";
|
||||
import { trainService, type Train } from "@/services/trains.service";
|
||||
import { wagonService, type Wagon, type WagonListFilters } from "@/services/wagon.service";
|
||||
import { vehiclesService, type Vehicle, type VehicleListFilters } from "@/services/vehicles.service";
|
||||
import { driversService, type Driver, type DriverListFilters } from "@/services/drivers.service";
|
||||
import type { FleetResourceSlug } from "@/pages/fleet/config/resources";
|
||||
|
||||
export type FleetRecord = Locomotive | Train | Wagon | Container | Cargo;
|
||||
export type FleetRecord = Locomotive | Train | Wagon | Container | Cargo | Vehicle | Driver;
|
||||
|
||||
export type FleetListFilters = WagonListFilters & LocomotiveListFilters;
|
||||
export type FleetListFilters = WagonListFilters & LocomotiveListFilters & VehicleListFilters & DriverListFilters;
|
||||
|
||||
const listHandlers: Record<
|
||||
FleetResourceSlug,
|
||||
@@ -22,6 +24,8 @@ const listHandlers: Record<
|
||||
wagons: (filters) => wagonService.getAll(filters ?? {}).then((r) => r.data),
|
||||
containers: () => containerService.getAll().then((r) => r.data),
|
||||
cargoes: () => cargoService.getAll().then((r) => r.data),
|
||||
vehicles: (filters) => vehiclesService.getAll(filters ?? {}).then((r) => r.data),
|
||||
drivers: (filters) => driversService.getAll(filters ?? {}).then((r) => r.data),
|
||||
};
|
||||
|
||||
const createHandlers: Record<FleetResourceSlug, (data: Record<string, unknown>) => Promise<unknown>> = {
|
||||
@@ -30,6 +34,8 @@ const createHandlers: Record<FleetResourceSlug, (data: Record<string, unknown>)
|
||||
wagons: (data) => wagonService.create(data),
|
||||
containers: (data) => containerService.create(data),
|
||||
cargoes: (data) => cargoService.create(data),
|
||||
vehicles: (data) => vehiclesService.create(data),
|
||||
drivers: (data) => driversService.create(data),
|
||||
};
|
||||
|
||||
const updateHandlers: Record<
|
||||
@@ -41,6 +47,8 @@ const updateHandlers: Record<
|
||||
wagons: (id, data) => wagonService.update(id, data),
|
||||
containers: (id, data) => containerService.update(id, data),
|
||||
cargoes: (id, data) => cargoService.update(id, data),
|
||||
vehicles: (id, data) => vehiclesService.update(id, data),
|
||||
drivers: (id, data) => driversService.update(id, data),
|
||||
};
|
||||
|
||||
const removeHandlers: Record<FleetResourceSlug, (id: string) => Promise<unknown>> = {
|
||||
@@ -49,6 +57,8 @@ const removeHandlers: Record<FleetResourceSlug, (id: string) => Promise<unknown>
|
||||
wagons: (id) => wagonService.delete(id),
|
||||
containers: (id) => containerService.delete(id),
|
||||
cargoes: (id) => cargoService.delete(id),
|
||||
vehicles: (id) => vehiclesService.delete(id),
|
||||
drivers: (id) => driversService.delete(id),
|
||||
};
|
||||
|
||||
export const fleetService = {
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
import { api as apiClient } from '../auth/http';
|
||||
import { URL_CONSTANTS } from '@/constants/URLS';
|
||||
|
||||
export type VehicleType = 'TRUCK' | 'VAN' | 'CAR' | 'BUS' | 'TRAILER' | 'TANKER' | 'FLATBED';
|
||||
export type FuelType = 'PETROL' | 'DIESEL' | 'ELECTRIC' | 'HYBRID';
|
||||
export type VehicleStatus = 'ACTIVE' | 'MAINTENANCE' | 'RETIRED' | 'OUT_OF_SERVICE';
|
||||
|
||||
export interface VehicleListFilters {
|
||||
status?: VehicleStatus;
|
||||
search?: string;
|
||||
page?: number;
|
||||
limit?: number;
|
||||
sortBy?: string;
|
||||
sortOrder?: 'ASC' | 'DESC';
|
||||
}
|
||||
|
||||
export interface Vehicle {
|
||||
id: string;
|
||||
plateNumber: string;
|
||||
registrationNumber: string;
|
||||
vehicleType: VehicleType;
|
||||
manufacturer: string;
|
||||
model: string;
|
||||
year: number;
|
||||
fuelType: FuelType;
|
||||
capacity: number;
|
||||
status: VehicleStatus;
|
||||
description?: string | null;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
}
|
||||
|
||||
export type SaveVehiclePayload = Omit<
|
||||
Vehicle,
|
||||
'id' | 'createdAt' | 'updatedAt' | 'registrationNumber'
|
||||
>;
|
||||
|
||||
export const vehiclesService = {
|
||||
getAll: (filters: VehicleListFilters = {}) => {
|
||||
const params = new URLSearchParams();
|
||||
if (filters.status) params.set('status', filters.status);
|
||||
if (filters.search) params.set('search', filters.search);
|
||||
if (filters.page) params.set('page', filters.page.toString());
|
||||
if (filters.limit) params.set('limit', filters.limit.toString());
|
||||
if (filters.sortBy) params.set('sortBy', filters.sortBy);
|
||||
if (filters.sortOrder) params.set('sortOrder', filters.sortOrder);
|
||||
const qs = params.toString();
|
||||
return apiClient.get<Vehicle[]>(
|
||||
`${URL_CONSTANTS.VEHICLES.BASE}${qs ? `?${qs}` : ''}`,
|
||||
);
|
||||
},
|
||||
getById: (id: string) => apiClient.get<Vehicle>(URL_CONSTANTS.VEHICLES.BY_ID(id)),
|
||||
create: (data: Partial<SaveVehiclePayload>) =>
|
||||
apiClient.post(URL_CONSTANTS.VEHICLES.BASE, data),
|
||||
update: (id: string, data: Partial<SaveVehiclePayload>) =>
|
||||
apiClient.patch(URL_CONSTANTS.VEHICLES.BY_ID(id), data),
|
||||
delete: (id: string) => apiClient.delete(URL_CONSTANTS.VEHICLES.BY_ID(id)),
|
||||
};
|
||||
Reference in New Issue
Block a user