mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-29 17:38:12 +00:00
198 lines
6.8 KiB
TypeScript
198 lines
6.8 KiB
TypeScript
import { useQuery } from "@tanstack/react-query";
|
|
import { Card, Container, Grid, Group, Loader, Select, Stack, Text, Title } from "@mantine/core";
|
|
import Breadcrumbs from "@/components/ui/Breadcrumbs";
|
|
import { api } from "@/auth/http";
|
|
import { QUERY_KEYS } from "@/constants/QUERY_KEYS";
|
|
import { vehiclesService, type Vehicle as VehicleType } from "@/services/vehicles.service";
|
|
import { useState } from "react";
|
|
|
|
export default function FuelStatsPage() {
|
|
const [selectedVehicleId, setSelectedVehicleId] = useState<string>("");
|
|
const [monthsBack, setMonthsBack] = useState<string>("12");
|
|
|
|
// Fetch vehicles
|
|
const { data: vehiclesData } = useQuery({
|
|
queryKey: QUERY_KEYS.VEHICLES.list(),
|
|
queryFn: async () => {
|
|
const res = await vehiclesService.getAll({ limit: 1000 });
|
|
return res.data || [];
|
|
},
|
|
});
|
|
|
|
// Fetch fuel stats
|
|
const { data: statsData, isFetching: isStatsFetching } = useQuery({
|
|
queryKey: ["fuel-stats", selectedVehicleId, monthsBack],
|
|
queryFn: async () => {
|
|
if (!selectedVehicleId) return null;
|
|
const res = await api.get(`/fuel/stats/${selectedVehicleId}?months=${monthsBack}`);
|
|
return res.data;
|
|
},
|
|
enabled: !!selectedVehicleId,
|
|
});
|
|
|
|
const vehicleOptions =
|
|
vehiclesData?.map((v: VehicleType) => ({
|
|
value: v.id,
|
|
label: `${v.plateNumber} - ${v.manufacturer} ${v.model}`,
|
|
})) || [];
|
|
|
|
const selectedVehicle = vehiclesData?.find((v: VehicleType) => v.id === selectedVehicleId);
|
|
|
|
const costPerKm =
|
|
statsData && selectedVehicle?.actualDistanceKm
|
|
? (statsData.totalCost / selectedVehicle.actualDistanceKm).toFixed(2)
|
|
: "—";
|
|
|
|
const efficiency = statsData
|
|
? (statsData.totalLiters > 0 ? (selectedVehicle?.actualDistanceKm || 0) / statsData.totalLiters : 0).toFixed(2)
|
|
: "—";
|
|
|
|
return (
|
|
<Container size="xl" py="xl" px="lg">
|
|
<Breadcrumbs items={[{ label: "Fleet" }, { label: "Fuel Management" }, { label: "Statistics" }]} />
|
|
|
|
<Group justify="space-between" mb="lg">
|
|
<Title order={1}>Fuel Consumption Analysis</Title>
|
|
</Group>
|
|
|
|
{/* Filters */}
|
|
<Card withBorder mb="lg" padding="md">
|
|
<Grid>
|
|
<Grid.Col span={{ base: 12, sm: 6 }}>
|
|
<Select
|
|
label="Vehicle"
|
|
placeholder="Select vehicle to analyze"
|
|
data={vehicleOptions}
|
|
value={selectedVehicleId}
|
|
onChange={(val) => setSelectedVehicleId(val || "")}
|
|
searchable
|
|
/>
|
|
</Grid.Col>
|
|
<Grid.Col span={{ base: 12, sm: 6 }}>
|
|
<Select
|
|
label="Period"
|
|
data={[
|
|
{ value: "3", label: "Last 3 months" },
|
|
{ value: "6", label: "Last 6 months" },
|
|
{ value: "12", label: "Last 12 months" },
|
|
]}
|
|
value={monthsBack}
|
|
onChange={(val) => setMonthsBack(val || "12")}
|
|
/>
|
|
</Grid.Col>
|
|
</Grid>
|
|
</Card>
|
|
|
|
{selectedVehicleId && statsData ? (
|
|
<>
|
|
{/* Stats Cards */}
|
|
<Grid mb="lg">
|
|
<Grid.Col span={{ base: 12, sm: 6, md: 3 }}>
|
|
<Card withBorder padding="lg">
|
|
<Text size="sm" c="dimmed" fw={500}>
|
|
Total Purchases
|
|
</Text>
|
|
<Text fw={700} size="lg">
|
|
{Number(statsData.totalPurchases) || 0}
|
|
</Text>
|
|
</Card>
|
|
</Grid.Col>
|
|
<Grid.Col span={{ base: 12, sm: 6, md: 3 }}>
|
|
<Card withBorder padding="lg">
|
|
<Text size="sm" c="dimmed" fw={500}>
|
|
Total Fuel
|
|
</Text>
|
|
<Text fw={700} size="lg">
|
|
{(Number(statsData.totalLiters) || 0).toFixed(2)} L
|
|
</Text>
|
|
</Card>
|
|
</Grid.Col>
|
|
<Grid.Col span={{ base: 12, sm: 6, md: 3 }}>
|
|
<Card withBorder padding="lg">
|
|
<Text size="sm" c="dimmed" fw={500}>
|
|
Total Cost
|
|
</Text>
|
|
<Text fw={700} size="lg">
|
|
ETB {(Number(statsData.totalCost) || 0).toLocaleString("en-US", { maximumFractionDigits: 2 })}
|
|
</Text>
|
|
</Card>
|
|
</Grid.Col>
|
|
<Grid.Col span={{ base: 12, sm: 6, md: 3 }}>
|
|
<Card withBorder padding="lg">
|
|
<Text size="sm" c="dimmed" fw={500}>
|
|
Avg Price/L
|
|
</Text>
|
|
<Text fw={700} size="lg">
|
|
ETB {(Number(statsData.averagePricePerLiter) || 0).toFixed(2)}
|
|
</Text>
|
|
</Card>
|
|
</Grid.Col>
|
|
</Grid>
|
|
|
|
{/* Efficiency Metrics */}
|
|
<Grid mb="lg">
|
|
<Grid.Col span={{ base: 12, sm: 6 }}>
|
|
<Card withBorder padding="lg">
|
|
<Text size="sm" c="dimmed" fw={500}>
|
|
Fuel Efficiency
|
|
</Text>
|
|
<Text fw={700} size="lg">
|
|
{efficiency} km/L
|
|
</Text>
|
|
</Card>
|
|
</Grid.Col>
|
|
<Grid.Col span={{ base: 12, sm: 6 }}>
|
|
<Card withBorder padding="lg">
|
|
<Text size="sm" c="dimmed" fw={500}>
|
|
Cost per KM
|
|
</Text>
|
|
<Text fw={700} size="lg">
|
|
ETB {costPerKm}
|
|
</Text>
|
|
</Card>
|
|
</Grid.Col>
|
|
</Grid>
|
|
|
|
{/* Summary */}
|
|
<Card withBorder padding="lg">
|
|
<Stack gap="md">
|
|
<div>
|
|
<Text fw={600} mb="xs">
|
|
Summary
|
|
</Text>
|
|
<Text size="sm">
|
|
{selectedVehicle?.plateNumber} consumed{" "}
|
|
<Text fw={700} span>
|
|
{(Number(statsData.totalLiters) || 0).toFixed(2)} liters
|
|
</Text>{" "}
|
|
over the last {monthsBack} months, costing{" "}
|
|
<Text fw={700} span>
|
|
ETB {(Number(statsData.totalCost) || 0).toLocaleString("en-US", { maximumFractionDigits: 2 })}
|
|
</Text>
|
|
. Average fuel price was{" "}
|
|
<Text fw={700} span>
|
|
ETB {(Number(statsData.averagePricePerLiter) || 0).toFixed(2)} per liter
|
|
</Text>
|
|
.
|
|
</Text>
|
|
</div>
|
|
</Stack>
|
|
</Card>
|
|
</>
|
|
) : selectedVehicleId && isStatsFetching ? (
|
|
<Card withBorder padding="lg">
|
|
<Group justify="center">
|
|
<Loader />
|
|
</Group>
|
|
</Card>
|
|
) : (
|
|
<Card withBorder padding="lg">
|
|
<Text c="dimmed" ta="center">
|
|
Select a vehicle to view fuel consumption statistics
|
|
</Text>
|
|
</Card>
|
|
)}
|
|
</Container>
|
|
);
|
|
}
|