using SharedKernel.Attributes; using SharedKernel.Extensions; using MediatR; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.RazorPages; using Microsoft.AspNetCore.Mvc.Rendering; using System.ComponentModel.DataAnnotations; namespace Admin.Pages.Crypto.List { public class EditModel(IMediator mediator) : PageModel { public int CoinID { get; private set; } public string? CurrentLogoImage { get; private set; } public IReadOnlyList Markets { get; private set; } = []; public List Categories { get; set; } = []; [BindProperty] public InputModel Input { get; set; } = new(); public sealed class InputModel { [Required(ErrorMessage = "심볼은 필수입니다.")] [StringLength(30, ErrorMessage = "심볼은 {1}자 이하로 입력하세요.")] public string Symbol { get; set; } = null!; [Required(ErrorMessage = "한글명은 필수입니다.")] [StringLength(200, ErrorMessage = "한글명은 {1}자 이하로 입력하세요.")] public string KorName { get; set; } = null!; [Required(ErrorMessage = "영문명은 필수입니다.")] [StringLength(200, ErrorMessage = "영문명은 {1}자 이하로 입력하세요.")] public string EngName { get; set; } = null!; [AllowedExtensions("jpg,jpeg,png,gif,webp,svg", ErrorMessage = "이미지 확장자는 jpg, jpeg, png, gif, webp, svg 이어야 합니다.")] public IFormFile? LogoImageFile { get; set; } public bool DeleteLogoImage { get; set; } = false; [StringLength(5000)] public string? Description { get; set; } [StringLength(100)] public string? ContractAddress { get; set; } [StringLength(500)] [DataType(DataType.Url)] public string? WebsiteUrl { get; set; } [StringLength(500)] [DataType(DataType.Url)] public string? WhitepaperUrl { get; set; } [StringLength(500)] [DataType(DataType.Url)] public string? TwitterUrl { get; set; } [StringLength(500)] [DataType(DataType.Url)] public string? TelegramUrl { get; set; } public bool IsActive { get; set; } = false; public bool IsWarning { get; set; } = false; public bool IsNew { get; set; } = false; public bool IsDelisted { get; set; } = false; public int[] CategoryIDs { get; set; } = []; } public async Task OnGetAsync(int id, CancellationToken ct) { try { var coin = await mediator.Send(new GetCoin.Query(id), ct); CoinID = coin.ID; CurrentLogoImage = coin.LogoImage; Markets = coin.Markets; var categories = await mediator.Send(new GetCryptoCategories.Query(), ct); Categories = [..categories.List.Select(c => new SelectListItem(c.Name, c.ID.ToString()))]; Input = new InputModel { Symbol = coin.Symbol, KorName = coin.KorName, EngName = coin.EngName, Description = coin.Description, ContractAddress = coin.ContractAddress, WebsiteUrl = coin.WebsiteUrl, WhitepaperUrl = coin.WhitepaperUrl, TwitterUrl = coin.TwitterUrl, TelegramUrl = coin.TelegramUrl, IsActive = coin.IsActive, IsWarning = coin.IsWarning, IsNew = coin.IsNew, IsDelisted = coin.IsDelisted, CategoryIDs = [..coin.CategoryIDs] }; return Page(); } catch (KeyNotFoundException) { return NotFound(); } } public async Task OnPostAsync(int id, CancellationToken ct) { try { if (!ModelState.IsValid) { throw new Exception(ModelState.GetErrorMessages()); } await mediator.Send(new UpdateCoin.Command( id, Input.Symbol, Input.KorName, Input.EngName, Input.LogoImageFile, Input.DeleteLogoImage, Input.Description, Input.ContractAddress, Input.WebsiteUrl, Input.WhitepaperUrl, Input.TwitterUrl, Input.TelegramUrl, Input.IsActive, Input.IsWarning, Input.IsNew, Input.IsDelisted, Input.CategoryIDs ), ct); TempData["SuccessMessage"] = $"{Input.Symbol} 코인이 수정되었습니다."; return RedirectToPage("/Crypto/List/Edit", new { id }); } catch (Exception e) { TempData["ErrorMessages"] = e.Message; return RedirectToPage("/Crypto/List/Edit", new { id }); } } } }