From ef4d189ef113c59fa5ae587f886a460e9f8c9e9c Mon Sep 17 00:00:00 2001 From: Ricardo Carneiro Date: Sun, 17 Aug 2025 19:36:30 -0300 Subject: [PATCH] =?UTF-8?q?feat:=20bot=C3=A3o=20fechar=20scroll=20de=20tem?= =?UTF-8?q?as?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Controllers/PaymentController.cs | 32 ++++- src/BCards.Web/Models/User.cs | 4 +- src/BCards.Web/Services/PaymentService.cs | 3 +- src/BCards.Web/Views/Admin/ManagePage.cshtml | 83 +++++++++-- src/BCards.Web/Views/UserPage/Display.cshtml | 129 +++++++++++++++++- 5 files changed, 234 insertions(+), 17 deletions(-) diff --git a/src/BCards.Web/Controllers/PaymentController.cs b/src/BCards.Web/Controllers/PaymentController.cs index 8727f87..b01b7b1 100644 --- a/src/BCards.Web/Controllers/PaymentController.cs +++ b/src/BCards.Web/Controllers/PaymentController.cs @@ -2,6 +2,7 @@ using BCards.Web.Models; using BCards.Web.Repositories; using BCards.Web.Services; using BCards.Web.ViewModels; +using BCards.Web.Utils; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc; @@ -110,12 +111,16 @@ public class PaymentController : Controller try { + // Parse do plano atual (mesmo que o Dashboard) + var userPlanType = Enum.TryParse(user.CurrentPlan, true, out var planType) ? planType : PlanType.Trial; + var currentPlanString = userPlanType.ToString().ToLower(); + var viewModel = new ManageSubscriptionViewModel { User = user, StripeSubscription = await _paymentService.GetSubscriptionDetailsAsync(user.Id), PaymentHistory = await _paymentService.GetPaymentHistoryAsync(user.Id), - AvailablePlans = GetAvailablePlans(user.CurrentPlan) + AvailablePlans = GetAvailablePlans(currentPlanString) }; // Pegar assinatura local se existir @@ -129,11 +134,15 @@ public class PaymentController : Controller } catch (Exception ex) { + // Parse do plano atual também no catch + var userPlanType = Enum.TryParse(user.CurrentPlan, true, out var planType) ? planType : PlanType.Trial; + var currentPlanString = userPlanType.ToString().ToLower(); + var errorViewModel = new ManageSubscriptionViewModel { User = user, ErrorMessage = $"Erro ao carregar dados da assinatura: {ex.Message}", - AvailablePlans = GetAvailablePlans(user.CurrentPlan) + AvailablePlans = GetAvailablePlans(currentPlanString) }; return View(errorViewModel); @@ -212,6 +221,8 @@ public class PaymentController : Controller { var plans = new List { + // Plano Trial não é incluído aqui pois é gerenciado internamente + // O "downgrade" para Trial acontece via cancelamento da assinatura new() { PlanType = "basic", @@ -250,8 +261,20 @@ public class PaymentController : Controller } }; - // Marcar upgrades e downgrades + // Marcar upgrades e filtrar downgrades var currentPlanIndex = plans.FindIndex(p => p.IsCurrentPlan); + + // Se usuário está no Trial (não encontrou plano atual), todos são upgrades + if (currentPlanIndex == -1 && (currentPlan == "trial" || currentPlan == "free")) + { + foreach (var plan in plans) + { + plan.IsUpgrade = true; + } + return plans; // Mostrar todos os planos pagos como upgrade + } + + // Para planos pagos, marcar apenas upgrades superiores for (int i = 0; i < plans.Count; i++) { if (i > currentPlanIndex) @@ -260,6 +283,7 @@ public class PaymentController : Controller plans[i].IsDowngrade = true; } - return plans; + // Retornar apenas plano atual e upgrades (Stripe não gerencia downgrades automaticamente) + return plans.Where(p => p.IsCurrentPlan || p.IsUpgrade).ToList(); } } \ No newline at end of file diff --git a/src/BCards.Web/Models/User.cs b/src/BCards.Web/Models/User.cs index 739b19d..a7910cf 100644 --- a/src/BCards.Web/Models/User.cs +++ b/src/BCards.Web/Models/User.cs @@ -25,10 +25,10 @@ public class User public string StripeCustomerId { get; set; } = string.Empty; [BsonElement("subscriptionStatus")] - public string SubscriptionStatus { get; set; } = "free"; + public string SubscriptionStatus { get; set; } = "trial"; [BsonElement("currentPlan")] - public string CurrentPlan { get; set; } = "free"; + public string CurrentPlan { get; set; } = "trial"; [BsonElement("createdAt")] public DateTime CreatedAt { get; set; } = DateTime.UtcNow; diff --git a/src/BCards.Web/Services/PaymentService.cs b/src/BCards.Web/Services/PaymentService.cs index a3cc862..84c00b7 100644 --- a/src/BCards.Web/Services/PaymentService.cs +++ b/src/BCards.Web/Services/PaymentService.cs @@ -322,7 +322,8 @@ public class PaymentService : IPaymentService user.SubscriptionStatus = stripeSubscription.Status; if (stripeSubscription.Status != "active") { - user.CurrentPlan = "free"; + // Quando assinatura não está ativa, usuário volta para o plano trial (gratuito) + user.CurrentPlan = "trial"; } await _userRepository.UpdateAsync(user); } diff --git a/src/BCards.Web/Views/Admin/ManagePage.cshtml b/src/BCards.Web/Views/Admin/ManagePage.cshtml index 69df0aa..132b811 100644 --- a/src/BCards.Web/Views/Admin/ManagePage.cshtml +++ b/src/BCards.Web/Views/Admin/ManagePage.cshtml @@ -162,10 +162,12 @@

Escolha um tema que combine com sua personalidade ou marca:

- @{ - var themeCount = 0; - } - @foreach (var theme in Model.AvailableThemes) + +
+ @{ + var themeCount = 0; + } + @foreach (var theme in Model.AvailableThemes) { @if (themeCount % 4 == 0) { @@ -200,10 +202,11 @@ themeCount++; } - @if (Model.AvailableThemes.Any()) - { - @:
- } + @if (Model.AvailableThemes.Any()) + { + @:
+ } + @@ -1605,6 +1608,70 @@ } +@section Styles { + +} + @if (TempData["Error"] != null) {
diff --git a/src/BCards.Web/Views/UserPage/Display.cshtml b/src/BCards.Web/Views/UserPage/Display.cshtml index 8812b6b..7e3fb69 100644 --- a/src/BCards.Web/Views/UserPage/Display.cshtml +++ b/src/BCards.Web/Views/UserPage/Display.cshtml @@ -42,6 +42,42 @@ } +@if (isPreview) +{ + +} +
@@ -240,8 +276,48 @@ @if (isPreview) { -
- MODO PREVIEW - Esta é uma prévia da sua página + +
+
+
+
+ + MODO PREVIEW +
+
+ Esta é uma prévia da sua página +
+
+ + +
+
+
+
+ + +
+
} @@ -335,6 +411,55 @@ } }); }); + + @if (isPreview) + { + @:// Mostrar toast informativo após carregar + @:setTimeout(function() { + @: var toastEl = document.getElementById('previewToast'); + @: if (toastEl) { + @: var toast = new bootstrap.Toast(toastEl); + @: toast.show(); + @: } + @:}, 1000); // Delay de 1 segundo + } }); + + @if (isPreview) + { + @:// Funções específicas do modo preview + @:function goBackToDashboard() { + @: // Detectar se está em mobile ou desktop + @: if (window.opener) { + @: // Está numa nova aba/janela - fechar e focar na aba pai + @: window.opener.focus(); + @: window.close(); + @: } else { + @: // Navegação normal - ir para o dashboard + @: window.location.href = '@Url.Action("Dashboard", "Admin")'; + @: } + @:} + @: + @:function closePreview() { + @: if (window.opener) { + @: // Está numa nova aba - fechar + @: window.close(); + @: } else { + @: // Tentar voltar na história ou ir para dashboard + @: if (window.history.length > 1) { + @: window.history.back(); + @: } else { + @: window.location.href = '@Url.Action("Dashboard", "Admin")'; + @: } + @: } + @:} + @: + @:// Detectar tecla ESC para fechar preview + @:document.addEventListener('keydown', function(e) { + @: if (e.key === 'Escape') { + @: closePreview(); + @: } + @:}); + } } \ No newline at end of file