import { Body, Controller, Get, Param, Post, Query, Req, UseGuards } from '@nestjs/common';
import { AuthGuard } from '../../../common/auth/auth.guard';
import { OrganizationContextGuard } from '../../../common/auth/organization-context.guard';
import { LocationContextGuard, type LocationScopedRequest } from '../../../common/auth/location-context.guard';
import { PermissionGuard } from '../../../common/auth/permission.guard';
import { RequirePermissions } from '../../../common/auth/permissions.decorator';
import { KitchenService } from '../application/kitchen.service';
import { KitchenStationService } from '../application/kitchen-station.service';
import { BarStationRoutingService } from '../application/bar-station-routing.service';
import { ZodValidationPipe } from '../../../common/validation/zod-validation.pipe';
import { kitchenRouteSchema,kitchenStationSchema,kitchenTicketTransitionSchema } from '../../../common/validation/critical-write.schemas';
import { KitchenBoardService } from '../read-models/kitchen-board.service';
import { KitchenRealtimeService } from '../realtime/kitchen-realtime.service';
import type { z } from 'zod';
type KitchenStationBody = z.infer<typeof kitchenStationSchema>;
type KitchenRouteBody = z.infer<typeof kitchenRouteSchema>;
type KitchenTicketTransitionBody = z.infer<typeof kitchenTicketTransitionSchema>;
type StationKind = KitchenStationBody['stationKind'];
@Controller('/kitchen')
@UseGuards(AuthGuard, OrganizationContextGuard, LocationContextGuard, PermissionGuard)
export class KitchenController {
constructor(
private readonly kitchen: KitchenService,
private readonly stations: KitchenStationService,
private readonly barStations: BarStationRoutingService,
private readonly board: KitchenBoardService,
private readonly realtime: KitchenRealtimeService,
) {}
@Get('/board')
@RequirePermissions('kitchen.read')
getBoard(
@Req() req: LocationScopedRequest,
@Query('stationId') stationId?: string,
@Query('stationKind') stationKind?: StationKind,
) {
return this.board.get({
organizationId: req.organizationId!,
locationId: req.locationId!,
stationId,
stationKind,
});
}
@Get('/realtime/snapshot')
@RequirePermissions('kitchen.read')
realtimeSnapshot(
@Req() req: LocationScopedRequest,
@Query('stationId') stationId?: string,
@Query('stationKind') stationKind?: StationKind,
) {
return this.realtime.snapshot({
organizationId: req.organizationId!,
locationId: req.locationId!,
stationId,
stationKind,
});
}
@Get('/realtime/contract')
@RequirePermissions('kitchen.read')
realtimeContract() {
return this.realtime.eventContract();
}
@Get('/stations')
@RequirePermissions('kitchen.manage_stations')
listStations(@Req() req: LocationScopedRequest) {
return this.stations.list({
organizationId: req.organizationId!,
locationId: req.locationId!,
});
}
@Post('/stations')
@RequirePermissions('kitchen.manage_stations')
createStation(@Req() req: LocationScopedRequest, @Body(new ZodValidationPipe(kitchenStationSchema)) body:KitchenStationBody) {
return this.stations.create({
organizationId: req.organizationId!,
locationId: req.locationId!,
code: body.code,
displayName: body.displayName,
targetSeconds: body.targetSeconds,
stationKind: body.stationKind,
actorUserId: req.auth?.userId,
});
}
@Post('/bar/stations')
@RequirePermissions('kitchen.manage_stations')
createBarStation(@Req() req: LocationScopedRequest, @Body(new ZodValidationPipe(kitchenStationSchema)) body:KitchenStationBody) {
return this.barStations.create({
organizationId: req.organizationId!,
locationId: req.locationId!,
code: body.code,
displayName: body.displayName,
targetSeconds: body.targetSeconds,
actorUserId: req.auth?.userId,
});
}
@Post('/routes')
@RequirePermissions('kitchen.manage_stations')
routeProduct(@Req() req: LocationScopedRequest, @Body(new ZodValidationPipe(kitchenRouteSchema)) body:KitchenRouteBody) {
return this.stations.routeProduct({
organizationId: req.organizationId!,
locationId: req.locationId!,
productId: body.productId,
stationId: body.stationId,
priority: body.priority,
});
}
@Post('/bar/routes')
@RequirePermissions('kitchen.manage_stations')
routeBarProduct(@Req() req: LocationScopedRequest, @Body(new ZodValidationPipe(kitchenRouteSchema)) body:KitchenRouteBody) {
return this.barStations.routeProduct({
organizationId: req.organizationId!,
locationId: req.locationId!,
productId: body.productId,
stationId: body.stationId,
priority: body.priority,
});
}
@Post('/tickets/:id/start')
@RequirePermissions('kitchen.start')
start(@Req() req: LocationScopedRequest, @Param('id') id: string, @Body(new ZodValidationPipe(kitchenTicketTransitionSchema)) body: KitchenTicketTransitionBody = {}) {
return this.kitchen.start({
organizationId: req.organizationId!,
locationId: req.locationId!,
ticketId: id,
userId: req.auth?.userId,
expectedVersion: body.expectedVersion,
});
}
@Post('/tickets/:id/ready')
@RequirePermissions('kitchen.ready')
ready(@Req() req: LocationScopedRequest, @Param('id') id: string, @Body(new ZodValidationPipe(kitchenTicketTransitionSchema)) body: KitchenTicketTransitionBody = {}) {
return this.kitchen.ready({
organizationId: req.organizationId!,
locationId: req.locationId!,
ticketId: id,
userId: req.auth?.userId,
expectedVersion: body.expectedVersion,
});
}
@Post('/tickets/:id/bump')
@RequirePermissions('kitchen.bump')
bump(@Req() req: LocationScopedRequest, @Param('id') id: string, @Body(new ZodValidationPipe(kitchenTicketTransitionSchema)) body: KitchenTicketTransitionBody = {}) {
return this.kitchen.bump({
organizationId: req.organizationId!,
locationId: req.locationId!,
ticketId: id,
userId: req.auth?.userId,
expectedVersion: body.expectedVersion,
});
}
@Post('/tickets/:id/recall')
@RequirePermissions('kitchen.recall')
recall(@Req() req: LocationScopedRequest, @Param('id') id: string, @Body(new ZodValidationPipe(kitchenTicketTransitionSchema)) body: KitchenTicketTransitionBody = {}) {
return this.kitchen.recall({
organizationId: req.organizationId!,
locationId: req.locationId!,
ticketId: id,
userId: req.auth?.userId,
expectedVersion: body.expectedVersion,
});
}
@Post('/tickets/:id/complete')
@RequirePermissions('kitchen.complete')
complete(@Req() req: LocationScopedRequest, @Param('id') id: string, @Body(new ZodValidationPipe(kitchenTicketTransitionSchema)) body: KitchenTicketTransitionBody = {}) {
return this.kitchen.complete({
organizationId: req.organizationId!,
locationId: req.locationId!,
ticketId: id,
userId: req.auth?.userId,
expectedVersion: body.expectedVersion,
});
}
}
