fix: menu celular perfeito
All checks were successful
BCards Deployment Pipeline / Run Tests (push) Successful in 3s
BCards Deployment Pipeline / PR Validation (push) Has been skipped
BCards Deployment Pipeline / Build and Push Image (push) Successful in 15m11s
BCards Deployment Pipeline / Deploy to Production (ARM - OCI) (push) Successful in 1m16s
BCards Deployment Pipeline / Deploy to Staging (x86 - Local) (push) Has been skipped
BCards Deployment Pipeline / Cleanup Old Resources (push) Has been skipped
BCards Deployment Pipeline / Deployment Summary (push) Successful in 0s

This commit is contained in:
Ricardo Carneiro 2025-08-31 00:31:08 -03:00
parent bd90f7b064
commit e6d46572d1
4 changed files with 197 additions and 646 deletions

View File

@ -110,7 +110,7 @@
<div id="loading-bar"></div> <div id="loading-bar"></div>
<header> <header>
<nav class="navbar navbar-expand navbar-toggleable-sm navbar-light fixed-top @(ViewBag.IsHomePage == true ? "bg-home-blue" : "bg-dashboard")" id="mainNavbar"> <nav class="navbar navbar-expand-lg navbar-toggleable-sm navbar-light fixed-top @(ViewBag.IsHomePage == true ? "bg-home-blue" : "bg-dashboard")" id="mainNavbar">
<div class="container-fluid"> <div class="container-fluid">
<a class="navbar-brand fw-bold @(ViewBag.IsHomePage == true ? "text-white" : "text-primary")" <a class="navbar-brand fw-bold @(ViewBag.IsHomePage == true ? "text-white" : "text-primary")"
asp-area="" asp-controller="Home" asp-action="Index"> asp-area="" asp-controller="Home" asp-action="Index">
@ -120,15 +120,15 @@
<button class="navbar-toggler @(ViewBag.IsHomePage == true ? "navbar-dark" : "")" <button class="navbar-toggler @(ViewBag.IsHomePage == true ? "navbar-dark" : "")"
type="button" type="button"
data-bs-toggle="collapse" data-bs-toggle="collapse"
data-bs-target=".navbar-collapse" data-bs-target="#navbarSupportedContent"
aria-controls="navbarSupportedContent" aria-controls="navbarSupportedContent"
aria-expanded="false" aria-expanded="false"
aria-label="Toggle navigation"> aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span> <span class="navbar-toggler-icon"></span>
</button> </button>
<div class="navbar-collapse d-inline-flex justify-content-between"> <div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav flex-grow-1"> <ul class="navbar-nav me-auto mb-2 mb-lg-0">
<li class="nav-item"> <li class="nav-item">
<a class="nav-link @(ViewBag.IsHomePage == true ? "text-white" : "text-dark")" <a class="nav-link @(ViewBag.IsHomePage == true ? "text-white" : "text-dark")"
asp-area="" asp-controller="Home" asp-action="Index"> asp-area="" asp-controller="Home" asp-action="Index">
@ -326,4 +326,5 @@
@await RenderSectionAsync("Scripts", required: false) @await RenderSectionAsync("Scripts", required: false)
</body> </body>
</html>ody>
</html> </html>

View File

