feat: botão fechar scroll de temas
This commit is contained in:
parent
ca98001299
commit
ef4d189ef1
@ -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<PlanType>(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<PlanType>(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<AvailablePlanViewModel>
|
||||
{
|
||||
// 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();
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -162,6 +162,8 @@
|
||||
<div class="accordion-body">
|
||||
<p class="text-muted mb-4">Escolha um tema que combine com sua personalidade ou marca:</p>
|
||||
|
||||
<!-- Container com scroll para os temas -->
|
||||
<div class="themes-container" style="max-height: 400px; overflow-y: auto; overflow-x: hidden; padding-right: 10px;">
|
||||
@{
|
||||
var themeCount = 0;
|
||||
}
|
||||
@ -204,6 +206,7 @@
|
||||
{
|
||||
@:</div>
|
||||
}
|
||||
</div> <!-- /themes-container -->
|
||||
|
||||
<input asp-for="SelectedTheme" type="hidden">
|
||||
|
||||
@ -1605,6 +1608,70 @@
|
||||
</script>
|
||||
}
|
||||
|
||||
@section Styles {
|
||||
<style>
|
||||
/* Estilo customizado para o scroll dos temas */
|
||||
.themes-container {
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: #007bff #f8f9fa;
|
||||
}
|
||||
|
||||
.themes-container::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
}
|
||||
|
||||
.themes-container::-webkit-scrollbar-track {
|
||||
background: #f8f9fa;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.themes-container::-webkit-scrollbar-thumb {
|
||||
background: #007bff;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #f8f9fa;
|
||||
}
|
||||
|
||||
.themes-container::-webkit-scrollbar-thumb:hover {
|
||||
background: #0056b3;
|
||||
}
|
||||
|
||||
/* Fade gradient no topo e bottom para indicar scroll */
|
||||
.themes-container::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 20px;
|
||||
background: linear-gradient(to bottom, rgba(248, 249, 250, 1), rgba(248, 249, 250, 0));
|
||||
pointer-events: none;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.themes-container::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 20px;
|
||||
background: linear-gradient(to top, rgba(248, 249, 250, 1), rgba(248, 249, 250, 0));
|
||||
pointer-events: none;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
/* Smooth scroll */
|
||||
.themes-container {
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
|
||||
/* Container relativo para os gradients */
|
||||
.accordion-body {
|
||||
position: relative;
|
||||
}
|
||||
</style>
|
||||
}
|
||||
|
||||
@if (TempData["Error"] != null)
|
||||
{
|
||||
<div class="toast-container position-fixed top-0 end-0 p-3">
|
||||
|
||||
@ -42,6 +42,42 @@
|
||||
</style>
|
||||
}
|
||||
|
||||
@if (isPreview)
|
||||
{
|
||||
<style>
|
||||
/* Compensar espaço da barra de preview */
|
||||
body {
|
||||
padding-top: 60px !important;
|
||||
}
|
||||
|
||||
/* Responsivo para mobile */
|
||||
@@media (max-width: 768px) {
|
||||
.position-fixed .container-fluid .row .col:not(.col-auto) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.position-fixed .container-fluid .row .col-auto:first-child {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
body {
|
||||
padding-top: 50px !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Animação suave para o toast */
|
||||
.toast {
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
/* Melhorar aparência dos botões na barra */
|
||||
.btn-outline-dark:hover {
|
||||
background-color: rgba(0,0,0,0.1);
|
||||
border-color: rgba(0,0,0,0.3);
|
||||
}
|
||||
</style>
|
||||
}
|
||||
|
||||
<div class="user-page min-vh-100 d-flex align-items-center py-4">
|
||||
<div class="container">
|
||||
<div class="row justify-content-center">
|
||||
@ -240,8 +276,48 @@
|
||||
|
||||
@if (isPreview)
|
||||
{
|
||||
<div class="position-fixed top-0 start-0 w-100 bg-warning text-dark text-center py-2" style="z-index: 9999;">
|
||||
<strong>MODO PREVIEW</strong> - Esta é uma prévia da sua página
|
||||
<!-- Barra de Preview Melhorada -->
|
||||
<div class="position-fixed top-0 start-0 w-100 bg-warning text-dark py-2" style="z-index: 9999; box-shadow: 0 2px 10px rgba(0,0,0,0.1);">
|
||||
<div class="container-fluid">
|
||||
<div class="row align-items-center">
|
||||
<div class="col-auto">
|
||||
<i class="fas fa-eye me-2"></i>
|
||||
<strong>MODO PREVIEW</strong>
|
||||
</div>
|
||||
<div class="col text-center">
|
||||
<span class="small">Esta é uma prévia da sua página</span>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<button type="button" class="btn btn-sm btn-outline-dark me-2" onclick="goBackToDashboard()">
|
||||
<i class="fas fa-arrow-left me-1"></i>
|
||||
Voltar ao Dashboard
|
||||
</button>
|
||||
<button type="button" class="btn btn-sm btn-dark" onclick="closePreview()">
|
||||
<i class="fas fa-times"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Toast Informativo -->
|
||||
<div class="toast-container position-fixed bottom-0 end-0 p-3" style="z-index: 9998;">
|
||||
<div id="previewToast" class="toast" role="alert" aria-live="assertive" aria-atomic="true" data-bs-autohide="false">
|
||||
<div class="toast-header bg-info text-white">
|
||||
<i class="fas fa-info-circle me-2"></i>
|
||||
<strong class="me-auto">Modo Preview</strong>
|
||||
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="toast"></button>
|
||||
</div>
|
||||
<div class="toast-body">
|
||||
<p class="mb-2"><strong>Você está visualizando uma prévia!</strong></p>
|
||||
<p class="mb-2 small">• Para fazer alterações, volte ao Dashboard</p>
|
||||
<p class="mb-2 small">• Esta prévia não é a página final publicada</p>
|
||||
<button type="button" class="btn btn-sm btn-primary w-100" onclick="goBackToDashboard()">
|
||||
<i class="fas fa-edit me-1"></i>
|
||||
Editar Página
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
@ -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();
|
||||
@: }
|
||||
@:});
|
||||
}
|
||||
</script>
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user