Skip to content
Merged
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
@@ -0,0 +1,30 @@
---
type: minor
area: web,api,sim
summary: Add Buildings Management UI with preflight validation and acquisition flows (Phase 4 & 5)
---

**Phase 4 - Frontend Integration + Preflight + Operator Visibility:**
- Add Buildings page with region/category grouping and status display
- Add building acquisition dialog with cost preview
- Add reusable Storage Meter component with warning thresholds (80%, 95%, 100%)
- Add preflight validation endpoints (canCreateProductionJob, canPlaceBuyOrder)
- Add storage and capacity info endpoints
- Add building type definitions endpoint with balanced costs

**Phase 5 - Acquisition Flows + Balance Pass:**
- Implement transactional building acquisition with ledger entries
- Define BuildingType dataset: Early Workshop, Factory, MegaFactory, Warehouse, HQ
- Add building reactivation flow for INACTIVE buildings
- Implement cost preview showing acquisition cost and weekly operating expenses

**API Layer:**
- Add BuildingsController with full CRUD operations
- Add BuildingsService with ownership validation
- Add buildings API client functions and parsers

**UI Components:**
- Add Dialog, Label, and Progress UI primitives
- Install required @radix-ui packages

- Add Buildings Management UI with preflight validation and acquisition flows (Phase 4 & 5)
4 changes: 4 additions & 0 deletions apps/api/src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import { MiddlewareConsumer, Module, NestModule } from "@nestjs/common";
import { AuthModule } from "@thallesp/nestjs-better-auth";
import { MaintenanceModeMiddleware } from "./common/middleware/maintenance-mode.middleware";
import { SchemaReadinessMiddleware } from "./common/middleware/schema-readiness.middleware";
import { BuildingsController } from "./buildings/buildings.controller";
import { BuildingsService } from "./buildings/buildings.service";
import { CompaniesController } from "./companies/companies.controller";
import { CompaniesService } from "./companies/companies.service";
import { ContractsController } from "./contracts/contracts.controller";
Expand Down Expand Up @@ -63,6 +65,7 @@ import { auth } from "./lib/auth";
ContractsController,
PlayersController,
CompaniesController,
BuildingsController,
MarketController,
ItemsController,
RegionsController,
Expand All @@ -83,6 +86,7 @@ import { auth } from "./lib/auth";
FinanceService,
ContractsService,
CompaniesService,
BuildingsService,
PlayersService,
MarketService,
MaintenanceService,
Expand Down
115 changes: 115 additions & 0 deletions apps/api/src/buildings/buildings.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
import {
Body,
Controller,
Get,
HttpCode,
HttpStatus,
Inject,
Post,
Query
} from "@nestjs/common";
import { CurrentPlayerId } from "../common/decorators/current-player-id.decorator";
import { AcquireBuildingDto } from "./dto/acquire-building.dto";
import { ListBuildingsDto } from "./dto/list-buildings.dto";
import { ReactivateBuildingDto } from "./dto/reactivate-building.dto";
import { PreflightProductionJobDto } from "./dto/preflight-production-job.dto";
import { PreflightBuyOrderDto } from "./dto/preflight-buy-order.dto";
import { BuildingsService } from "./buildings.service";

@Controller("v1/buildings")
export class BuildingsController {
private readonly buildingsService: BuildingsService;

constructor(@Inject(BuildingsService) buildingsService: BuildingsService) {
this.buildingsService = buildingsService;
}

@Get()
async listBuildings(
@Query() query: ListBuildingsDto,
@CurrentPlayerId() playerId: string
) {
return this.buildingsService.listBuildings(query, playerId);
}

@Post("acquire")
async acquireBuilding(
@Body() body: AcquireBuildingDto,
@CurrentPlayerId() playerId: string
) {
return this.buildingsService.acquireBuilding(
{
companyId: body.companyId,
regionId: body.regionId,
buildingType: body.buildingType,
name: body.name
},
playerId
);
}

@Post("reactivate")
@HttpCode(HttpStatus.OK)
async reactivateBuilding(
@Body() body: ReactivateBuildingDto,
@CurrentPlayerId() playerId: string
) {
return this.buildingsService.reactivateBuilding(body.buildingId, playerId);
}

@Get("storage-info")
async getStorageInfo(
@Query("companyId") companyId: string,
@Query("regionId") regionId: string,
@CurrentPlayerId() playerId: string
) {
return this.buildingsService.getRegionalStorageInfo(companyId, regionId, playerId);
}

@Get("capacity-info")
async getCapacityInfo(
@Query("companyId") companyId: string,
@CurrentPlayerId() playerId: string
) {
return this.buildingsService.getProductionCapacityInfo(companyId, playerId);
}

@Post("preflight/production-job")
@HttpCode(HttpStatus.OK)
async preflightProductionJob(
@Body() body: PreflightProductionJobDto,
@CurrentPlayerId() playerId: string
) {
return this.buildingsService.preflightProductionJob(
{
companyId: body.companyId,
recipeId: body.recipeId,
quantity: body.quantity
},
playerId
);
}

@Post("preflight/buy-order")
@HttpCode(HttpStatus.OK)
async preflightBuyOrder(
@Body() body: PreflightBuyOrderDto,
@CurrentPlayerId() playerId: string
) {
return this.buildingsService.preflightBuyOrder(
{
companyId: body.companyId,
regionId: body.regionId,
itemId: body.itemId,
quantity: body.quantity
},
playerId
);
}

@Get("definitions")
async getBuildingTypeDefinitions() {
const definitions = await this.buildingsService.getBuildingTypeDefinitions();
return { definitions };
}
}
Loading
Loading