From 1cc8665176eac0c50ed68ab8d4d0953bc003d032 Mon Sep 17 00:00:00 2001 From: Ricardo Carneiro Date: Wed, 9 Jul 2025 10:37:57 -0300 Subject: [PATCH] fix: alterar link de produto --- .../Utils/ConditionalRequiredAttribute.cs | 72 +++++++ .../ViewModels/ManagePageViewModel.cs | 10 +- src/BCards.Web/Views/Admin/ManagePage.cshtml | 199 +++++++++++++----- 3 files changed, 220 insertions(+), 61 deletions(-) create mode 100644 src/BCards.Web/Utils/ConditionalRequiredAttribute.cs diff --git a/src/BCards.Web/Utils/ConditionalRequiredAttribute.cs b/src/BCards.Web/Utils/ConditionalRequiredAttribute.cs new file mode 100644 index 0000000..d989d1e --- /dev/null +++ b/src/BCards.Web/Utils/ConditionalRequiredAttribute.cs @@ -0,0 +1,72 @@ +using BCards.Web.Models; +using BCards.Web.ViewModels; +using Microsoft.AspNetCore.Mvc.ModelBinding; +using System.ComponentModel.DataAnnotations; + +// Atributo de validação customizado para links +public class ConditionalRequiredAttribute : ValidationAttribute +{ + private readonly string _dependentProperty; + private readonly object _targetValue; + + public ConditionalRequiredAttribute(string dependentProperty, object targetValue) + { + _dependentProperty = dependentProperty; + _targetValue = targetValue; + } + + protected override ValidationResult IsValid(object value, ValidationContext validationContext) + { + var dependentProperty = validationContext.ObjectType.GetProperty(_dependentProperty); + if (dependentProperty == null) + return ValidationResult.Success; + + var dependentValue = dependentProperty.GetValue(validationContext.ObjectInstance); + + // Se o valor dependente não é o target, não valida + if (!Equals(dependentValue, _targetValue)) + return ValidationResult.Success; + + // Se é o target value e o campo está vazio, retorna erro + if (value == null || string.IsNullOrWhiteSpace(value.ToString())) + return new ValidationResult(ErrorMessage ?? $"{validationContext.DisplayName} é obrigatório."); + + return ValidationResult.Success; + } +} + +// Método de extensão para validação personalizada no Controller +public static class ModelStateExtensions +{ + public static void ValidateLinks(this ModelStateDictionary modelState, List links) + { + for (int i = 0; i < links.Count; i++) + { + var link = links[i]; + + // Validação condicional baseada no tipo + if (link.Type == LinkType.Product) + { + // Para links de produto, ProductTitle é obrigatório + if (string.IsNullOrWhiteSpace(link.ProductTitle)) + { + modelState.AddModelError($"Links[{i}].ProductTitle", "Título do produto é obrigatório"); + } + + // Title pode ser vazio para links de produto (será preenchido automaticamente) + modelState.Remove($"Links[{i}].Title"); + } + else + { + // Para links normais, Title é obrigatório + if (string.IsNullOrWhiteSpace(link.Title)) + { + modelState.AddModelError($"Links[{i}].Title", "Título é obrigatório"); + } + + // Campos de produto podem ser vazios para links normais + modelState.Remove($"Links[{i}].ProductTitle"); + } + } + } +} \ No newline at end of file diff --git a/src/BCards.Web/ViewModels/ManagePageViewModel.cs b/src/BCards.Web/ViewModels/ManagePageViewModel.cs index f3e460c..d36cbaa 100644 --- a/src/BCards.Web/ViewModels/ManagePageViewModel.cs +++ b/src/BCards.Web/ViewModels/ManagePageViewModel.cs @@ -50,14 +50,14 @@ public class ManageLinkViewModel public string Id { get; set; } = "new"; [Required(ErrorMessage = "Título é obrigatório")] - [StringLength(50, ErrorMessage = "Título deve ter no máximo 50 caracteres")] + [StringLength(200, ErrorMessage = "Título deve ter no máximo 50 caracteres")] public string Title { get; set; } = string.Empty; [Required(ErrorMessage = "URL é obrigatória")] [Url(ErrorMessage = "URL inválida")] public string Url { get; set; } = string.Empty; - [StringLength(100, ErrorMessage = "Descrição deve ter no máximo 100 caracteres")] + [StringLength(3000, ErrorMessage = "Descrição deve ter no máximo 100 caracteres")] public string Description { get; set; } = string.Empty; public string Icon { get; set; } = string.Empty; @@ -67,15 +67,15 @@ public class ManageLinkViewModel // Campos para Links de Produto public LinkType Type { get; set; } = LinkType.Normal; - [StringLength(100, ErrorMessage = "Título do produto deve ter no máximo 100 caracteres")] + [StringLength(200, ErrorMessage = "Título do produto deve ter no máximo 100 caracteres")] public string ProductTitle { get; set; } = string.Empty; public string ProductImage { get; set; } = string.Empty; [StringLength(50, ErrorMessage = "Preço deve ter no máximo 50 caracteres")] - public string ProductPrice { get; set; } = string.Empty; + public string? ProductPrice { get; set; } = string.Empty; - [StringLength(200, ErrorMessage = "Descrição do produto deve ter no máximo 200 caracteres")] + [StringLength(3000, ErrorMessage = "Descrição do produto deve ter no máximo 200 caracteres")] public string ProductDescription { get; set; } = string.Empty; public DateTime? ProductDataCachedAt { get; set; } diff --git a/src/BCards.Web/Views/Admin/ManagePage.cshtml b/src/BCards.Web/Views/Admin/ManagePage.cshtml index f2f1140..0af5ec6 100644 --- a/src/BCards.Web/Views/Admin/ManagePage.cshtml +++ b/src/BCards.Web/Views/Admin/ManagePage.cshtml @@ -129,32 +129,48 @@

Escolha um tema que combine com sua personalidade ou marca:

-
- @foreach (var theme in Model.AvailableThemes) + @{ + var themeCount = 0; + } + @foreach (var theme in Model.AvailableThemes) + { + @if (themeCount % 4 == 0) { -
-
-
-
-
-
@theme.Name
-
- + @if (themeCount > 0) + { + @:
+ } + @:
+ } + +
+
+
+
+
+
@theme.Name
-
- @theme.Name - @if (theme.IsPremium) - { - Premium - } +
+
+ @theme.Name + @if (theme.IsPremium) + { + Premium + } +
- } -
+
+ + themeCount++; + } + @if (Model.AvailableThemes.Any()) + { + @:
+ } @@ -194,39 +210,109 @@ "instagram" }; var match = myList.FirstOrDefault(stringToCheck => Model.Links[i].Icon.Contains(stringToCheck)); - if (match==null) { -