@ -0,0 +1,124 @@
@*
Layout dedicado para o modo de preview de páginas de usuário.
Contém a barra de preview amarela no topo, mas remove a navegação principal e o rodapé
para fornecer uma visualização limpa e evitar conflitos de layout.
*@
<!DOCTYPE html>
<html lang="pt-BR">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Preview - @(ViewData["Title"])</title>
@* Meta tags para evitar indexação de robôs no modo preview *@
<meta name="robots" content="noindex, nofollow, noarchive, nosnippet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<link rel="stylesheet" href="~/lib/bootstrap/css/bootstrap.min.css" />
<link rel="stylesheet" href="~/css/site.css" asp-append-version="true" />
<link rel="icon" type="image/x-icon" href="~/favicon.ico" />
@await RenderSectionAsync("Head", required: false)
@await RenderSectionAsync("Styles", required: false)
<style>
/* Garante que o corpo da página comece abaixo da barra de preview */
body {
padding-top: 56px; /* Altura da barra de preview */
}
/* Estilos para a barra de preview responsiva */
.preview-bar {
z-index: 1031; /* Acima do conteúdo da página, mas abaixo de modais do Bootstrap */
}
.preview-bar .btn-sm {
padding: 0.25rem 0.5rem;
font-size: 0.8rem;
}
/* Em telas pequenas, esconde o texto e mostra apenas ícones/botões principais */
@@media (max-width: 767.98px) {
.preview-bar .preview-text {
display: none; /* Esconde o texto "Esta é uma prévia..." */
}
.preview-bar .btn-back-text {
display: none; /* Esconde o texto do botão "Voltar" */
}
.preview-bar .btn-back i {
margin-right: 0 !important;
}
}
</style>
</head>
<body>
<!-- Barra de Preview Fixa no Topo -->
<div class="preview-bar position-fixed top-0 start-0 w-100 bg-warning text-dark shadow-sm">
<div class="container-fluid px-3 py-2">
<div class="d-flex justify-content-between align-items-center">
<!-- Lado Esquerdo: Ícone e Título -->
<div class="fw-bold">
<i class="fas fa-eye me-2"></i>
<span class="d-none d-md-inline">MODO PREVIEW</span>
</div>
<!-- Centro: Mensagem (visível em telas maiores) -->
<div class="preview-text text-center small d-none d-lg-block">
Esta é uma prévia de como sua página será exibida para os visitantes.
</div>
<!-- Lado Direito: Botões de Ação -->
<div class="d-flex align-items-center">
<button type="button" class="btn btn-sm btn-outline-dark me-2 btn-back" onclick="goBackToDashboard()">
<i class="fas fa-arrow-left me-1"></i>
<span class="btn-back-text">Voltar</span>
</button>
<button type="button" class="btn btn-sm btn-dark" onclick="closePreview()">
<i class="fas fa-times"></i>
</button>
</div>
</div>
</div>
</div>
<!-- Conteúdo Principal da Página -->
<main role="main">
@RenderBody()
</main>
<script src="~/lib/jquery/jquery.min.js"></script>
<script src="~/lib/bootstrap/js/bootstrap.bundle.min.js"></script>
@* Scripts JS que estavam no Display.cshtml para funcionalidade do preview *@
<script>
function goBackToDashboard() {
if (window.opener) {
window.opener.focus();
window.close();
} else {
window.location.href = '@Url.Action("Dashboard", "Admin")';
}
}
function closePreview() {
if (window.opener) {
window.close();
} else {
if (window.history.length > 1) {
window.history.back();
} else {
window.location.href = '@Url.Action("Dashboard", "Admin")';
}
}
}
document.addEventListener('keydown', function(e) {
if (e.key === 'Escape') {
closePreview();
}
});
</script>
@await RenderSectionAsync("Scripts", required: false)
</body>
</html>

View File

