124 lines
4.4 KiB
TypeScript
124 lines
4.4 KiB
TypeScript
import { GameStructure, Resource } from "@/payload-types";
|
|
import { convertLexicalToPlaintext } from "@payloadcms/richtext-lexical/plaintext";
|
|
import { pluralize } from "@/lib/utils";
|
|
import {
|
|
Item,
|
|
ItemContent,
|
|
ItemDescription,
|
|
ItemHeader,
|
|
ItemMedia,
|
|
ItemSeparator,
|
|
ItemTitle,
|
|
} from "@/components/ui/item";
|
|
import { BrainIcon, DropletIcon, PackageOpenIcon, ZapIcon } from "lucide-react";
|
|
|
|
interface ResourceListProps {
|
|
storedResources: GameStructure["storedResources"];
|
|
maxCapacityMass: number | null;
|
|
}
|
|
|
|
function getResourceIcon(type: Resource["type"]) {
|
|
switch (type) {
|
|
case "fluid":
|
|
return <DropletIcon className="size-4" />;
|
|
case "energy":
|
|
return <ZapIcon className="size-4" />;
|
|
case "abstract":
|
|
return <BrainIcon className="size-4" />;
|
|
default:
|
|
return <PackageOpenIcon className="size-4" />;
|
|
}
|
|
}
|
|
|
|
export function ResourceList({ storedResources, maxCapacityMass }: ResourceListProps) {
|
|
const resources = storedResources ?? [];
|
|
|
|
if (resources.length === 0) {
|
|
return (
|
|
<Item variant="outline">
|
|
<ItemHeader>
|
|
<ItemTitle className="flex items-center gap-2">
|
|
<PackageOpenIcon className="size-4 text-muted-foreground" />
|
|
Stored Resources
|
|
</ItemTitle>
|
|
</ItemHeader>
|
|
<ItemContent>
|
|
<p className="text-sm text-muted-foreground py-2">No resources currently stored.</p>
|
|
</ItemContent>
|
|
</Item>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<Item variant="outline">
|
|
<ItemHeader>
|
|
<ItemTitle className="flex items-center gap-2">
|
|
<PackageOpenIcon className="size-4 text-muted-foreground" />
|
|
Stored Resources
|
|
</ItemTitle>
|
|
<ItemDescription>
|
|
{resources.length} resource type{resources.length !== 1 ? "s" : ""}
|
|
</ItemDescription>
|
|
</ItemHeader>
|
|
<ItemContent>
|
|
<div className="flex flex-col">
|
|
{resources.map((entry, index) => {
|
|
const resource = entry.resource as Resource;
|
|
if (!resource || typeof resource === "number") return null;
|
|
|
|
const mass = (resource.massPerUnit ?? 0) * entry.amount;
|
|
const description = resource.description
|
|
? convertLexicalToPlaintext({ data: resource.description })
|
|
: null;
|
|
|
|
return (
|
|
<div key={entry.id ?? index}>
|
|
<div className="flex items-center gap-3 py-2.5">
|
|
<ItemMedia variant="icon">
|
|
{resource.icon ? (
|
|
<img
|
|
src={typeof resource.icon === "object" ? (resource.icon.url ?? "") : ""}
|
|
alt={resource.name}
|
|
className="size-4 object-contain"
|
|
/>
|
|
) : (
|
|
getResourceIcon(resource.type)
|
|
)}
|
|
</ItemMedia>
|
|
<ItemContent className="flex-1 min-w-0">
|
|
<ItemTitle>
|
|
<span
|
|
className="w-2 h-2 rounded-full shrink-0"
|
|
style={{ backgroundColor: resource.color ?? "#888" }}
|
|
/>
|
|
<span>{resource.name}</span>
|
|
<span className="text-xs font-normal text-muted-foreground">
|
|
{entry.amount.toLocaleString()}{" "}
|
|
{pluralize(entry.amount, resource.unitOfMeasure)}
|
|
</span>
|
|
</ItemTitle>
|
|
{description && (
|
|
<ItemDescription className="line-clamp-1">{description}</ItemDescription>
|
|
)}
|
|
</ItemContent>
|
|
<div className="text-right shrink-0">
|
|
<div className="text-sm font-medium">
|
|
{entry.amount.toLocaleString()}{" "}
|
|
{pluralize(entry.amount, resource.unitOfMeasure)}
|
|
</div>
|
|
<div className="text-xs text-muted-foreground">
|
|
{mass > 0 && `${mass.toLocaleString()} kg mass`}
|
|
{mass > 0 && " · "}
|
|
{(resource.baseValue * entry.amount).toLocaleString()} value
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{index < resources.length - 1 && <ItemSeparator />}
|
|
</div>
|
|
);
|
|
})}
|
|
</div>
|
|
</ItemContent>
|
|
</Item>
|
|
);
|
|
}
|