Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ const BuildingContext = ({ building, nodeId }: BuildingContextProps) => {
<Separator />

<ProductionMethodSection
buildingType={building.type}
productionMethod={productionMethod}
productionState={productionState}
availableMethods={availableMethods}
Expand Down
59 changes: 53 additions & 6 deletions src/components/Factory/Context/Building/Production/Inputs.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,36 @@
import { RESOURCES } from "@/components/Factory/data/resources";
import { SPECIAL_BUILDINGS } from "@/components/Factory/data/buildings";
import {
getResourceTypeFoodValue,
RESOURCES,
} from "@/components/Factory/data/resources";
import type { BuildingType } from "@/components/Factory/types/buildings";
import type { ProductionMethod } from "@/components/Factory/types/production";
import { BlockStack, InlineStack } from "@/components/ui/layout";
import { Text } from "@/components/ui/typography";

interface ProductionInputsProps {
productionMethod: ProductionMethod;
buildingType: BuildingType;
}

export const ProductionInputs = ({
productionMethod,
buildingType,
}: ProductionInputsProps) => {
if (productionMethod.inputs.length === 0) return null;

const isSpecialBuilding = SPECIAL_BUILDINGS.includes(buildingType);

// Determine which global resource this special building outputs
const globalOutput = isSpecialBuilding
? productionMethod.outputs.find(
(o) =>
o.resource === "money" ||
o.resource === "food" ||
o.resource === "knowledge",
)?.resource
: null;

return (
<BlockStack gap="1">
<Text size="xs" tone="subdued">
Expand All @@ -24,12 +43,40 @@ export const ProductionInputs = ({
) : (
<>
<Text size="sm">
• {input.amount}x {input.resource}
</Text>
<Text size="xs" tone="subdued">
({RESOURCES.money.icon}{" "}
{input.amount * RESOURCES[input.resource].value})
{`• ${!isSpecialBuilding ? input.amount + "x " : ""}${input.resource}`}
</Text>
{isSpecialBuilding ? (
<Text size="xs" tone="subdued">
(
{globalOutput === "money" && (
<>
{RESOURCES.money.icon} {RESOURCES[input.resource].value}
</>
)}
{globalOutput === "food" && (
<>
{RESOURCES.food.icon}{" "}
{getResourceTypeFoodValue(input.resource)}
</>
)}
{globalOutput === "knowledge" && (
<>
{RESOURCES.knowledge.icon}{" "}
{/* Knowledge buildings might have custom logic */}
</>
)}
)
</Text>
) : (
// For regular buildings, show money value and food value (if applicable)
<Text size="xs" tone="subdued">
({RESOURCES.money.icon}{" "}
{input.amount * RESOURCES[input.resource].value}{" "}
{getResourceTypeFoodValue(input.resource) > 0 &&
`/ ${RESOURCES.food.icon} ${getResourceTypeFoodValue(input.resource) * input.amount}`}
)
</Text>
)}
</>
)}
</InlineStack>
Expand Down
11 changes: 9 additions & 2 deletions src/components/Factory/Context/Building/Production/Outputs.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import { BlockStack, InlineStack } from "@/components/ui/layout";
import { Text } from "@/components/ui/typography";

import { isGlobalResource, RESOURCES } from "../../../data/resources";
import {
getResourceTypeFoodValue,
isGlobalResource,
RESOURCES,
} from "../../../data/resources";
import type { ProductionMethod } from "../../../types/production";

interface ProductionOutputsProps {
Expand Down Expand Up @@ -36,7 +40,10 @@ export const ProductionOutputs = ({
</Text>
<Text size="xs" tone="subdued">
({RESOURCES.money.icon}{" "}
{output.amount * RESOURCES[output.resource].value})
{output.amount * RESOURCES[output.resource].value}{" "}
{getResourceTypeFoodValue(output.resource) > 0 &&
`/ ${RESOURCES.food.icon} ${getResourceTypeFoodValue(output.resource) * output.amount}`}
)
</Text>
</InlineStack>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,15 @@ import { ProductionMethodSwitcher } from "./ProductionMethodSwitcher";
import { ProductionProgress } from "./Progress";

interface ProductionMethodSectionProps {
buildingType: string;
productionMethod?: ProductionMethod;
productionState?: ProductionState;
availableMethods?: ProductionMethod[];
onMethodChange?: (method: ProductionMethod) => void;
}

export const ProductionMethodSection = ({
buildingType,
productionMethod,
productionState,
availableMethods = [],
Expand Down Expand Up @@ -59,7 +61,10 @@ export const ProductionMethodSection = ({
</InlineStack>

<BlockStack gap="2">
<ProductionInputs productionMethod={productionMethod} />
<ProductionInputs
productionMethod={productionMethod}
buildingType={buildingType}
/>
<ProductionOutputs productionMethod={productionMethod} />
<ProductionDuration productionMethod={productionMethod} />
<ProductionProgress
Expand Down
79 changes: 79 additions & 0 deletions src/components/Factory/Sidebar/BuildingFolder.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import { useState } from "react";

import {
Collapsible,
CollapsibleContent,
CollapsibleTrigger,
} from "@/components/ui/collapsible";
import { Icon, type IconName } from "@/components/ui/icon";
import { BlockStack, InlineStack } from "@/components/ui/layout";
import { cn } from "@/lib/utils";

import type { BuildingCategory, BuildingType } from "../types/buildings";
import BuildingItem from "./BuildingItem";

const CATEGORY_LABELS: Record<BuildingCategory, string> = {
special: "Special",
production: "Production",
refining: "Refining",
utility: "Utility",
storage: "Storage",
};

const CATEGORY_ICONS: Record<BuildingCategory, IconName> = {
special: "Star",
production: "Hammer",
refining: "Factory",
utility: "Wrench",
storage: "Package",
};

type BuildingFolderProps = {
category: BuildingCategory;
buildings: BuildingType[];
};

const BuildingFolder = ({ category, buildings }: BuildingFolderProps) => {
const [isOpen, setIsOpen] = useState(false);

return (
<Collapsible open={isOpen} onOpenChange={setIsOpen}>
<CollapsibleTrigger
className={cn(
"flex items-center justify-between w-full px-2 py-1.5 rounded-sm",
"hover:bg-gray-100 transition-colors group",
)}
>
<InlineStack gap="2" align="center">
<Icon
name={CATEGORY_ICONS[category]}
size="sm"
className="text-gray-600"
/>
<span className="text-sm font-semibold text-gray-700">
{CATEGORY_LABELS[category]}
</span>
<span className="text-xs text-gray-500">({buildings.length})</span>
</InlineStack>
<Icon
name="ChevronDown"
size="sm"
className={cn(
"text-gray-500 transition-transform",
isOpen && "rotate-180",
)}
/>
</CollapsibleTrigger>

<CollapsibleContent>
<BlockStack gap="1" className="mt-1 ml-2">
{buildings.map((buildingType) => (
<BuildingItem key={buildingType} buildingType={buildingType} />
))}
</BlockStack>
</CollapsibleContent>
</Collapsible>
);
};

export default BuildingFolder;
54 changes: 49 additions & 5 deletions src/components/Factory/Sidebar/Buildings.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,64 @@
import { BlockStack } from "@/components/ui/layout";
import { Text } from "@/components/ui/typography";

import { BUILDINGS } from "../data/buildings";
import type { BuildingCategory, BuildingType } from "../types/buildings";
import { BUILDING_TYPES } from "../types/buildings";
import BuildingItem from "./BuildingItem";
import BuildingFolder from "./BuildingFolder";

const CATEGORY_ORDER: BuildingCategory[] = [
"special",
"production",
"refining",
"utility",
"storage",
];

const Buildings = () => {
// Group buildings by category
const buildingsByCategory = getBuildingsByCategory();

return (
<BlockStack gap="2">
<Text>Buildings</Text>
<BlockStack gap="1">
{BUILDING_TYPES.map((buildingType) => (
<BuildingItem key={buildingType} buildingType={buildingType} />
))}
<BlockStack gap="2">
{CATEGORY_ORDER.map((category) => {
const buildings = buildingsByCategory.get(category) || [];

if (buildings.length === 0) return null;

return (
<BuildingFolder
key={category}
category={category}
buildings={buildings}
/>
);
})}
</BlockStack>
</BlockStack>
);
};

export default Buildings;

function getBuildingsByCategory() {
const grouped = new Map<BuildingCategory, BuildingType[]>();

CATEGORY_ORDER.forEach((category) => {
grouped.set(category, []);
});

BUILDING_TYPES.forEach((buildingType) => {
const building = BUILDINGS[buildingType];
const category = building.category;

if (!grouped.has(category)) {
grouped.set(category, []);
}

grouped.get(category)!.push(buildingType);
});

return grouped;
}
Loading
Loading