BCards/src/BCards.Web/Views/Shared/_Layout.cshtml
2025-08-17 20:50:40 -03:00

329 lines
14 KiB
Plaintext

<!DOCTYPE html>
<html lang="pt-BR">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>@(ViewData["Title"] ?? "BCards - Crie sua bios / links Profissional")</title>
@if (ViewBag.SeoSettings != null)
{
var seo = ViewBag.SeoSettings as BCards.Web.Models.SeoSettings;
<meta name="description" content="@seo?.Description" />
<meta name="keywords" content="@string.Join(", ", seo?.Keywords ?? new List<string>())" />
<link rel="canonical" href="@seo?.CanonicalUrl" />
<!-- Open Graph -->
<meta property="og:title" content="@seo?.OgTitle" />
<meta property="og:description" content="@seo?.OgDescription" />
<meta property="og:image" content="@seo?.OgImage" />
<meta property="og:url" content="@seo?.CanonicalUrl" />
<meta property="og:type" content="profile" />
<!-- Twitter Card -->
<meta name="twitter:card" content="@seo?.TwitterCard" />
<meta name="twitter:title" content="@seo?.OgTitle" />
<meta name="twitter:description" content="@seo?.OgDescription" />
<meta name="twitter:image" content="@seo?.OgImage" />
}
else
{
<meta name="description" content="Crie sua página profissional com links organizados. A melhor alternativa para ter sua bio / links. Criada para profissionais e empresas no Brasil." />
<meta name="keywords" content="linktree, links, página profissional, perfil, redes sociais, cartão digital, bio, links, página simples" />
}
@await RenderSectionAsync("Head", required: false)
<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("Styles", required: false)
<!-- Estilos para menu ativo e barra de carregamento -->
<style>
/* Barra de carregamento moderna */
#loading-bar {
position: fixed;
top: 0;
left: 0;
width: 0%;
height: 3px;
background: linear-gradient(90deg, #007bff, #0056b3, #007bff);
background-size: 200% 100%;
animation: loading-shimmer 1.5s infinite;
z-index: 9999;
transition: width 0.3s ease, opacity 0.3s ease;
opacity: 0;
}
#loading-bar.active {
opacity: 1;
}
@@keyframes loading-shimmer {
0% { background-position: -200% 0; }
100% { background-position: 200% 0; }
}
/* Destacar item ativo do menu */
.nav-link.active {
background-color: rgba(0, 123, 255, 0.1) !important;
border-radius: 6px !important;
font-weight: 600 !important;
}
/* Para homepage (fundo azul) */
.bg-home-blue .nav-link.active {
background-color: rgba(255, 255, 255, 0.2) !important;
}
/* Smooth transition para links */
.nav-link {
transition: all 0.2s ease;
padding: 8px 12px !important;
margin: 0 2px;
border-radius: 6px;
}
.nav-link:hover {
background-color: rgba(0, 123, 255, 0.05);
}
.bg-home-blue .nav-link:hover {
background-color: rgba(255, 255, 255, 0.1);
}
/* Suavizar transições de página */
main {
opacity: 1;
transition: opacity 0.2s ease;
}
main.page-loading {
opacity: 0.7;
}
</style>
</head>
<body>
<!-- Barra de carregamento -->
<div id="loading-bar"></div>
<header>
<nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-light fixed-top @(ViewBag.IsHomePage == true ? "bg-home-blue" : "bg-dashboard")" id="mainNavbar">
<div class="container-fluid">
<a class="navbar-brand fw-bold @(ViewBag.IsHomePage == true ? "text-white" : "text-primary")"
asp-area="" asp-controller="Home" asp-action="Index">
BCards
</a>
<button class="navbar-toggler @(ViewBag.IsHomePage == true ? "navbar-dark" : "")"
type="button"
data-bs-toggle="collapse"
data-bs-target=".navbar-collapse"
aria-controls="navbarSupportedContent"
aria-expanded="false"
aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="navbar-collapse collapse d-sm-inline-flex justify-content-between">
<ul class="navbar-nav flex-grow-1">
<li class="nav-item">
<a class="nav-link @(ViewBag.IsHomePage == true ? "text-white" : "text-dark")"
asp-area="" asp-controller="Home" asp-action="Index">
Início
</a>
</li>
<li class="nav-item">
<a class="nav-link @(ViewBag.IsHomePage == true ? "text-white" : "text-dark")"
asp-area="" asp-controller="Home" asp-action="Pricing">
Planos
</a>
</li>
@* Menu de Moderação via ViewComponent *@
@await Component.InvokeAsync("ModerationMenu")
</ul>
<ul class="navbar-nav">
@if (User.Identity?.IsAuthenticated == true)
{
<li class="nav-item">
<a class="nav-link @(ViewBag.IsHomePage == true ? "text-white" : "text-dark")"
asp-area="" asp-controller="Admin" asp-action="Dashboard">
<i class="fas fa-user me-1"></i>Dashboard
</a>
</li>
<li class="nav-item">
<a class="nav-link @(ViewBag.IsHomePage == true ? "text-white" : "text-dark")"
asp-area="" asp-controller="Auth" asp-action="Logout">
<i class="fas fa-sign-out-alt me-1"></i>Sair
</a>
</li>
}
else
{
<li class="nav-item">
<a class="nav-link @(ViewBag.IsHomePage == true ? "text-white" : "text-dark")"
asp-area="" asp-controller="Auth" asp-action="Login">
<i class="fas fa-sign-in-alt me-1"></i>Entrar
</a>
</li>
}
</ul>
</div>
</div>
</nav>
</header>
<div class="container-fluid">
<main role="main">
@if (TempData["Success"] != null)
{
<div class="alert alert-success alert-dismissible fade show" role="alert">
@TempData["Success"]
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
}
@if (TempData["Error"] != null)
{
<div class="alert alert-danger alert-dismissible fade show" role="alert">
@TempData["Error"]
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
}
@if (TempData["Info"] != null)
{
<div class="alert alert-info alert-dismissible fade show" role="alert">
@TempData["Info"]
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
}
@RenderBody()
</main>
</div>
<footer class="border-top footer text-muted">
<div class="container">
<div class="row py-4">
<div class="col-md-6">
&copy; 2024 - BCards - <a asp-area="" asp-controller="Home" asp-action="Privacy">Privacidade</a>
</div>
<div class="col-md-6 text-end">
<small>Crie sua página profissional em minutos</small>
</div>
</div>
</div>
</footer>
<script src="~/lib/jquery/jquery.min.js"></script>
<script src="~/lib/bootstrap/js/bootstrap.bundle.min.js"></script>
<script src="~/js/site.js" asp-append-version="true"></script>
<!-- Scripts para menu ativo e barra de carregamento -->
<script>
$(document).ready(function() {
// Destacar item ativo do menu baseado na URL atual
highlightActiveMenuItem();
// Interceptar cliques em links de navegação
setupLoadingBar();
});
function highlightActiveMenuItem() {
var currentPath = window.location.pathname.toLowerCase();
var currentController = '@ViewContext.RouteData.Values["Controller"]?.ToString()?.ToLower()';
var currentAction = '@ViewContext.RouteData.Values["Action"]?.ToString()?.ToLower()';
// Remover classes ativas existentes
$('.nav-link').removeClass('active');
// Lógica para destacar o item correto
$('.nav-link').each(function() {
var link = $(this);
var href = link.attr('href');
if (href) {
var linkPath = href.toLowerCase();
// Verificar correspondência exata primeiro
if (currentPath === linkPath ||
(currentPath === '/' && linkPath.includes('/home')) ||
(currentController === 'home' && currentAction === 'index' && linkPath.includes('/home')) ||
(currentController === 'home' && currentAction === 'pricing' && linkPath.includes('pricing')) ||
(currentController === 'admin' && linkPath.includes('dashboard')) ||
(currentController === 'moderation' && linkPath.includes('moderation')) ||
(currentController === 'auth' && linkPath.includes('login')) ||
(currentController === 'payment' && linkPath.includes('managesubscription'))) {
link.addClass('active');
}
}
});
}
function setupLoadingBar() {
var loadingBar = $('#loading-bar');
// Interceptar todos os cliques em links que navegam para outras páginas
$('a[href]:not([href^="#"]):not([href^="javascript:"]):not([target="_blank"]):not(.no-loading)').click(function(e) {
var href = $(this).attr('href');
// Ignorar links externos, downloads, ou âncoras
if (href && !href.startsWith('http') && !href.startsWith('mailto:') && !href.startsWith('tel:')) {
showLoadingBar();
}
});
// Interceptar submissão de formulários
$('form:not(.no-loading)').submit(function() {
showLoadingBar();
});
// Ocultar barra quando página carrega
$(window).on('load', function() {
hideLoadingBar();
});
// Ocultar barra em caso de erro ou volta do histórico
$(window).on('pageshow', function() {
hideLoadingBar();
});
}
function showLoadingBar() {
var loadingBar = $('#loading-bar');
loadingBar.addClass('active').css('width', '0%');
// Animação progressiva
setTimeout(function() { loadingBar.css('width', '20%'); }, 100);
setTimeout(function() { loadingBar.css('width', '40%'); }, 300);
setTimeout(function() { loadingBar.css('width', '60%'); }, 600);
setTimeout(function() { loadingBar.css('width', '80%'); }, 1000);
// Adicionar efeito de loading ao conteúdo
$('main').addClass('page-loading');
}
function hideLoadingBar() {
var loadingBar = $('#loading-bar');
loadingBar.css('width', '100%');
setTimeout(function() {
loadingBar.removeClass('active').css('width', '0%');
$('main').removeClass('page-loading');
}, 200);
}
// Fallback para esconder loading se demorar muito (mais de 5 segundos)
setTimeout(function() {
hideLoadingBar();
}, 5000);
</script>
@await RenderSectionAsync("Scripts", required: false)
</body>
</html>