300 lines
8.5 KiB
TypeScript
300 lines
8.5 KiB
TypeScript
import {
|
|
Card,
|
|
CardContent,
|
|
CardDescription,
|
|
CardFooter,
|
|
CardHeader,
|
|
CardTitle,
|
|
} from '@/views/components/ui/card';
|
|
import {
|
|
ChartContainer,
|
|
ChartLegend,
|
|
ChartLegendContent,
|
|
ChartTooltip,
|
|
ChartTooltipContent,
|
|
type ChartConfig,
|
|
} from '@/views/components/ui/chart';
|
|
import { Progress } from '@/views/components/ui/progress';
|
|
import { TrendingUp } from 'lucide-react';
|
|
import {
|
|
Bar,
|
|
BarChart,
|
|
CartesianGrid,
|
|
Line,
|
|
LineChart,
|
|
Pie,
|
|
PieChart,
|
|
XAxis,
|
|
} from 'recharts';
|
|
import { Header } from './views/components/header';
|
|
|
|
export const description = 'A stacked bar chart with a legend';
|
|
|
|
export function App() {
|
|
const stats = [
|
|
{
|
|
title: 'Média de Consumo (R$)',
|
|
value: 'R$ 0,00',
|
|
description: '',
|
|
progress: 44.6,
|
|
},
|
|
{
|
|
title: 'Total de KM Percorridos',
|
|
value: '0 KM',
|
|
description: '',
|
|
progress: 30.6,
|
|
},
|
|
{
|
|
title: 'Viagens Realizadas no Mês',
|
|
value: '0',
|
|
description: '',
|
|
progress: 24.8,
|
|
},
|
|
];
|
|
|
|
const evolutionConfig = {
|
|
price: {
|
|
label: 'Preço',
|
|
color: '#0e233d',
|
|
},
|
|
} satisfies ChartConfig;
|
|
|
|
const evolutionData = [
|
|
{ month: 'Shell', price: 186 },
|
|
{ month: 'Ipiranga', price: 305 },
|
|
{ month: 'BR', price: 237 },
|
|
];
|
|
|
|
const bestPricesConfig = {
|
|
price: {
|
|
label: 'Preço',
|
|
color: '#0e233d',
|
|
},
|
|
} satisfies ChartConfig;
|
|
|
|
const bestPricesData = [
|
|
{ station: 'Shell', price: 5.42 },
|
|
{ station: 'Ipiranga', price: 5.35 },
|
|
{ station: 'BR', price: 5.38 },
|
|
{ station: 'Ale', price: 5.3 },
|
|
];
|
|
|
|
const vehicleStatusConfig = {
|
|
active: {
|
|
label: 'Ativos',
|
|
color: '#0e233d',
|
|
},
|
|
maintenance: {
|
|
label: 'Manutenção',
|
|
color: '#173b63',
|
|
},
|
|
inactive: {
|
|
label: 'Inativos',
|
|
color: '#154677',
|
|
},
|
|
reserved: {
|
|
label: 'Reservados',
|
|
color: '#145190',
|
|
},
|
|
} satisfies ChartConfig;
|
|
|
|
const vehicleStatusData = [
|
|
{ status: 'active', count: 45, fill: '#0e233d' },
|
|
{ status: 'maintenance', count: 12, fill: '#173b63' },
|
|
{ status: 'inactive', count: 8, fill: '#154677' },
|
|
{ status: 'reserved', count: 15, fill: '#145190' },
|
|
];
|
|
|
|
const mileageConfig = {
|
|
company: {
|
|
label: 'Frota Empresa',
|
|
color: '#0e233d',
|
|
},
|
|
rented: {
|
|
label: 'Frota Terceirizada',
|
|
color: '#173b63',
|
|
},
|
|
} satisfies ChartConfig;
|
|
|
|
const mileageData = [
|
|
{ month: 'Jan', company: 1200, rented: 800 },
|
|
{ month: 'Fev', company: 1350, rented: 750 },
|
|
{ month: 'Mar', company: 1100, rented: 900 },
|
|
{ month: 'Abr', company: 1400, rented: 600 },
|
|
{ month: 'Mai', company: 1250, rented: 850 },
|
|
{ month: 'Jun', company: 1300, rented: 700 },
|
|
];
|
|
|
|
return (
|
|
<div className="p-[18px] flex flex-col gap-6">
|
|
<Header />
|
|
|
|
<div className="grid gap-4 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-2 xl:grid-cols-3">
|
|
{stats.map((stat) => (
|
|
<Card
|
|
key={stat.title}
|
|
className="hover:shadow-md transition-shadow duration-300"
|
|
>
|
|
<CardHeader className="pb-2">
|
|
<CardTitle className="text-sm font-medium text-muted-foreground">
|
|
{stat.title}
|
|
</CardTitle>
|
|
<div className="text-4xl font-bold">{stat.value}</div>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<p className="text-xs text-muted-foreground mb-4">
|
|
{stat.description}
|
|
</p>
|
|
<Progress value={stat.progress} className="h-2" />
|
|
</CardContent>
|
|
</Card>
|
|
))}
|
|
</div>
|
|
|
|
<div className="grid gap-6 grid-cols-1 md:grid-cols-2">
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle>Evolução do Consumo de Combustível</CardTitle>
|
|
<CardDescription>
|
|
Comparativo de preços entre os principais postos
|
|
</CardDescription>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<ChartContainer config={evolutionConfig}>
|
|
<LineChart
|
|
accessibilityLayer
|
|
data={evolutionData}
|
|
margin={{
|
|
left: 12,
|
|
right: 12,
|
|
}}
|
|
>
|
|
<CartesianGrid vertical={false} />
|
|
<XAxis
|
|
dataKey="month"
|
|
tickLine={false}
|
|
axisLine={false}
|
|
tickMargin={8}
|
|
/>
|
|
<ChartTooltip
|
|
cursor={false}
|
|
content={<ChartTooltipContent hideLabel />}
|
|
/>
|
|
<Line
|
|
dataKey="price"
|
|
type="natural"
|
|
stroke="var(--color-price)"
|
|
strokeWidth={2}
|
|
dot={{
|
|
fill: 'var(--color-price)',
|
|
}}
|
|
activeDot={{
|
|
r: 6,
|
|
}}
|
|
/>
|
|
</LineChart>
|
|
</ChartContainer>
|
|
</CardContent>
|
|
</Card>
|
|
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle>Postos com Melhor Preço</CardTitle>
|
|
<CardDescription>
|
|
Comparativo de preços entre os principais postos
|
|
</CardDescription>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<ChartContainer config={bestPricesConfig}>
|
|
<BarChart accessibilityLayer data={bestPricesData}>
|
|
<CartesianGrid vertical={false} />
|
|
<XAxis
|
|
dataKey="station"
|
|
tickLine={false}
|
|
tickMargin={10}
|
|
axisLine={false}
|
|
/>
|
|
<ChartTooltip
|
|
cursor={false}
|
|
content={<ChartTooltipContent hideLabel />}
|
|
/>
|
|
<Bar dataKey="price" fill="var(--color-price)" radius={8} />
|
|
</BarChart>
|
|
</ChartContainer>
|
|
</CardContent>
|
|
</Card>
|
|
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle>Quilometragem Percorrida</CardTitle>
|
|
<CardDescription>
|
|
Total de quilômetros percorridos nos últimos meses
|
|
</CardDescription>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<ChartContainer config={mileageConfig}>
|
|
<BarChart accessibilityLayer data={mileageData}>
|
|
<CartesianGrid vertical={false} />
|
|
<XAxis
|
|
dataKey="month"
|
|
tickLine={false}
|
|
tickMargin={10}
|
|
axisLine={false}
|
|
/>
|
|
<ChartTooltip content={<ChartTooltipContent hideLabel />} />
|
|
<ChartLegend content={<ChartLegendContent />} />
|
|
<Bar
|
|
dataKey="company"
|
|
stackId="a"
|
|
fill="var(--color-company)"
|
|
radius={[0, 0, 4, 4]}
|
|
/>
|
|
<Bar
|
|
dataKey="rented"
|
|
stackId="a"
|
|
fill="var(--color-rented)"
|
|
radius={[4, 4, 0, 0]}
|
|
/>
|
|
</BarChart>
|
|
</ChartContainer>
|
|
</CardContent>
|
|
</Card>
|
|
|
|
<Card className="flex flex-col">
|
|
<CardHeader className="items-center pb-0">
|
|
<CardTitle>Status dos Veículos</CardTitle>
|
|
<CardDescription>
|
|
Distribuição dos veículos por status de operação
|
|
</CardDescription>
|
|
</CardHeader>
|
|
<CardContent className="flex-1 pb-0">
|
|
<ChartContainer
|
|
config={vehicleStatusConfig}
|
|
className="[&_.recharts-pie-label-text]:fill-foreground mx-auto aspect-square max-h-[320px] pb-0"
|
|
>
|
|
<PieChart>
|
|
<ChartTooltip content={<ChartTooltipContent hideLabel />} />
|
|
<Pie
|
|
data={vehicleStatusData}
|
|
dataKey="count"
|
|
label
|
|
nameKey="status"
|
|
/>
|
|
</PieChart>
|
|
</ChartContainer>
|
|
</CardContent>
|
|
<CardFooter className="flex-col gap-2 text-sm">
|
|
<div className="flex items-center gap-2 leading-none font-medium">
|
|
Frota operando com 85% de disponibilidade{' '}
|
|
<TrendingUp className="h-4 w-4" />
|
|
</div>
|
|
<div className="text-muted-foreground leading-none">
|
|
Status atual da frota de veículos
|
|
</div>
|
|
</CardFooter>
|
|
</Card>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|