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.Repositories;
|
||||||
using BCards.Web.Services;
|
using BCards.Web.Services;
|
||||||
using BCards.Web.ViewModels;
|
using BCards.Web.ViewModels;
|
||||||
|
using BCards.Web.Utils;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Identity;
|
using Microsoft.AspNetCore.Identity;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
@ -110,12 +111,16 @@ public class PaymentController : Controller
|
|||||||
|
|
||||||
try
|
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
|
var viewModel = new ManageSubscriptionViewModel
|
||||||
{
|
{
|
||||||
User = user,
|
User = user,
|
||||||
StripeSubscription = await _paymentService.GetSubscriptionDetailsAsync(user.Id),
|
StripeSubscription = await _paymentService.GetSubscriptionDetailsAsync(user.Id),
|
||||||
PaymentHistory = await _paymentService.GetPaymentHistoryAsync(user.Id),
|
PaymentHistory = await _paymentService.GetPaymentHistoryAsync(user.Id),
|
||||||
AvailablePlans = GetAvailablePlans(user.CurrentPlan)
|
AvailablePlans = GetAvailablePlans(currentPlanString)
|
||||||
};
|
};
|
||||||
|
|
||||||
// Pegar assinatura local se existir
|
// Pegar assinatura local se existir
|
||||||
@ -129,11 +134,15 @@ public class PaymentController : Controller
|
|||||||
}
|
}
|
||||||
catch (Exception ex)
|
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
|
var errorViewModel = new ManageSubscriptionViewModel
|
||||||
{
|
{
|
||||||
User = user,
|
User = user,
|
||||||
ErrorMessage = $"Erro ao carregar dados da assinatura: {ex.Message}",
|
ErrorMessage = $"Erro ao carregar dados da assinatura: {ex.Message}",
|
||||||
AvailablePlans = GetAvailablePlans(user.CurrentPlan)
|
AvailablePlans = GetAvailablePlans(currentPlanString)
|
||||||
};
|
};
|
||||||
|
|
||||||
return View(errorViewModel);
|
return View(errorViewModel);
|
||||||
@ -212,6 +221,8 @@ public class PaymentController : Controller
|
|||||||
{
|
{
|
||||||
var plans = new List<AvailablePlanViewModel>
|
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()
|
new()
|
||||||
{
|
{
|
||||||
PlanType = "basic",
|
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);
|
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++)
|
for (int i = 0; i < plans.Count; i++)
|
||||||
{
|
{
|
||||||
if (i > currentPlanIndex)
|
if (i > currentPlanIndex)
|
||||||
@ -260,6 +283,7 @@ public class PaymentController : Controller
|
|||||||
plans[i].IsDowngrade = true;
|
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;
|
public string StripeCustomerId { get; set; } = string.Empty;
|
||||||
|
|
||||||
[BsonElement("subscriptionStatus")]
|
[BsonElement("subscriptionStatus")]
|
||||||
public string SubscriptionStatus { get; set; } = "free";
|
public string SubscriptionStatus { get; set; } = "trial";
|
||||||
|
|
||||||
[BsonElement("currentPlan")]
|
[BsonElement("currentPlan")]
|
||||||
public string CurrentPlan { get; set; } = "free";
|
public string CurrentPlan { get; set; } = "trial";
|
||||||
|
|
||||||
[BsonElement("createdAt")]
|
[BsonElement("createdAt")]
|
||||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||||
|
|||||||
@ -322,7 +322,8 @@ public class PaymentService : IPaymentService
|
|||||||
user.SubscriptionStatus = stripeSubscription.Status;
|
user.SubscriptionStatus = stripeSubscription.Status;
|
||||||
if (stripeSubscription.Status != "active")
|
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);
|
await _userRepository.UpdateAsync(user);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -162,10 +162,12 @@
|
|||||||
<div class="accordion-body">
|
<div class="accordion-body">
|
||||||
<p class="text-muted mb-4">Escolha um tema que combine com sua personalidade ou marca:</p>
|
<p class="text-muted mb-4">Escolha um tema que combine com sua personalidade ou marca:</p>
|
||||||
|
|
||||||
@{
|
<!-- Container com scroll para os temas -->
|
||||||
var themeCount = 0;
|
<div class="themes-container" style="max-height: 400px; overflow-y: auto; overflow-x: hidden; padding-right: 10px;">
|
||||||
}
|
@{
|
||||||
@foreach (var theme in Model.AvailableThemes)
|
var themeCount = 0;
|
||||||
|
}
|
||||||
|
@foreach (var theme in Model.AvailableThemes)
|
||||||
{
|
{
|
||||||
@if (themeCount % 4 == 0)
|
@if (themeCount % 4 == 0)
|
||||||
{
|
{
|
||||||
@ -200,10 +202,11 @@
|
|||||||
|
|
||||||
themeCount++;
|
themeCount++;
|
||||||
}
|
}
|
||||||
@if (Model.AvailableThemes.Any())
|
@if (Model.AvailableThemes.Any())
|
||||||
{
|
{
|
||||||
@:</div>
|
@:</div>
|
||||||
}
|
}
|
||||||
|
</div> <!-- /themes-container -->
|
||||||
|
|
||||||
<input asp-for="SelectedTheme" type="hidden">
|
<input asp-for="SelectedTheme" type="hidden">
|
||||||
|
|
||||||
@ -1605,6 +1608,70 @@
|
|||||||
</script>
|
</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)
|
@if (TempData["Error"] != null)
|
||||||
{
|
{
|
||||||
<div class="toast-container position-fixed top-0 end-0 p-3">
|
<div class="toast-container position-fixed top-0 end-0 p-3">
|
||||||
|
|||||||
@ -42,6 +42,42 @@
|
|||||||
</style>
|
</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="user-page min-vh-100 d-flex align-items-center py-4">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="row justify-content-center">
|
<div class="row justify-content-center">
|
||||||
@ -240,8 +276,48 @@
|
|||||||
|
|
||||||
@if (isPreview)
|
@if (isPreview)
|
||||||
{
|
{
|
||||||
<div class="position-fixed top-0 start-0 w-100 bg-warning text-dark text-center py-2" style="z-index: 9999;">
|
<!-- Barra de Preview Melhorada -->
|
||||||
<strong>MODO PREVIEW</strong> - Esta é uma prévia da sua página
|
<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>
|
</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>
|
</script>
|
||||||
}
|
}
|
||||||
Loading…
Reference in New Issue
Block a user