@ -6,15 +6,12 @@
var isLivePage = ViewBag.IsLivePage as bool? ?? false; var isLivePage = ViewBag.IsLivePage as bool? ?? false;
ViewData["Title"] = seo?.Title ?? $"{Model.DisplayName} - {category?.Name}"; ViewData["Title"] = seo?.Title ?? $"{Model.DisplayName} - {category?.Name}";
Layout = isPreview ? "_Layout" : "_UserPageLayout"; // Lógica de layout corrigida para usar o _PreviewLayout
Layout = isPreview ? "_PreviewLayout" : "_UserPageLayout";
} }
@section Head { @section Head {
@if (isPreview) @if (isLivePage)
{
<meta name="robots" content="noindex, nofollow, noarchive, nosnippet">
}
else if (isLivePage)
{ {
<meta name="robots" content="index, follow"> <meta name="robots" content="index, follow">
@if (!string.IsNullOrEmpty(ViewBag.PageUrl as string)) @if (!string.IsNullOrEmpty(ViewBag.PageUrl as string))
@ -24,6 +21,7 @@
} }
else else
{ {
// A meta tag de noindex/nofollow para previews já está no _PreviewLayout
<meta name="robots" content="noindex, nofollow"> <meta name="robots" content="noindex, nofollow">
} }
} }
@ -32,6 +30,7 @@
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style> <style>
@{ @{
// Renderiza os estilos do tema dinâmico
var partialOutput = await Html.PartialAsync("_ThemeStyles", Model.Theme); var partialOutput = await Html.PartialAsync("_ThemeStyles", Model.Theme);
using (var writer = new System.IO.StringWriter()) using (var writer = new System.IO.StringWriter())
{ {
@ -42,180 +41,6 @@
</style> </style>
} }
@if (!isPreview)
{
<style>
/* Layout normal sem preview - corrigir centralização */
.user-page {
display: flex !important;
align-items: center !important;
min-height: 100vh !important;
}
.user-page .container {
width: 100% !important;
max-width: 1140px !important;
margin: 0 auto !important;
}
.user-page .row {
display: flex !important;
justify-content: center !important;
margin: 0 !important;
}
.user-page .col-lg-6,
.user-page .col-md-8 {
display: block !important;
margin: 0 auto !important;
float: none !important;
}
.profile-card {
margin: 0 auto !important;
text-align: center !important;
}
</style>
}
@if (isPreview)
{
<style>
/* Compensar espaço da barra de preview */
body {
padding-top: 60px !important;
display: block !important; /* Override flexbox for user pages */
}
/* Corrigir centralização do conteúdo da página */
.user-page {
display: flex !important;
align-items: center !important;
min-height: calc(100vh - 60px) !important;
}
.user-page .container {
width: 100% !important;
max-width: 1140px !important;
margin: 0 auto !important;
}
.user-page .row {
display: flex !important;
justify-content: center !important;
margin: 0 !important;
}
.user-page .col-lg-6,
.user-page .col-md-8 {
display: block !important;
margin: 0 auto !important;
float: none !important;
}
.profile-card {
margin: 0 auto !important;
text-align: center !important;
}
/* Garantir que a barra de preview funcione corretamente */
.position-fixed.top-0 {
display: block !important;
flex: none !important;
height: 60px !important;
line-height: 1 !important;
}
.position-fixed.top-0 .container-fluid {
display: block !important;
flex: none !important;
padding: 0.75rem 1rem !important;
height: 100% !important;
}
.position-fixed.top-0 .container-fluid .row {
display: flex !important;
flex-wrap: nowrap !important;
align-items: center !important;
justify-content: space-between !important;
margin: 0 !important;
min-height: auto !important;
}
.position-fixed.top-0 .container-fluid .row .col-auto {
flex: 0 0 auto !important;
padding: 0 0.25rem !important;
white-space: nowrap !important;
}
.position-fixed.top-0 .container-fluid .row .col {
flex: 1 1 auto !important;
padding: 0 0.5rem !important;
text-align: center !important;
min-width: 0 !important;
}
/* Garantir que os botões fiquem na mesma linha */
.position-fixed.top-0 .btn {
margin: 0 0.25rem !important;
white-space: nowrap !important;
font-size: 0.875rem !important;
padding: 0.25rem 0.5rem !important;
}
/* Forçar layout horizontal para a barra toda */
.position-fixed.top-0 .container-fluid .row {
width: 100% !important;
max-width: 100% !important;
overflow: hidden !important;
}
/* Para telas grandes (Full HD+) */
@@media (min-width: 1200px) {
.position-fixed.top-0 .container-fluid {
max-width: 100% !important;
padding: 0.5rem 2rem !important;
}
.position-fixed.top-0 .container-fluid .row .col-auto:last-child {
flex-shrink: 0 !important;
white-space: nowrap !important;
}
.position-fixed.top-0 .btn {
display: inline-block !important;
margin: 0 0.125rem !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">
@ -255,9 +80,7 @@
var hasExpandableContent = (!string.IsNullOrEmpty(link.Description) || var hasExpandableContent = (!string.IsNullOrEmpty(link.Description) ||
(link.Type == BCards.Web.Models.LinkType.Product && !string.IsNullOrEmpty(link.ProductDescription))); (link.Type == BCards.Web.Models.LinkType.Product && !string.IsNullOrEmpty(link.ProductDescription)));
<!-- Universal Link Style (TODOS OS LINKS IGUAIS) -->
<div class="universal-link" data-link-id="@i"> <div class="universal-link" data-link-id="@i">
<!-- Header clicável (vai para o link) -->
<a href="@link.Url" <a href="@link.Url"
class="universal-link-header" class="universal-link-header"
onclick="recordClick('@Model.Id', @i)" onclick="recordClick('@Model.Id', @i)"
@ -267,7 +90,6 @@
<div class="universal-link-content"> <div class="universal-link-content">
@if (link.Type == BCards.Web.Models.LinkType.Product && !string.IsNullOrEmpty(link.ProductImage)) @if (link.Type == BCards.Web.Models.LinkType.Product && !string.IsNullOrEmpty(link.ProductImage))
{ {
<!-- Thumbnail para produtos -->
<img src="@link.ProductImage" <img src="@link.ProductImage"
alt="@(link.ProductTitle ?? link.Title)" alt="@(link.ProductTitle ?? link.Title)"
class="link-thumbnail" class="link-thumbnail"
@ -276,17 +98,11 @@
} }
else if (!string.IsNullOrEmpty(link.Icon)) else if (!string.IsNullOrEmpty(link.Icon))
{ {
<!-- Ícone para links normais --> <div class="link-icon"><i class="@link.Icon"></i></div>
<div class="link-icon">
<i class="@link.Icon"></i>
</div>
} }
else else
{ {
<!-- Ícone padrão se não tiver --> <div class="link-icon"><i class="fas fa-link"></i></div>
<div class="link-icon">
<i class="fas fa-link"></i>
</div>
} }
<div class="link-text-container"> <div class="link-text-container">
@ -314,7 +130,6 @@
@if (hasExpandableContent) @if (hasExpandableContent)
{ {
<!-- Seta de expansão (só aparece se tem conteúdo expandível) -->
<button class="expand-arrow" <button class="expand-arrow"
type="button" type="button"
onclick="event.preventDefault(); event.stopPropagation(); toggleLinkDetails(@i)"> onclick="event.preventDefault(); event.stopPropagation(); toggleLinkDetails(@i)">
@ -325,11 +140,9 @@
@if (hasExpandableContent) @if (hasExpandableContent)
{ {
<!-- Conteúdo expandível -->
<div class="universal-link-details" id="details-@i"> <div class="universal-link-details" id="details-@i">
@if (link.Type == BCards.Web.Models.LinkType.Product) @if (link.Type == BCards.Web.Models.LinkType.Product)
{ {
<!-- Conteúdo expandido para produtos -->
@if (!string.IsNullOrEmpty(link.ProductImage)) @if (!string.IsNullOrEmpty(link.ProductImage))
{ {
<img src="@link.ProductImage" <img src="@link.ProductImage"
@ -337,30 +150,22 @@
class="expanded-image" class="expanded-image"
loading="lazy"> loading="lazy">
} }
@if (!string.IsNullOrEmpty(link.ProductPrice)) @if (!string.IsNullOrEmpty(link.ProductPrice))
{ {
<div class="expanded-price">@link.ProductPrice</div> <div class="expanded-price">@link.ProductPrice</div>
} }
@if (!string.IsNullOrEmpty(link.ProductDescription)) @if (!string.IsNullOrEmpty(link.ProductDescription))
{ {
<div class="expanded-description"> <div class="expanded-description">@link.ProductDescription</div>
@link.ProductDescription
</div>
} }
} }
else else
{ {
<!-- Conteúdo expandido para links normais -->
@if (!string.IsNullOrEmpty(link.Description)) @if (!string.IsNullOrEmpty(link.Description))
{ {
<div class="expanded-description"> <div class="expanded-description">@link.Description</div>
@link.Description
</div>
} }
} }
<div class="expanded-action"> <div class="expanded-action">
<i class="fas fa-external-link-alt"></i> <i class="fas fa-external-link-alt"></i>
Clique no título acima para abrir Clique no título acima para abrir
@ -381,8 +186,6 @@
<!-- Footer --> <!-- Footer -->
<div class="profile-footer"> <div class="profile-footer">
<!-- Promoção BCards -->
<div class="footer-promo" onclick="togglePromo(this)"> <div class="footer-promo" onclick="togglePromo(this)">
<div class="footer-promo-header"> <div class="footer-promo-header">
<span>💡 Gostou desta página?</span> <span>💡 Gostou desta página?</span>
@ -399,69 +202,20 @@
</div> </div>
</div> </div>
</div> </div>
<div class="footer-credits"> <div class="footer-credits">
Criado com <a href="@Url.Action("Index", "Home")">BCards</a> Criado com <a href="@Url.Action("Index", "Home")">BCards</a>
</div> </div>
</div> </div>
</div> <!-- /text-center --> </div>
</div> <!-- /profile-card --> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
@if (isPreview)
{
<!-- 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 e clique <i class="fas fa-cog"></i></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-cog"></i> >>
Editar Página
</button>
</div>
</div>
</div>
}
@section Scripts { @section Scripts {
<script> <script>
// Função original de rastreamento de cliques
function recordClick(pageId, linkIndex) { function recordClick(pageId, linkIndex) {
fetch('/click/' + pageId, { fetch('/click/' + pageId, {
method: 'POST', method: 'POST',
@ -474,130 +228,45 @@
}); });
} }
// Toggle link details (função universal para todos os links)
function toggleLinkDetails(linkIndex) { function toggleLinkDetails(linkIndex) {
const currentDetails = document.getElementById('details-' + linkIndex); const currentDetails = document.getElementById('details-' + linkIndex);
const currentArrow = document.querySelector('[data-link-id="' + linkIndex + '"] .expand-arrow'); const currentArrow = document.querySelector(`[data-link-id="${linkIndex}"] .expand-arrow`);
if (!currentDetails || !currentArrow) return; if (!currentDetails || !currentArrow) return;
const isCurrentlyExpanded = currentDetails.classList.contains('show'); const isCurrentlyExpanded = currentDetails.classList.contains('show');
// Fechar todos os outros links primeiro (auto-close) document.querySelectorAll('.universal-link-details').forEach(details => details.classList.remove('show'));
const allDetails = document.querySelectorAll('.universal-link-details'); document.querySelectorAll('.expand-arrow').forEach(arrow => {
const allArrows = document.querySelectorAll('.expand-arrow');
allDetails.forEach(details => {
details.classList.remove('show');
});
allArrows.forEach(arrow => {
arrow.classList.remove('expanded'); arrow.classList.remove('expanded');
const icon = arrow.querySelector('i'); arrow.querySelector('i').style.transform = 'rotate(0deg)';
if (icon) {
icon.style.transform = 'rotate(0deg)';
}
}); });
// Se não estava expandido, expandir este
if (!isCurrentlyExpanded) { if (!isCurrentlyExpanded) {
currentDetails.classList.add('show'); currentDetails.classList.add('show');
currentArrow.classList.add('expanded'); currentArrow.classList.add('expanded');
const icon = currentArrow.querySelector('i'); currentArrow.querySelector('i').style.transform = 'rotate(180deg)';
if (icon) {
icon.style.transform = 'rotate(180deg)';
}
} }
} }
// Toggle footer promo
function togglePromo(element) { function togglePromo(element) {
const content = element.querySelector('.footer-promo-content'); const content = element.querySelector('.footer-promo-content');
const arrow = element.querySelector('.footer-promo-header i'); const arrow = element.querySelector('.footer-promo-header i');
const isExpanded = content.classList.toggle('show');
if (content.classList.contains('show')) { arrow.style.transform = isExpanded ? 'rotate(180deg)' : 'rotate(0deg)';
content.classList.remove('show'); element.querySelector('.footer-promo-header').classList.toggle('expanded', isExpanded);
arrow.style.transform = 'rotate(0deg)';
element.querySelector('.footer-promo-header').classList.remove('expanded');
} else {
content.classList.add('show');
arrow.style.transform = 'rotate(180deg)';
element.querySelector('.footer-promo-header').classList.add('expanded');
}
} }
// Initialize page
document.addEventListener('DOMContentLoaded', function() { document.addEventListener('DOMContentLoaded', function() {
// Garantir que todos os accordions comecem fechados document.querySelectorAll('.universal-link-details, .footer-promo-content').forEach(d => d.classList.remove('show'));
const allDetails = document.querySelectorAll('.universal-link-details, .footer-promo-content'); document.querySelectorAll('.expand-arrow i, .footer-promo-header i').forEach(arrow => arrow.style.transform = 'rotate(0deg)');
allDetails.forEach(detail => { document.querySelectorAll('.expand-arrow').forEach(button => {
detail.classList.remove('show'); button.addEventListener('keydown', e => {
});
const allArrows = document.querySelectorAll('.expand-arrow i, .footer-promo-header i');
allArrows.forEach(arrow => {
arrow.style.transform = 'rotate(0deg)';
});
// Adicionar eventos de teclado para acessibilidade
const expandButtons = document.querySelectorAll('.expand-arrow');
expandButtons.forEach(button => {
button.addEventListener('keydown', function(e) {
if (e.key === 'Enter' || e.key === ' ') { if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault(); e.preventDefault();
button.click(); button.click();
} }
}); });
}); });
@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>
} }

View File

@ -13,68 +13,13 @@ html {
} }
html { html {
height: 100%; position: relative;
min-height: 100%;
} }
/* Ajuste no padding do body para a navbar fixa */
body { body {
min-height: 100vh; padding-top: 70px;
padding-top: 70px; /* Altura da navbar fixa */
display: flex;
flex-direction: column;
}
/* Garantir que a navbar não seja afetada pelo flexbox */
.navbar {
position: fixed !important;
top: 0 !important;
left: 0 !important;
right: 0 !important;
z-index: 1030 !important;
}
/* Container principal com flexbox apenas para o conteúdo */
body > .container-fluid {
flex: 1;
display: flex;
flex-direction: column;
min-height: calc(100vh - 70px); /* Altura total menos navbar */
}
/* Exceções para containers que não devem usar flexbox */
.position-fixed .container-fluid,
.navbar .container-fluid,
.user-page .container {
display: block !important;
flex: none !important;
min-height: auto !important;
}
/* Correção específica para a tarja de preview */
.position-fixed .container-fluid .row {
display: flex !important;
flex-direction: row !important;
align-items: center !important;
margin: 0 !important;
}
.position-fixed .container-fluid .row .col,
.position-fixed .container-fluid .row .col-auto {
display: block !important;
flex: none !important;
}
/* Garantir que páginas de usuário não sejam afetadas pelo flexbox global */
.user-page {
flex: none !important;
}
/* Reset específico para user pages - deixar que o CSS inline controle */
.user-page .container,
.user-page .row,
.user-page .col,
.user-page .col-lg-6,
.user-page .col-md-8 {
/* CSS inline controlará o layout */
} }
main { main {
@ -82,11 +27,11 @@ main {
} }
.footer { .footer {
position: absolute;
bottom: 0;
width: 100%; width: 100%;
margin-top: auto; white-space: nowrap;
background-color: #f8f9fa; line-height: 60px;
border-top: 1px solid #dee2e6;
padding: 20px 0;
} }
/* Custom Styles */ /* Custom Styles */
@ -140,17 +85,6 @@ main {
left: 100%; left: 100%;
} }
/* Responsive improvements */
@media (max-width: 768px) {
.hero-section h1 {
font-size: 2.5rem;
}
.hero-section .lead {
font-size: 1.1rem;
}
}
/* Card improvements */ /* Card improvements */
.card { .card {
border-radius: 15px; border-radius: 15px;
@ -201,235 +135,58 @@ main {
box-shadow: 0 0 0 0.2rem rgba(0,123,255,.25); box-shadow: 0 0 0 0.2rem rgba(0,123,255,.25);
} }
/* Navbar improvements */ /* Navbar base improvements */
.navbar { .navbar {
border-radius: 0;
box-shadow: 0 2px 10px rgba(0,0,0,0.1); box-shadow: 0 2px 10px rgba(0,0,0,0.1);
display: flex !important;
flex-wrap: nowrap !important;
}
/* Corrigir problemas de layout da navbar */
.navbar .container-fluid {
display: flex !important;
flex-direction: row !important;
min-height: auto !important;
flex-wrap: nowrap !important;
}
.navbar-nav {
display: flex !important;
flex-direction: row !important;
flex-wrap: nowrap !important;
}
.navbar-collapse {
display: flex !important;
flex-basis: auto !important;
} }
.nav-link { .nav-link {
border-radius: 20px;
margin: 0 0.2rem; margin: 0 0.2rem;
transition: all 0.3s ease; transition: all 0.3s ease;
} }
.nav-link:hover { /* Menu Home (gradiente) */
background-color: rgba(0,123,255,0.1);
}
/* Table improvements */
.table {
border-radius: 15px;
overflow: hidden;
box-shadow: 0 4px 15px rgba(0,0,0,0.1);
}
.table thead th {
border-bottom: none;
font-weight: 600;
}
/* Accordion improvements */
.accordion-item {
border-radius: 10px;
margin-bottom: 1rem;
border: 1px solid #e9ecef;
}
.accordion-button {
border-radius: 10px;
font-weight: 500;
}
.accordion-button:not(.collapsed) {
background-color: #f8f9fa;
color: #007bff;
}
/* Loading animation */
.loading {
display: inline-block;
width: 20px;
height: 20px;
border: 3px solid rgba(255,255,255,.3);
border-radius: 50%;
border-top-color: #fff;
animation: spin 1s ease-in-out infinite;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
/* Utility classes */
.text-gradient {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.shadow-primary {
box-shadow: 0 4px 15px rgba(0,123,255,0.3);
}
.border-gradient {
border: 2px solid;
border-image: linear-gradient(135deg, #667eea 0%, #764ba2 100%) 1;
}
/* Menu Home (mesmo gradiente do fundo principal) */
.bg-home-blue { .bg-home-blue {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important;
/* Removida linha divisória na home */
} }
/* Menu Dashboard (mais neutro) */
.bg-dashboard {
background-color: #ffffff !important;
border-bottom: 1px solid #dee2e6;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
/* Responsive adjustments */
@media (max-width: 576px) {
body {
padding-top: 60px;
}
.bg-home-blue .navbar-nav {
background: rgba(102, 126, 234, 0.95);
border-radius: 8px;
margin-top: 10px;
padding: 10px;
flex-direction: column !important;
}
.navbar-nav {
flex-direction: column !important;
}
.navbar-collapse {
flex-direction: column !important;
}
}
/* Para telas médias e grandes, manter horizontal */
@media (min-width: 577px) {
.navbar-nav {
flex-direction: row !important;
}
.navbar-collapse {
flex-direction: row !important;
justify-content: space-between !important;
}
}
/* Hamburger menu para home */
.navbar-dark .navbar-toggler-icon {
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.85%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e");
}
/* Correção para problema visual do card Premium na página de pricing */
.pricing-cards {
margin-top: 80px;
}
.pricing-premium-badge {
top: -20px !important;
}
/* Garantir que badges não sejam cortados */
.pricing-cards .position-absolute {
z-index: 10;
}
.pricing-cards .card {
overflow: visible;
}
/* Layout otimizado para 5 cards */
@media (min-width: 1200px) {
/* XL+: 5 cards em uma linha com largura igual */
.pricing-cards {
display: flex;
flex-wrap: wrap;
justify-content: center;
}
.pricing-cards .col-xl-2 {
flex: 0 0 18%;
max-width: 18%;
margin: 0 1%;
}
}
@media (min-width: 992px) and (max-width: 1199.98px) {
/* LG: 3 na primeira linha, 2 na segunda (centralizado) */
.pricing-cards {
display: flex;
flex-wrap: wrap;
justify-content: center;
}
.pricing-cards .col-lg-4 {
flex: 0 0 30%;
max-width: 30%;
margin: 0 1.5% 20px 1.5%;
}
/* Quebra de linha após o 3º elemento */
.pricing-cards .col-lg-4:nth-child(4) {
margin-top: 20px;
}
}
@media (max-width: 991.98px) {
.pricing-cards {
margin-top: 60px;
}
}
/* Hover effects para home menu */
.bg-home-blue .nav-link:hover {
background-color: rgba(255, 255, 255, 0.1);
border-radius: 4px;
transition: all 0.3s ease;
}
/* Hover effects para dashboard menu */
.bg-dashboard .nav-link:hover {
background-color: rgba(37, 99, 235, 0.1);
border-radius: 4px;
transition: all 0.3s ease;
}
/* Garantir contraste adequado no menu home */
.bg-home-blue .navbar-brand, .bg-home-blue .navbar-brand,
.bg-home-blue .nav-link { .bg-home-blue .nav-link {
color: #ffffff !important; color: #ffffff !important;
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.1); text-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
} }
.bg-home-blue .nav-link:hover {
background-color: rgba(255, 255, 255, 0.1);
border-radius: 4px;
}
/* Hamburger menu icon color for dark backgrounds */
.navbar-dark .navbar-toggler-icon {
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.85%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e");
}
/* Menu Dashboard (neutro) */
.bg-dashboard {
background-color: #ffffff !important;
border-bottom: 1px solid #dee2e6;
}
.bg-dashboard .nav-link:hover {
background-color: rgba(37, 99, 235, 0.1);
border-radius: 4px;
}
/* Estilos para o menu recolhido (mobile) */
@media (max-width: 991.98px) {
.navbar-collapse {
padding: 1rem;
background-color: rgba(255, 255, 255, 0.98);
border-radius: 0.5rem;
margin-top: 0.5rem;
box-shadow: 0 4px 15px rgba(0,0,0,0.1);
}
.bg-home-blue .navbar-collapse {
background-color: rgba(118, 75, 162, 0.95); /* Cor do gradiente para o menu recolhido */
}
}