import { Body, Controller, Get, Post, Put, Query, Req, UseGuards } from '@nestjs/common';
import type { z } from 'zod';
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 { ZodValidationPipe } from '../../../common/validation/zod-validation.pipe';
import { MenuService } from '../application/menu.service';
import { MenuAvailabilityRuleService } from '../application/menu-availability-rule.service';
import { ModifierService } from '../application/modifier.service';
import { ActiveMenuService } from '../read-models/active-menu.service';
import { MenuAvailabilityRuleReadService } from '../read-models/menu-availability-rule-read.service';
import {
attachModifierGroupToProductSchema,
attachModifierSchema,
createIngredientSchema,
createModifierGroupSchema,
createModifierSchema,
createProductSchema,
createAvailabilityRuleSchema,
setAvailabilitySchema,
setIngredientAllergensSchema,
setPriceSchema,
} from './dto/menu.schemas';
@Controller('/menu')
@UseGuards(AuthGuard, OrganizationContextGuard, LocationContextGuard, PermissionGuard)
export class MenuController {
constructor(
private readonly menu: MenuService,
private readonly modifiers: ModifierService,
private readonly activeMenu: ActiveMenuService,
private readonly availabilityRules: MenuAvailabilityRuleService,
private readonly availabilityRuleRead: MenuAvailabilityRuleReadService,
) {}
@Get('/active')
@RequirePermissions('menu.read')
active(@Req() req: LocationScopedRequest, @Query('locale') locale?: string, @Query('channel') channel?: string, @Query('placeId') placeId?: string, @Query('operationalUnitId') operationalUnitId?: string, @Query('stationId') stationId?: string, @Query('at') at?: string) {
return this.activeMenu.list({
organizationId: req.organizationId!,
locationId: req.locationId!,
locale: locale ?? req.auth?.locale ?? 'en-GB',
channel,
placeId,
operationalUnitId,
stationId,
at,
});
}
@Post('/ingredients')
@RequirePermissions('ingredients.manage')
createIngredient(@Req() req: LocationScopedRequest, @Body(new ZodValidationPipe(createIngredientSchema)) body: z.infer<typeof createIngredientSchema>) {
return this.menu.createIngredient({
organizationId: req.organizationId!,
...body,
});
}
@Put('/ingredients/allergens')
@RequirePermissions('allergens.manage')
setAllergens(@Req() req: LocationScopedRequest, @Body(new ZodValidationPipe(setIngredientAllergensSchema)) body: z.infer<typeof setIngredientAllergensSchema>) {
return this.menu.setIngredientAllergens({
organizationId: req.organizationId!,
ingredientId: body.ingredientId,
allergens: body.allergens,
userId: req.auth?.userId,
});
}
@Post('/products')
@RequirePermissions('menu.manage')
createProduct(@Req() req: LocationScopedRequest, @Body(new ZodValidationPipe(createProductSchema)) body: z.infer<typeof createProductSchema>) {
return this.menu.createProduct({
organizationId: req.organizationId!,
...body,
});
}
@Post('/prices')
@RequirePermissions('menu.prices.manage')
setPrice(@Req() req: LocationScopedRequest, @Body(new ZodValidationPipe(setPriceSchema)) body: z.infer<typeof setPriceSchema>) {
return this.menu.setPrice({
organizationId: req.organizationId!,
productId: body.productId,
locationId: req.locationId!,
currencyCode: body.currencyCode,
amountMinor: body.amountMinor,
channel: body.channel,
});
}
@Put('/availability')
@RequirePermissions('menu.availability.manage')
setAvailability(@Req() req: LocationScopedRequest, @Body(new ZodValidationPipe(setAvailabilitySchema)) body: z.infer<typeof setAvailabilitySchema>) {
return this.menu.setAvailability({
organizationId: req.organizationId!,
productId: body.productId,
locationId: req.locationId!,
available: body.available,
reasonCode: body.reasonCode,
userId: req.auth?.userId,
});
}
@Post('/availability/rules')
@RequirePermissions('menu.availability.manage')
createAvailabilityRule(@Req() req: LocationScopedRequest, @Body(new ZodValidationPipe(createAvailabilityRuleSchema)) body: z.infer<typeof createAvailabilityRuleSchema>) {
return this.availabilityRules.create({organizationId:req.organizationId!,locationId:req.locationId!,...body,userId:req.auth?.userId});
}
@Get('/availability/rules')
@RequirePermissions('menu.availability.manage')
availabilityRuleList(@Req() req: LocationScopedRequest, @Query('productId') productId?: string, @Query('stationId') stationId?: string) {
return this.availabilityRuleRead.list({
organizationId: req.organizationId!,
locationId: req.locationId!,
productId,
stationId,
});
}
@Post('/modifier-groups')
@RequirePermissions('menu.manage')
createModifierGroup(@Req() req: LocationScopedRequest, @Body(new ZodValidationPipe(createModifierGroupSchema)) body: z.infer<typeof createModifierGroupSchema>) {
return this.modifiers.createGroup({
organizationId: req.organizationId!,
name: body.name,
minSelect: body.minSelect,
maxSelect: body.maxSelect,
});
}
@Post('/modifiers')
@RequirePermissions('menu.manage')
createModifier(@Req() req: LocationScopedRequest, @Body(new ZodValidationPipe(createModifierSchema)) body: z.infer<typeof createModifierSchema>) {
return this.modifiers.createModifier({
organizationId: req.organizationId!,
...body,
});
}
@Post('/modifier-groups/items')
@RequirePermissions('menu.manage')
attachModifier(@Req() req: LocationScopedRequest, @Body(new ZodValidationPipe(attachModifierSchema)) body: z.infer<typeof attachModifierSchema>) {
return this.modifiers.attachModifier({
organizationId: req.organizationId!,
...body,
});
}
@Post('/products/modifier-groups')
@RequirePermissions('menu.manage')
attachModifierGroup(@Req() req: LocationScopedRequest, @Body(new ZodValidationPipe(attachModifierGroupToProductSchema)) body: z.infer<typeof attachModifierGroupToProductSchema>) {
return this.modifiers.attachGroupToProduct({
organizationId: req.organizationId!,
...body,
});
}
}
