using FluentValidation; using Microsoft.AspNetCore.Mvc; using Meezi.API.Models.Orders; using Meezi.API.Models.Tables; using Meezi.API.Services; using Meezi.Core.Authorization; using Meezi.Core.Interfaces; using Meezi.Shared; namespace Meezi.API.Controllers; [Route("api/cafes/{cafeId}/tables")] public class TablesController : CafeApiControllerBase { private readonly ITableService _tableService; private readonly IOrderService _orderService; private readonly IValidator _createValidator; private readonly IValidator _patchValidator; private readonly IValidator _cleaningValidator; public TablesController( ITableService tableService, IOrderService orderService, IValidator createValidator, IValidator patchValidator, IValidator cleaningValidator) { _tableService = tableService; _orderService = orderService; _createValidator = createValidator; _patchValidator = patchValidator; _cleaningValidator = cleaningValidator; } [HttpGet("board")] public async Task GetBoard( string cafeId, ITenantContext tenant, [FromQuery] bool activeOnly = true, [FromQuery] string? branchId = null, CancellationToken ct = default) { if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied; var data = await _tableService.GetTableBoardAsync(cafeId, activeOnly, branchId, ct); return Ok(new ApiResponse>(true, data)); } [HttpGet] public async Task GetTables( string cafeId, ITenantContext tenant, [FromQuery] string? branchId = null, CancellationToken ct = default) { if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied; var data = await _tableService.GetTablesAsync(cafeId, branchId, ct); return Ok(new ApiResponse>(true, data)); } [HttpPost] public async Task CreateTable( string cafeId, [FromBody] CreateTableRequest request, ITenantContext tenant, CancellationToken ct) { if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied; if (EnsurePermission(tenant, Permission.ManageTables) is { } permDenied) return permDenied; var validation = await _createValidator.ValidateAsync(request, ct); if (!validation.IsValid) return BadRequest(ValidationError(validation)); var data = await _tableService.CreateTableAsync(cafeId, request, ct); if (data is null) return NotFoundError("Branch not found."); return Ok(new ApiResponse(true, data)); } [HttpPatch("{id}")] public async Task PatchTable( string cafeId, string id, [FromBody] PatchTableRequest request, ITenantContext tenant, CancellationToken ct) { if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied; if (EnsurePermission(tenant, Permission.ManageTables) is { } permDenied) return permDenied; var validation = await _patchValidator.ValidateAsync(request, ct); if (!validation.IsValid) return BadRequest(ValidationError(validation)); var data = await _tableService.PatchTableAsync(cafeId, id, request, ct); if (data is null) return NotFoundError(); return Ok(new ApiResponse(true, data)); } [HttpGet("{id}/active-order")] public async Task GetActiveOrder( string cafeId, string id, ITenantContext tenant, CancellationToken ct) { if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied; var data = await _orderService.GetActiveOrderByTableAsync(cafeId, id, ct); if (data is null) return NotFoundError("No active order for this table."); return Ok(new ApiResponse(true, data)); } [HttpDelete("{id}")] public async Task DeleteTable( string cafeId, string id, ITenantContext tenant, CancellationToken ct) { if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied; if (EnsurePermission(tenant, Permission.ManageTables) is { } permDenied) return permDenied; var result = await _tableService.DeleteTableAsync(cafeId, id, ct); if (!result.Success) { var status = result.ErrorCode == "TABLE_HAS_OPEN_ORDER" ? StatusCodes.Status409Conflict : StatusCodes.Status400BadRequest; return StatusCode(status, new ApiResponse(false, null, new ApiError(result.ErrorCode!, result.Message ?? result.ErrorCode!))); } return Ok(new ApiResponse(true, result.Data)); } [HttpPatch("{id}/cleaning")] public async Task SetCleaning( string cafeId, string id, [FromBody] SetTableCleaningRequest request, ITenantContext tenant, CancellationToken ct) { if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied; if (EnsurePermission(tenant, Permission.ManageTables) is { } permDenied) return permDenied; var validation = await _cleaningValidator.ValidateAsync(request, ct); if (!validation.IsValid) return BadRequest(ValidationError(validation)); var data = await _tableService.SetTableCleaningAsync(cafeId, id, request.IsCleaning, ct); if (data is null) return NotFoundError(); return Ok(new ApiResponse(true, data)); } [HttpGet("{id}/qr")] public async Task GetQrPng( string cafeId, string id, ITenantContext tenant, CancellationToken ct) { if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied; var png = await _tableService.GetQrPngAsync(cafeId, id, ct); if (png is null) return NotFoundError(); return File(png, "image/png", $"table-{id}-qr.png"); } }