126 lines
5 KiB
TypeScript
126 lines
5 KiB
TypeScript
import config from "@payload-config";
|
|
import { getPayload } from "payload";
|
|
import type { GameVehicle, Vehicle } from "@/payload-types";
|
|
import { TruckIcon } from "lucide-react";
|
|
import Link from "next/link";
|
|
import {
|
|
Item,
|
|
ItemContent,
|
|
ItemDescription,
|
|
ItemHeader,
|
|
ItemMedia,
|
|
ItemTitle,
|
|
} from "@/components/ui/item";
|
|
|
|
export const metadata = {
|
|
title: "Vehicles: Polaris Task Force",
|
|
};
|
|
|
|
export default async function GameVehiclesPage() {
|
|
const payload = await getPayload({ config });
|
|
|
|
const vehiclesRes = await payload.find({
|
|
collection: "game-vehicles",
|
|
limit: 500,
|
|
depth: 2,
|
|
});
|
|
|
|
const vehicles = vehiclesRes.docs as unknown as (GameVehicle & {
|
|
type: Vehicle;
|
|
})[];
|
|
|
|
const statusColors: Record<string, string> = {
|
|
idle: "bg-emerald-500/20 text-emerald-400",
|
|
assigned: "bg-blue-500/20 text-blue-400",
|
|
in_transit: "bg-amber-500/20 text-amber-400",
|
|
damaged: "bg-red-500/20 text-red-400",
|
|
destroyed: "bg-red-500/20 text-red-400",
|
|
};
|
|
|
|
return (
|
|
<div className="p-5 flex flex-col gap-6">
|
|
<div className="flex flex-col gap-1">
|
|
<div className="flex items-center gap-2">
|
|
<TruckIcon className="size-5 text-muted-foreground" />
|
|
<h1 className="text-lg font-semibold">Vehicles</h1>
|
|
</div>
|
|
<p className="text-sm text-muted-foreground">
|
|
{vehicles.length} vehicle{vehicles.length !== 1 ? "s" : ""} deployed.
|
|
</p>
|
|
</div>
|
|
|
|
{vehicles.length > 0 ? (
|
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-3">
|
|
{vehicles.map((vehicle) => {
|
|
const vehicleType = typeof vehicle.type === "object" ? vehicle.type : null;
|
|
const fuelPercent = vehicleType?.fuel?.fuelCapacity
|
|
? (vehicle.currentFuel / vehicleType.fuel.fuelCapacity) * 100
|
|
: 0;
|
|
|
|
return (
|
|
<Link key={vehicle.id} href={`/logistics/game-vehicles/${vehicle.id}`}>
|
|
<Item
|
|
variant="outline"
|
|
className="cursor-pointer group transition-all duration-200 hover:scale-[1.01] hover:[box-shadow:inset_0_2px_8px_rgba(255,255,255,0.33)]"
|
|
>
|
|
<ItemHeader>
|
|
<div className="flex items-center gap-2">
|
|
<ItemMedia variant="icon">
|
|
<TruckIcon />
|
|
</ItemMedia>
|
|
<ItemTitle className="flex flex-col">
|
|
<span>{vehicle.name}</span>
|
|
<span className="text-xs font-normal text-muted-foreground">
|
|
{vehicleType?.name ?? "Unknown type"}
|
|
</span>
|
|
</ItemTitle>
|
|
</div>
|
|
<span
|
|
className={`text-[10px] font-semibold uppercase tracking-wider px-1.5 py-0.5 rounded ${statusColors[vehicle.status] ?? "bg-muted text-muted-foreground"}`}
|
|
>
|
|
{vehicle.status === "in_transit"
|
|
? "In Transit"
|
|
: vehicle.status.charAt(0).toUpperCase() + vehicle.status.slice(1)}
|
|
</span>
|
|
</ItemHeader>
|
|
<ItemContent>
|
|
<div className="flex flex-col gap-2 w-full">
|
|
<div className="flex items-center gap-2">
|
|
<div className="flex-1 h-1.5 bg-border rounded-full overflow-hidden">
|
|
<div
|
|
className={`h-full rounded-full transition-all ${
|
|
fuelPercent > 20 ? "bg-green-500" : "bg-red-500"
|
|
}`}
|
|
style={{
|
|
width: `${Math.min(100, fuelPercent)}%`,
|
|
}}
|
|
/>
|
|
</div>
|
|
<span className="text-[10px] font-mono text-muted-foreground">
|
|
{vehicle.currentFuel?.toFixed(0)} /{" "}
|
|
{vehicleType?.fuel?.fuelCapacity ?? "?"} fuel
|
|
</span>
|
|
</div>
|
|
<div className="flex items-center justify-between text-xs text-muted-foreground">
|
|
<span>HP: {vehicle.currentHealth}</span>
|
|
{vehicleType?.fuel?.fuelType &&
|
|
typeof vehicleType.fuel.fuelType === "object" && (
|
|
<span>{vehicleType.fuel.fuelType.name}</span>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</ItemContent>
|
|
<ItemDescription>{vehicleType?.name ?? "Unknown type"}</ItemDescription>
|
|
</Item>
|
|
</Link>
|
|
);
|
|
})}
|
|
</div>
|
|
) : (
|
|
<div className="flex flex-col items-center justify-center py-12 text-muted-foreground">
|
|
<p className="text-sm">No vehicles deployed.</p>
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|