Files
flatrender/services/identity/FlatRender.IdentitySvc/Controllers/DiscountsController.cs
T
soroush.asadi 67060c73b2 feat(admin): discount edit/delete + project-scoped scene/color editor
Identity (discounts):
- DiscountsController: PUT /v1/discounts/{id}, DELETE /v1/discounts/{id}
- DiscountService.UpdateAsync (partial update, code-clash guard) + DeleteAsync
- UpdateDiscountRequest record (all fields optional incl. is_active)
- Frontend discountsConfig: canEdit + canDelete + is_active field

Content (scenes/colors — UI for existing CRUD endpoints):
- New SceneColorEditor.tsx: 3-tab modal (scenes / shared-colors / color-presets),
  project-scoped, full add/edit/delete per tab, colour pickers + palette item editor
- Wired into TemplatesAdmin: "صحنه‌ها و رنگ‌ها" button per template variant row
- Routes through the generic admin proxy with ?project_id=

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-05 12:16:13 +03:30

65 lines
2.2 KiB
C#

using FlatRender.IdentitySvc.Application.Services.Interfaces;
using FlatRender.IdentitySvc.Models.Requests;
using FlatRender.IdentitySvc.Models.Responses;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace FlatRender.IdentitySvc.Controllers;
[ApiController]
[Route("v1/discounts")]
[Authorize]
public class DiscountsController(IDiscountService discountService) : ControllerBase
{
[HttpPost("validate")]
[AllowAnonymous]
[ProducesResponseType(typeof(DiscountValidateResponse), 200)]
public async Task<IActionResult> Validate([FromBody] ValidateDiscountRequest request)
{
var tenantId = GetTenantIdOrDefault();
var result = await discountService.ValidateAsync(tenantId, request.Code, request.PlanId);
return Ok(result);
}
[HttpGet]
[ProducesResponseType(typeof(PagedResponse<DiscountResponse>), 200)]
public async Task<IActionResult> List([FromQuery] int page = 1, [FromQuery] int pageSize = 20)
{
var result = await discountService.ListAsync(GetTenantId(), page, pageSize);
return Ok(result);
}
[HttpPost]
[ProducesResponseType(typeof(DiscountResponse), 201)]
public async Task<IActionResult> Create([FromBody] CreateDiscountRequest request)
{
var result = await discountService.CreateAsync(GetTenantId(), request);
return StatusCode(201, result);
}
[HttpPut("{id:guid}")]
[ProducesResponseType(typeof(DiscountResponse), 200)]
public async Task<IActionResult> Update(Guid id, [FromBody] UpdateDiscountRequest request)
{
var result = await discountService.UpdateAsync(GetTenantId(), id, request);
return result == null ? NotFound() : Ok(result);
}
[HttpDelete("{id:guid}")]
[ProducesResponseType(204)]
public async Task<IActionResult> Delete(Guid id)
{
var ok = await discountService.DeleteAsync(GetTenantId(), id);
return ok ? NoContent() : NotFound();
}
private Guid GetTenantId() => Guid.Parse(User.FindFirst("tenant_id")?.Value
?? throw new UnauthorizedAccessException());
private Guid GetTenantIdOrDefault()
{
var claim = User.FindFirst("tenant_id")?.Value;
return claim != null ? Guid.Parse(claim) : Guid.Empty;
}
}