- Rebuild /logistics/storage overview with capacity bars, filters, and structure cards - Add /logistics/structures/[id] detail page with resource list, storage rules, upkeep - Add server actions for deposit, withdraw, and transfer of resources - Add ManageStorageDialog with deposit/withdraw/transfer tabs - Add capacity bar component with color-coded utilization - Add StorageGrid, StructureCard, StorageFilters, ResourceList, StorageRules components - Add logistics utility functions for mass calculations - Install shadcn dialog, select, tabs, label components
118 lines
4.1 KiB
TypeScript
118 lines
4.1 KiB
TypeScript
"use client";
|
||
|
||
import Link from "next/link";
|
||
import {
|
||
Item,
|
||
ItemContent,
|
||
ItemDescription,
|
||
ItemHeader,
|
||
ItemMedia,
|
||
ItemTitle,
|
||
} from "@/components/ui/item";
|
||
import { CapacityBar } from "@/components/frontend/storage/CapacityBar";
|
||
import {
|
||
getCategoryLabel,
|
||
getFunctionTypeLabel,
|
||
getGridSlots,
|
||
getResourceCount,
|
||
calculateStoredMass,
|
||
calculateUtilization,
|
||
} from "@/lib/logistics";
|
||
import { GameStructure, Faction, Structure, Resource } from "@/payload-types";
|
||
import {
|
||
BoxIcon,
|
||
Building2Icon,
|
||
ChevronRightIcon,
|
||
Grid3x3Icon,
|
||
MapPinIcon,
|
||
} from "lucide-react";
|
||
|
||
interface StructureCardProps {
|
||
structure: GameStructure & {
|
||
type: Structure;
|
||
faction?: Faction | null;
|
||
};
|
||
}
|
||
|
||
export function StructureCard({ structure }: StructureCardProps) {
|
||
const structureType = structure.type;
|
||
const storedResources = structure.storedResources ?? [];
|
||
|
||
const currentMass = calculateStoredMass(storedResources);
|
||
const utilization = calculateUtilization(structureType, storedResources);
|
||
const resourceCount = getResourceCount(storedResources);
|
||
const gridSlots = getGridSlots(structureType);
|
||
const functionLabel = getFunctionTypeLabel(structureType.functionType);
|
||
|
||
return (
|
||
<Link href={`/logistics/structures/${structure.id}`}>
|
||
<Item variant="outline" className="cursor-pointer group">
|
||
<ItemHeader>
|
||
<div className="flex items-center gap-2">
|
||
<ItemMedia variant="icon">
|
||
<Building2Icon />
|
||
</ItemMedia>
|
||
<ItemTitle className="flex flex-col">
|
||
<span>{structure.name}</span>
|
||
<span className="text-xs font-normal text-muted-foreground">
|
||
{structureType.name}
|
||
</span>
|
||
</ItemTitle>
|
||
</div>
|
||
<div className="flex items-center gap-2">
|
||
{structure.faction && typeof structure.faction === "object" && (
|
||
<span className="text-xs text-muted-foreground bg-muted px-2 py-0.5 rounded-md">
|
||
{structure.faction.name}
|
||
</span>
|
||
)}
|
||
<span className="text-xs text-muted-foreground bg-muted px-2 py-0.5 rounded-md">
|
||
{getCategoryLabel(structureType.category)}
|
||
</span>
|
||
</div>
|
||
</ItemHeader>
|
||
<ItemContent>
|
||
<div className="flex flex-col gap-3 w-full">
|
||
<CapacityBar
|
||
utilization={utilization}
|
||
currentMass={currentMass}
|
||
maxMass={structureType.maxCapacityMass ?? null}
|
||
/>
|
||
<div className="flex items-center justify-between text-xs text-muted-foreground">
|
||
<div className="flex items-center gap-3">
|
||
<span className="flex items-center gap-1">
|
||
<BoxIcon className="size-3" />
|
||
{resourceCount} resource{resourceCount !== 1 ? "s" : ""}
|
||
</span>
|
||
{gridSlots !== null && (
|
||
<span className="flex items-center gap-1">
|
||
<Grid3x3Icon className="size-3" />
|
||
{structureType.xSize}×{structureType.ySize} grid
|
||
</span>
|
||
)}
|
||
{functionLabel && (
|
||
<span className="flex items-center gap-1">
|
||
{functionLabel}
|
||
</span>
|
||
)}
|
||
</div>
|
||
{structure.coordinates && (
|
||
<span className="flex items-center gap-1">
|
||
<MapPinIcon className="size-3" />
|
||
{structure.coordinates[0].toFixed(1)}, {structure.coordinates[1].toFixed(1)}
|
||
</span>
|
||
)}
|
||
</div>
|
||
</div>
|
||
</ItemContent>
|
||
<ItemDescription>
|
||
{resourceCount === 0
|
||
? "Empty — no resources stored."
|
||
: `${resourceCount} resource type${resourceCount !== 1 ? "s" : ""} stored`}
|
||
</ItemDescription>
|
||
<span className="text-xs text-muted-foreground group-hover:text-primary transition-colors flex items-center gap-1">
|
||
View details <ChevronRightIcon className="size-3" />
|
||
</span>
|
||
</Item>
|
||
</Link>
|
||
);
|
||
}
|