1
0
Fork 0
polaris-task-force/src/components/frontend/storage/StructureCard.tsx
Z8MB1E 12d276d44d feat: implement storage system with capacity tracking and resource management
- 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
2026-07-27 19:19:48 -04:00

118 lines
4.1 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"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>
);
}