feat(logistics): add game-vehicles collection and deployed vehicle views
This commit is contained in:
parent
9a5952ec99
commit
d6ba4ccc39
4 changed files with 649 additions and 0 deletions
320
src/app/(frontend)/logistics/game-vehicles/[id]/page.tsx
Normal file
320
src/app/(frontend)/logistics/game-vehicles/[id]/page.tsx
Normal file
|
|
@ -0,0 +1,320 @@
|
||||||
|
import config from "@payload-config";
|
||||||
|
import { getPayload } from "payload";
|
||||||
|
import { notFound } from "next/navigation";
|
||||||
|
import Link from "next/link";
|
||||||
|
import type {
|
||||||
|
GameVehicle,
|
||||||
|
Vehicle,
|
||||||
|
GameStructure,
|
||||||
|
Structure,
|
||||||
|
Resource,
|
||||||
|
} from "@/payload-types";
|
||||||
|
import {
|
||||||
|
ArrowLeftIcon,
|
||||||
|
FuelIcon,
|
||||||
|
HeartIcon,
|
||||||
|
MapPinIcon,
|
||||||
|
ShieldIcon,
|
||||||
|
TruckIcon,
|
||||||
|
} from "lucide-react";
|
||||||
|
import {
|
||||||
|
Item,
|
||||||
|
ItemContent,
|
||||||
|
ItemDescription,
|
||||||
|
ItemHeader,
|
||||||
|
ItemMedia,
|
||||||
|
ItemTitle,
|
||||||
|
} from "@/components/ui/item";
|
||||||
|
|
||||||
|
interface GameVehicleDetailPageProps {
|
||||||
|
params: Promise<{ id: string }>;
|
||||||
|
}
|
||||||
|
|
||||||
|
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",
|
||||||
|
};
|
||||||
|
|
||||||
|
export default async function GameVehicleDetailPage({
|
||||||
|
params,
|
||||||
|
}: GameVehicleDetailPageProps) {
|
||||||
|
const { id } = await params;
|
||||||
|
const payload = await getPayload({ config });
|
||||||
|
|
||||||
|
const vehicle = await payload
|
||||||
|
.findByID({
|
||||||
|
collection: "game-vehicles",
|
||||||
|
id: Number(id),
|
||||||
|
depth: 2,
|
||||||
|
})
|
||||||
|
.catch(() => null);
|
||||||
|
|
||||||
|
if (!vehicle) {
|
||||||
|
notFound();
|
||||||
|
}
|
||||||
|
|
||||||
|
const vehicleData = vehicle as unknown as GameVehicle & {
|
||||||
|
type: Vehicle;
|
||||||
|
};
|
||||||
|
const vehicleType =
|
||||||
|
typeof vehicleData.type === "object" ? vehicleData.type : null;
|
||||||
|
const deployedAt =
|
||||||
|
typeof vehicleData.deployedAt === "object" &&
|
||||||
|
vehicleData.deployedAt !== null
|
||||||
|
? (vehicleData.deployedAt as GameStructure & { type: Structure })
|
||||||
|
: null;
|
||||||
|
const faction =
|
||||||
|
typeof vehicleData.faction === "object" && vehicleData.faction !== null
|
||||||
|
? vehicleData.faction
|
||||||
|
: null;
|
||||||
|
|
||||||
|
const fuelPercent = vehicleType?.fuel?.fuelCapacity
|
||||||
|
? (vehicleData.currentFuel / vehicleType.fuel.fuelCapacity) * 100
|
||||||
|
: 0;
|
||||||
|
|
||||||
|
const storedResources = vehicleData.storedResources ?? [];
|
||||||
|
const resourceCount = storedResources.length;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="p-5 flex flex-col gap-6 max-w-7xl">
|
||||||
|
<Link
|
||||||
|
href="/logistics/game-vehicles"
|
||||||
|
className="text-sm text-muted-foreground hover:text-primary transition-colors flex items-center gap-1 w-fit"
|
||||||
|
>
|
||||||
|
<ArrowLeftIcon className="size-3" />
|
||||||
|
Back to Vehicles
|
||||||
|
</Link>
|
||||||
|
|
||||||
|
<div className="flex items-start justify-between gap-4">
|
||||||
|
<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">{vehicleData.name}</h1>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<span
|
||||||
|
className={`text-[10px] font-semibold uppercase tracking-wider px-1.5 py-0.5 rounded ${statusColors[vehicleData.status] ?? "bg-muted text-muted-foreground"}`}
|
||||||
|
>
|
||||||
|
{vehicleData.status === "in_transit"
|
||||||
|
? "In Transit"
|
||||||
|
: vehicleData.status.charAt(0).toUpperCase() +
|
||||||
|
vehicleData.status.slice(1)}
|
||||||
|
</span>
|
||||||
|
<span className="text-xs text-muted-foreground">
|
||||||
|
{vehicleType?.name ?? "Unknown type"}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="grid grid-cols-2 md:grid-cols-4 gap-3">
|
||||||
|
<Item variant="muted" size="sm">
|
||||||
|
<ItemMedia variant="icon">
|
||||||
|
<FuelIcon />
|
||||||
|
</ItemMedia>
|
||||||
|
<ItemContent>
|
||||||
|
<ItemTitle>Fuel</ItemTitle>
|
||||||
|
<ItemDescription>
|
||||||
|
{vehicleData.currentFuel?.toFixed(1)} /{" "}
|
||||||
|
{vehicleType?.fuel?.fuelCapacity ?? "?"}
|
||||||
|
</ItemDescription>
|
||||||
|
</ItemContent>
|
||||||
|
</Item>
|
||||||
|
<Item variant="muted" size="sm">
|
||||||
|
<ItemMedia variant="icon">
|
||||||
|
<HeartIcon />
|
||||||
|
</ItemMedia>
|
||||||
|
<ItemContent>
|
||||||
|
<ItemTitle>Health</ItemTitle>
|
||||||
|
<ItemDescription>
|
||||||
|
{vehicleData.currentHealth}
|
||||||
|
</ItemDescription>
|
||||||
|
</ItemContent>
|
||||||
|
</Item>
|
||||||
|
<Item variant="muted" size="sm">
|
||||||
|
<ItemMedia variant="icon">
|
||||||
|
<MapPinIcon />
|
||||||
|
</ItemMedia>
|
||||||
|
<ItemContent>
|
||||||
|
<ItemTitle>Location</ItemTitle>
|
||||||
|
<ItemDescription>
|
||||||
|
{deployedAt?.name ?? "In transit"}
|
||||||
|
</ItemDescription>
|
||||||
|
</ItemContent>
|
||||||
|
</Item>
|
||||||
|
<Item variant="muted" size="sm">
|
||||||
|
<ItemMedia variant="icon">
|
||||||
|
<ShieldIcon />
|
||||||
|
</ItemMedia>
|
||||||
|
<ItemContent>
|
||||||
|
<ItemTitle>Faction</ItemTitle>
|
||||||
|
<ItemDescription>
|
||||||
|
{faction?.name ?? "Unassigned"}
|
||||||
|
</ItemDescription>
|
||||||
|
</ItemContent>
|
||||||
|
</Item>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="grid grid-cols-1 xl:grid-cols-3 gap-6">
|
||||||
|
<div className="xl:col-span-2 flex flex-col gap-6">
|
||||||
|
{/* Fuel Gauge */}
|
||||||
|
<Item variant="outline">
|
||||||
|
<ItemHeader>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<FuelIcon className="size-4 text-muted-foreground" />
|
||||||
|
<ItemTitle>Fuel Tank</ItemTitle>
|
||||||
|
</div>
|
||||||
|
</ItemHeader>
|
||||||
|
<ItemContent className="flex flex-col gap-3">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<div className="flex-1 h-3 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-sm font-mono font-semibold">
|
||||||
|
{fuelPercent.toFixed(0)}%
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex justify-between text-xs text-muted-foreground">
|
||||||
|
<span>
|
||||||
|
{vehicleData.currentFuel?.toFixed(1)} units remaining
|
||||||
|
</span>
|
||||||
|
<span>
|
||||||
|
Capacity: {vehicleType?.fuel?.fuelCapacity ?? "?"} units
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
{vehicleType?.fuel?.fuelType &&
|
||||||
|
typeof vehicleType.fuel.fuelType === "object" && (
|
||||||
|
<div className="text-xs text-muted-foreground">
|
||||||
|
Fuel type: {vehicleType.fuel.fuelType.name} · Consumption
|
||||||
|
rate: {vehicleType.fuel.fuelConsumptionRate}/unit
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</ItemContent>
|
||||||
|
</Item>
|
||||||
|
|
||||||
|
{/* Cargo */}
|
||||||
|
<Item variant="outline">
|
||||||
|
<ItemHeader>
|
||||||
|
<ItemTitle>Cargo</ItemTitle>
|
||||||
|
<span className="text-xs text-muted-foreground">
|
||||||
|
{resourceCount} item type{resourceCount !== 1 ? "s" : ""}
|
||||||
|
</span>
|
||||||
|
</ItemHeader>
|
||||||
|
<ItemContent>
|
||||||
|
{storedResources.length > 0 ? (
|
||||||
|
<div className="border border-border rounded-lg overflow-hidden">
|
||||||
|
<table className="w-full text-sm">
|
||||||
|
<thead>
|
||||||
|
<tr className="bg-muted/50 border-b border-border">
|
||||||
|
<th className="text-left px-4 py-2.5 font-semibold uppercase text-xs tracking-wider text-muted-foreground">
|
||||||
|
Resource
|
||||||
|
</th>
|
||||||
|
<th className="text-right px-4 py-2.5 font-semibold uppercase text-xs tracking-wider text-muted-foreground">
|
||||||
|
Amount
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{storedResources.map((entry, index) => {
|
||||||
|
const resource =
|
||||||
|
typeof entry.resource === "object"
|
||||||
|
? (entry.resource as Resource)
|
||||||
|
: null;
|
||||||
|
return (
|
||||||
|
<tr
|
||||||
|
key={index}
|
||||||
|
className="border-b border-border/50"
|
||||||
|
>
|
||||||
|
<td className="px-4 py-2 font-medium">
|
||||||
|
{resource?.name ?? `Resource #${entry.resource}`}
|
||||||
|
</td>
|
||||||
|
<td className="px-4 py-2 text-right font-mono">
|
||||||
|
{entry.amount?.toLocaleString()}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<p className="text-sm text-muted-foreground italic">
|
||||||
|
Vehicle cargo is empty.
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</ItemContent>
|
||||||
|
</Item>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Sidebar */}
|
||||||
|
<div className="flex flex-col gap-4">
|
||||||
|
{/* Vehicle Type Info */}
|
||||||
|
<Item variant="outline">
|
||||||
|
<ItemHeader>
|
||||||
|
<ItemTitle>Vehicle Type</ItemTitle>
|
||||||
|
</ItemHeader>
|
||||||
|
<ItemContent>
|
||||||
|
<div className="flex flex-col gap-2 text-sm">
|
||||||
|
<div>
|
||||||
|
<p className="font-semibold">
|
||||||
|
{vehicleType?.name ?? "Unknown"}
|
||||||
|
</p>
|
||||||
|
{vehicleType?.classname && (
|
||||||
|
<p className="text-xs text-muted-foreground font-mono">
|
||||||
|
{vehicleType.classname}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
{vehicleType?.category &&
|
||||||
|
vehicleType.category.length > 0 && (
|
||||||
|
<div className="flex flex-wrap gap-1">
|
||||||
|
{vehicleType.category.map((cat) => (
|
||||||
|
<span
|
||||||
|
key={cat}
|
||||||
|
className="text-[10px] font-semibold uppercase tracking-wider px-1.5 py-0.5 rounded bg-muted"
|
||||||
|
>
|
||||||
|
{cat}
|
||||||
|
</span>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</ItemContent>
|
||||||
|
</Item>
|
||||||
|
|
||||||
|
{/* Location */}
|
||||||
|
{deployedAt && (
|
||||||
|
<Link href={`/logistics/structures/${deployedAt.id}`}>
|
||||||
|
<Item
|
||||||
|
variant="outline"
|
||||||
|
className="cursor-pointer group transition-all duration-200 hover:scale-[1.01]"
|
||||||
|
>
|
||||||
|
<ItemHeader>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<MapPinIcon className="size-4 text-muted-foreground" />
|
||||||
|
<ItemTitle>Stationed At</ItemTitle>
|
||||||
|
</div>
|
||||||
|
</ItemHeader>
|
||||||
|
<ItemContent>
|
||||||
|
<p className="text-sm font-semibold group-hover:text-primary transition-colors">
|
||||||
|
{deployedAt.name}
|
||||||
|
</p>
|
||||||
|
</ItemContent>
|
||||||
|
</Item>
|
||||||
|
</Link>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
137
src/app/(frontend)/logistics/game-vehicles/page.tsx
Normal file
137
src/app/(frontend)/logistics/game-vehicles/page.tsx
Normal file
|
|
@ -0,0 +1,137 @@
|
||||||
|
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>
|
||||||
|
);
|
||||||
|
}
|
||||||
154
src/collections/game/GameVehicles.ts
Normal file
154
src/collections/game/GameVehicles.ts
Normal file
|
|
@ -0,0 +1,154 @@
|
||||||
|
import { CollectionConfig } from "payload";
|
||||||
|
import { isDeveloper } from "@/utils/access-control/isRole";
|
||||||
|
|
||||||
|
export const GameVehicles: CollectionConfig = {
|
||||||
|
slug: "game-vehicles",
|
||||||
|
admin: {
|
||||||
|
group: "Game",
|
||||||
|
useAsTitle: "name",
|
||||||
|
},
|
||||||
|
access: {
|
||||||
|
read: ({ req }) => !!req.user,
|
||||||
|
create: isDeveloper,
|
||||||
|
update: isDeveloper,
|
||||||
|
delete: isDeveloper,
|
||||||
|
},
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: "name",
|
||||||
|
type: "text",
|
||||||
|
required: true,
|
||||||
|
admin: {
|
||||||
|
description: "Display name or callsign for this vehicle instance.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "type",
|
||||||
|
type: "relationship",
|
||||||
|
relationTo: "vehicles",
|
||||||
|
required: true,
|
||||||
|
admin: {
|
||||||
|
description: "The vehicle type/template this instance is based on.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "deployedAt",
|
||||||
|
type: "relationship",
|
||||||
|
relationTo: "game-structures",
|
||||||
|
admin: {
|
||||||
|
description: "The structure where this vehicle is currently stationed. Null if in transit.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "faction",
|
||||||
|
type: "relationship",
|
||||||
|
relationTo: "factions",
|
||||||
|
admin: {
|
||||||
|
description: "The faction that owns this vehicle.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "status",
|
||||||
|
type: "select",
|
||||||
|
options: [
|
||||||
|
{ label: "Idle", value: "idle" },
|
||||||
|
{ label: "Assigned", value: "assigned" },
|
||||||
|
{ label: "In Transit", value: "in_transit" },
|
||||||
|
{ label: "Damaged", value: "damaged" },
|
||||||
|
{ label: "Destroyed", value: "destroyed" },
|
||||||
|
],
|
||||||
|
defaultValue: "idle",
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "currentFuel",
|
||||||
|
type: "number",
|
||||||
|
required: true,
|
||||||
|
defaultValue: 0,
|
||||||
|
admin: {
|
||||||
|
description: "Current fuel level. Must be between 0 and the vehicle type's fuel capacity.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "currentHealth",
|
||||||
|
type: "number",
|
||||||
|
required: true,
|
||||||
|
admin: {
|
||||||
|
description: "Current health. Must be between 0 and the vehicle type's max health.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "storedResources",
|
||||||
|
type: "array",
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: "resource",
|
||||||
|
type: "relationship",
|
||||||
|
relationTo: "resources",
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "amount",
|
||||||
|
type: "number",
|
||||||
|
required: true,
|
||||||
|
min: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "gridX",
|
||||||
|
type: "number",
|
||||||
|
required: true,
|
||||||
|
min: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "gridY",
|
||||||
|
type: "number",
|
||||||
|
required: true,
|
||||||
|
min: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "rotated",
|
||||||
|
type: "checkbox",
|
||||||
|
defaultValue: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "voidStorage",
|
||||||
|
type: "array",
|
||||||
|
admin: {
|
||||||
|
description: "Items displaced by grid resizing.",
|
||||||
|
},
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: "resource",
|
||||||
|
type: "relationship",
|
||||||
|
relationTo: "resources",
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "amount",
|
||||||
|
type: "number",
|
||||||
|
required: true,
|
||||||
|
min: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "gridX",
|
||||||
|
type: "number",
|
||||||
|
required: true,
|
||||||
|
min: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "gridY",
|
||||||
|
type: "number",
|
||||||
|
required: true,
|
||||||
|
min: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "rotated",
|
||||||
|
type: "checkbox",
|
||||||
|
defaultValue: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
@ -92,6 +92,44 @@ export const Vehicles: CollectionConfig = {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "fuel",
|
||||||
|
type: "group",
|
||||||
|
admin: {
|
||||||
|
description:
|
||||||
|
"Fuel configuration for this vehicle. Defines what fuel type it uses, how much it can hold, and how fast it consumes fuel.",
|
||||||
|
},
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: "fuelType",
|
||||||
|
type: "relationship",
|
||||||
|
relationTo: "resources",
|
||||||
|
required: true,
|
||||||
|
admin: {
|
||||||
|
description:
|
||||||
|
"The type of fuel this vehicle consumes (e.g., gasoline, diesel, jet fuel).",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "fuelCapacity",
|
||||||
|
type: "number",
|
||||||
|
required: true,
|
||||||
|
admin: {
|
||||||
|
description: "Maximum fuel units this vehicle's tank can hold.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "fuelConsumptionRate",
|
||||||
|
type: "number",
|
||||||
|
required: true,
|
||||||
|
defaultValue: 1,
|
||||||
|
admin: {
|
||||||
|
description:
|
||||||
|
"Fuel consumed per unit of distance traveled.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue