feat: novo menu
This commit is contained in:
parent
f3de10cc4f
commit
c25bf9dc94
@ -1,4 +1,5 @@
|
|||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using OnlyOneAccessTemplate.Models;
|
||||||
using OnlyOneAccessTemplate.Services;
|
using OnlyOneAccessTemplate.Services;
|
||||||
|
|
||||||
namespace OnlyOneAccessTemplate.Controllers
|
namespace OnlyOneAccessTemplate.Controllers
|
||||||
@ -7,10 +8,12 @@ namespace OnlyOneAccessTemplate.Controllers
|
|||||||
public class MenuController : ControllerBase
|
public class MenuController : ControllerBase
|
||||||
{
|
{
|
||||||
private readonly IModuleService _moduleService;
|
private readonly IModuleService _moduleService;
|
||||||
|
private readonly ILogger<MenuController> _logger;
|
||||||
|
|
||||||
public MenuController(IModuleService moduleService)
|
public MenuController(IModuleService moduleService, ILogger<MenuController> logger)
|
||||||
{
|
{
|
||||||
_moduleService = moduleService;
|
_moduleService = moduleService;
|
||||||
|
_logger = logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("converters")]
|
[HttpGet("converters")]
|
||||||
@ -21,31 +24,52 @@ namespace OnlyOneAccessTemplate.Controllers
|
|||||||
var modules = await _moduleService.GetAllActiveModulesAsync();
|
var modules = await _moduleService.GetAllActiveModulesAsync();
|
||||||
|
|
||||||
var menuItems = modules
|
var menuItems = modules
|
||||||
.Where(m => m.ShowInMenu && m.IsActive && m.IsHealthy)
|
.Where(m => m.ShowInMenu && m.IsActive)
|
||||||
.OrderBy(m => m.MenuOrder)
|
.OrderBy(m => m.MenuOrder)
|
||||||
.ThenBy(m => m.MenuTitle)
|
.ThenBy(m => m.MenuTitle)
|
||||||
.GroupBy(m => m.MenuCategory)
|
.GroupBy(m => m.MenuCategory ?? "Conversores")
|
||||||
.Select(g => new
|
.Select(g => new
|
||||||
{
|
{
|
||||||
category = g.Key,
|
category = g.Key,
|
||||||
items = g.Select(m => new
|
items = g.Select(m => new
|
||||||
{
|
{
|
||||||
moduleId = m.ModuleId,
|
moduleId = m.ModuleId,
|
||||||
title = m.SeoTitles.ContainsKey(language) ? m.SeoTitles[language] : m.MenuTitle,
|
title = GetLocalizedTitle(m, language),
|
||||||
description = m.SeoDescriptions.ContainsKey(language) ? m.SeoDescriptions[language] : m.MenuDescription,
|
description = GetLocalizedDescription(m, language),
|
||||||
icon = m.MenuIcon,
|
icon = m.MenuIcon ?? "fas fa-exchange-alt",
|
||||||
url = $"/{language}/{m.RequestBy}",
|
url = $"/{language}/{m.RequestBy}",
|
||||||
order = m.MenuOrder,
|
order = m.MenuOrder,
|
||||||
isNew = m.CreatedAt > DateTime.UtcNow.AddDays(-7) // Novos nos últimos 7 dias
|
isNew = m.CreatedAt > DateTime.UtcNow.AddDays(-7),
|
||||||
|
isHealthy = m.IsHealthy,
|
||||||
|
version = m.Version
|
||||||
}).ToList()
|
}).ToList()
|
||||||
}).ToList();
|
})
|
||||||
|
.OrderBy(g => g.category)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
return Ok(new { success = true, menu = menuItems });
|
return Ok(new { success = true, menu = menuItems });
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
_logger.LogError(ex, "Erro ao buscar menu de conversores");
|
||||||
return StatusCode(500, new { success = false, message = ex.Message });
|
return StatusCode(500, new { success = false, message = ex.Message });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private string GetLocalizedTitle(ModuleConfig module, string language)
|
||||||
|
{
|
||||||
|
if (module.SeoTitles?.ContainsKey(language) == true)
|
||||||
|
return module.SeoTitles[language];
|
||||||
|
|
||||||
|
return module.MenuTitle ?? module.Name ?? "Conversor";
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetLocalizedDescription(ModuleConfig module, string language)
|
||||||
|
{
|
||||||
|
if (module.SeoDescriptions?.ContainsKey(language) == true)
|
||||||
|
return module.SeoDescriptions[language];
|
||||||
|
|
||||||
|
return module.MenuDescription ?? "Ferramenta de conversão";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -4,359 +4,380 @@
|
|||||||
Layout = "~/Views/Shared/_Layout.cshtml";
|
Layout = "~/Views/Shared/_Layout.cshtml";
|
||||||
}
|
}
|
||||||
|
|
||||||
<!-- Google AdSense - Script Global -->
|
|
||||||
@section Head {
|
@section Head {
|
||||||
|
<!-- Google AdSense Script -->
|
||||||
@if (ViewBag.GoogleAdsEnabled == true && !string.IsNullOrEmpty(ViewBag.GoogleAdsPublisher))
|
@if (ViewBag.GoogleAdsEnabled == true && !string.IsNullOrEmpty(ViewBag.GoogleAdsPublisher))
|
||||||
{
|
{
|
||||||
<script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=@ViewBag.GoogleAdsPublisher"
|
<script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=@ViewBag.GoogleAdsPublisher"
|
||||||
crossorigin="anonymous"></script>
|
crossorigin="anonymous"></script>
|
||||||
}
|
}
|
||||||
|
|
||||||
<style>
|
|
||||||
.ad-container { margin: 15px 0; text-align: center; min-height: 50px; }
|
|
||||||
.ad-banner { min-height: 90px; }
|
|
||||||
.ad-rectangle { min-height: 250px; }
|
|
||||||
.ad-sidebar { min-height: 600px; }
|
|
||||||
.ad-sticky { position: sticky; top: 20px; z-index: 100; }
|
|
||||||
.converter-section { background: #f8f9fa; }
|
|
||||||
|
|
||||||
@@media (max-width: 768px) {
|
|
||||||
.ad-sidebar { display: none !important; }
|
|
||||||
.ad-rectangle { min-height: 200px; }
|
|
||||||
.main-content { padding: 0 10px; }
|
|
||||||
}
|
|
||||||
|
|
||||||
@@media (min-width: 1200px) {
|
|
||||||
.ad-sidebar { min-height: 600px; }
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
}
|
}
|
||||||
|
|
||||||
<!-- Banner Superior -->
|
<!-- Hero Section -->
|
||||||
@{
|
<section class="hero-section">
|
||||||
ViewBag.AdPosition = "banner-top";
|
<div class="container">
|
||||||
ViewBag.AdSlotId = ViewBag.AdSlots?.BannerTop ?? "1234567890";
|
<div class="hero-content text-center">
|
||||||
ViewBag.AdFormat = "auto";
|
<h1 class="hero-title">
|
||||||
ViewBag.AdCssClass = "ad-container ad-banner";
|
@(ViewBag.MainTitle ?? "FERRAMENTAS DE CONVERSÃO")
|
||||||
ViewBag.ShowOnMobile = true;
|
</h1>
|
||||||
ViewBag.ShowOnDesktop = true;
|
<p class="hero-subtitle">
|
||||||
}
|
@(ViewBag.MainDescription ?? "Converta seus arquivos de forma rápida e segura")
|
||||||
@await Html.PartialAsync("_AdUnit")
|
</p>
|
||||||
|
|
||||||
<div class="container-fluid">
|
<div class="d-flex flex-wrap justify-content-center gap-2 mb-4">
|
||||||
|
<span class="feature-badge">
|
||||||
|
<i class="fas fa-code me-2"></i> Standalone
|
||||||
|
</span>
|
||||||
|
<span class="feature-badge">
|
||||||
|
<i class="fas fa-globe me-2"></i> Multi-idioma
|
||||||
|
</span>
|
||||||
|
<span class="feature-badge">
|
||||||
|
<i class="fas fa-bolt me-2"></i> API Ready
|
||||||
|
</span>
|
||||||
|
<span class="feature-badge">
|
||||||
|
<i class="fas fa-shield-alt me-2"></i> Seguro
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- Banner Superior Ads -->
|
||||||
|
<div class="container-fluid mt-3">
|
||||||
|
<div class="ad-placeholder banner">
|
||||||
|
<i class="fas fa-rectangle-ad"></i>
|
||||||
|
<div>
|
||||||
|
<strong>Anúncio Banner Superior</strong><br>
|
||||||
|
<small>728x90 ou responsivo</small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Layout Principal -->
|
||||||
|
<div class="container-fluid main-layout">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<!-- Sidebar Esquerda com Anúncios -->
|
<!-- Sidebar Esquerda - Anúncios -->
|
||||||
<div class="col-xl-2 col-lg-2 d-none d-lg-block">
|
<div class="col-xl-2 col-lg-2 d-none d-lg-block">
|
||||||
@{
|
<div class="ad-placeholder sidebar">
|
||||||
ViewBag.AdPosition = "sidebar-left";
|
<i class="fas fa-rectangle-ad"></i>
|
||||||
ViewBag.AdSlotId = ViewBag.AdSlots?.SidebarLeft ?? "2345678901";
|
<div>
|
||||||
ViewBag.AdFormat = "vertical";
|
<strong>Anúncio Vertical</strong><br>
|
||||||
ViewBag.AdSize = "; width: 300px; height: 600px;";
|
<small>160x600 ou 300x600</small>
|
||||||
ViewBag.AdCssClass = "ad-container ad-sidebar";
|
</div>
|
||||||
ViewBag.IsSticky = true;
|
</div>
|
||||||
ViewBag.ShowOnMobile = false;
|
|
||||||
ViewBag.ShowOnDesktop = true;
|
|
||||||
}
|
|
||||||
@await Html.PartialAsync("_AdUnit")
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Conteúdo Principal -->
|
<!-- Menu de Módulos -->
|
||||||
<div class="col-xl-8 col-lg-8 col-md-12 main-content">
|
<div class="col-xl-3 col-lg-3 col-md-4">
|
||||||
<!-- Seção Principal do Conversor -->
|
<div class="modules-sidebar">
|
||||||
<section class="converter-section py-4">
|
<h5 class="fw-bold mb-3 d-flex align-items-center">
|
||||||
<div class="container">
|
<i class="fas fa-puzzle-piece text-primary me-2"></i>
|
||||||
<div class="row justify-content-center">
|
Conversores Disponíveis
|
||||||
<div class="col-lg-11">
|
</h5>
|
||||||
<!-- Título e Descrição -->
|
|
||||||
<div class="text-center mb-4">
|
|
||||||
<h1 class="display-4 fw-bold text-gradient mb-3">
|
|
||||||
@(ViewBag.ConverterTitle ?? "CONVERSOR ONLINE")
|
|
||||||
</h1>
|
|
||||||
<p class="lead text-green-light mb-4">
|
|
||||||
@(ViewBag.ConverterDescription ?? "Converta seus arquivos de forma rápida e segura")
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Anúncio Retangular Antes do Conversor -->
|
<div id="modules-list" class="modules-list">
|
||||||
@{
|
<!-- Loading state -->
|
||||||
ViewBag.AdPosition = "rectangle-pre-converter";
|
<div class="loading-shimmer rounded p-3 mb-2" style="height: 80px;"></div>
|
||||||
ViewBag.AdSlotId = ViewBag.AdSlots?.RectanglePre ?? "3456789012";
|
<div class="loading-shimmer rounded p-3 mb-2" style="height: 80px;"></div>
|
||||||
ViewBag.AdFormat = "rectangle";
|
<div class="loading-shimmer rounded p-3 mb-2" style="height: 80px;"></div>
|
||||||
ViewBag.AdSize = "; width: 300px; height: 250px;";
|
|
||||||
ViewBag.AdCssClass = "ad-container ad-rectangle mb-4";
|
|
||||||
ViewBag.IsSticky = false;
|
|
||||||
ViewBag.ShowOnMobile = true;
|
|
||||||
ViewBag.ShowOnDesktop = true;
|
|
||||||
}
|
|
||||||
@await Html.PartialAsync("_AdUnit")
|
|
||||||
|
|
||||||
<!-- Card do Conversor -->
|
|
||||||
<div class="card shadow-lg border-0 rounded-3">
|
|
||||||
<div class="card-body p-4">
|
|
||||||
<!-- Steps do Processo -->
|
|
||||||
<div class="row text-center mb-4">
|
|
||||||
<div class="col-md-4 mb-3">
|
|
||||||
<div class="step-indicator">
|
|
||||||
<span class="badge bg-gradient-green rounded-circle p-3 fs-5 mb-2 text-white">1</span>
|
|
||||||
<h6 class="fw-bold text-green-light">@(ViewBag.Step1Title ?? "Digite")</h6>
|
|
||||||
<small class="text-muted">@(ViewBag.Step1Description ?? "Digite seu texto")</small>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-4 mb-3">
|
|
||||||
<div class="step-indicator">
|
|
||||||
<span class="badge bg-gradient-green rounded-circle p-3 fs-5 mb-2 text-white">2</span>
|
|
||||||
<h6 class="fw-bold text-green-light">@(ViewBag.Step2Title ?? "Converter")</h6>
|
|
||||||
<small class="text-muted">@(ViewBag.Step2Description ?? "Clique para converter")</small>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-4 mb-3">
|
|
||||||
<div class="step-indicator">
|
|
||||||
<span class="badge bg-gradient-green rounded-circle p-3 fs-5 mb-2 text-white">3</span>
|
|
||||||
<h6 class="fw-bold text-green-light">@(ViewBag.Step3Title ?? "Copiar")</h6>
|
|
||||||
<small class="text-muted">@(ViewBag.Step3Description ?? "Copie o resultado")</small>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Área do Conversor -->
|
|
||||||
<div id="converter-container">
|
|
||||||
@await Html.PartialAsync("_ConverterWidget")
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Informações Adicionais -->
|
|
||||||
<div class="row mt-4">
|
|
||||||
<div class="col-md-6">
|
|
||||||
<div class="d-flex align-items-center mb-2">
|
|
||||||
<i class="fas fa-shield-alt text-success me-2"></i>
|
|
||||||
<small class="text-green-light fw-bold">@(ViewBag.SecurityText ?? "Seus dados estão seguros")</small>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-6">
|
|
||||||
<div class="d-flex align-items-center mb-2">
|
|
||||||
<i class="fas fa-bolt text-success me-2"></i>
|
|
||||||
<small class="text-green-light fw-bold">@(ViewBag.FileInfoText ?? "Processamento rápido e seguro")</small>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Anúncio Retangular Após o Conversor -->
|
|
||||||
@{
|
|
||||||
ViewBag.AdPosition = "rectangle-post-converter";
|
|
||||||
ViewBag.AdSlotId = ViewBag.AdSlots?.RectanglePost ?? "4567890123";
|
|
||||||
ViewBag.AdFormat = "rectangle";
|
|
||||||
ViewBag.AdSize = "; width: 300px; height: 250px;";
|
|
||||||
ViewBag.AdCssClass = "ad-container ad-rectangle mt-4";
|
|
||||||
ViewBag.IsSticky = false;
|
|
||||||
ViewBag.ShowOnMobile = true;
|
|
||||||
ViewBag.ShowOnDesktop = true;
|
|
||||||
}
|
|
||||||
@await Html.PartialAsync("_AdUnit")
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</section>
|
|
||||||
|
|
||||||
<!-- Anúncio In-Feed Entre Seções -->
|
<div class="text-center mt-3">
|
||||||
@{
|
<button class="btn btn-outline-primary btn-sm" onclick="refreshModulesList()">
|
||||||
ViewBag.AdPosition = "in-feed";
|
<i class="fas fa-sync me-1"></i> Atualizar
|
||||||
ViewBag.AdSlotId = ViewBag.AdSlots?.InFeed ?? "5678901234";
|
</button>
|
||||||
ViewBag.AdFormat = "fluid";
|
|
||||||
ViewBag.AdCssClass = "ad-container";
|
|
||||||
ViewBag.IsSticky = false;
|
|
||||||
ViewBag.ShowOnMobile = true;
|
|
||||||
ViewBag.ShowOnDesktop = true;
|
|
||||||
}
|
|
||||||
@await Html.PartialAsync("_AdUnit")
|
|
||||||
|
|
||||||
<!-- Seção de Benefícios -->
|
|
||||||
<section class="benefits-section py-5">
|
|
||||||
<div class="container">
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-lg-10 mx-auto text-center mb-5">
|
|
||||||
<h2 class="h3 fw-bold mb-3 text-gradient">@(ViewBag.BenefitsTitle ?? "Por Que Usar Nossa Ferramenta?")</h2>
|
|
||||||
<p class="text-green-light">@(ViewBag.BenefitsSubtitle ?? "Descubra os benefícios de nossa solução")</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="row g-4 justify-content-center">
|
|
||||||
<!-- Features (código existente) -->
|
|
||||||
<div class="col-md-6 col-lg-3">
|
|
||||||
<div class="text-center p-3">
|
|
||||||
<div class="feature-icon mb-3">
|
|
||||||
<i class="fas fa-rocket fa-2x text-success"></i>
|
|
||||||
</div>
|
|
||||||
<h6 class="fw-bold text-green-light">@(ViewBag.Feature1Title ?? "Rápido e Fácil")</h6>
|
|
||||||
<small class="text-muted">@(ViewBag.Feature1Description ?? "Conversão instantânea")</small>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-6 col-lg-3">
|
|
||||||
<div class="text-center p-3">
|
|
||||||
<div class="feature-icon mb-3">
|
|
||||||
<i class="fas fa-shield-alt fa-2x text-success"></i>
|
|
||||||
</div>
|
|
||||||
<h6 class="fw-bold text-green-light">@(ViewBag.Feature2Title ?? "Seguro")</h6>
|
|
||||||
<small class="text-muted">@(ViewBag.Feature2Description ?? "Dados protegidos")</small>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-6 col-lg-3">
|
|
||||||
<div class="text-center p-3">
|
|
||||||
<div class="feature-icon mb-3">
|
|
||||||
<i class="fas fa-users fa-2x text-success"></i>
|
|
||||||
</div>
|
|
||||||
<h6 class="fw-bold text-green-light">@(ViewBag.Feature3Title ?? "Confiável")</h6>
|
|
||||||
<small class="text-muted">@(ViewBag.Feature3Description ?? "Resultados precisos")</small>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-6 col-lg-3">
|
|
||||||
<div class="text-center p-3">
|
|
||||||
<div class="feature-icon mb-3">
|
|
||||||
<i class="fas fa-clock fa-2x text-success"></i>
|
|
||||||
</div>
|
|
||||||
<h6 class="fw-bold text-green-light">Rápido</h6>
|
|
||||||
<small class="text-muted">Conversão em segundos</small>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Anúncio Multiplex -->
|
<!-- Área Principal do Conversor -->
|
||||||
@{
|
<div class="col-xl-5 col-lg-5 col-md-8">
|
||||||
ViewBag.AdPosition = "multiplex";
|
<!-- Anúncio Retangular Pré-Conversor -->
|
||||||
ViewBag.AdSlotId = ViewBag.AdSlots?.Multiplex ?? "6789012345";
|
<div class="ad-placeholder rectangle">
|
||||||
ViewBag.AdFormat = "autorelaxed";
|
<i class="fas fa-rectangle-ad"></i>
|
||||||
ViewBag.AdCssClass = "ad-container";
|
<div>
|
||||||
ViewBag.IsSticky = false;
|
<strong>Anúncio Retangular</strong><br>
|
||||||
ViewBag.ShowOnMobile = true;
|
<small>300x250</small>
|
||||||
ViewBag.ShowOnDesktop = true;
|
|
||||||
}
|
|
||||||
@await Html.PartialAsync("_AdUnit")
|
|
||||||
|
|
||||||
<!-- Seção CTA Final -->
|
|
||||||
@*
|
|
||||||
<section class="final-cta py-5">
|
|
||||||
<div class="container">
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-lg-8 mx-auto text-center">
|
|
||||||
<h2 class="h3 fw-bold mb-3 text-gradient">@(ViewBag.FinalCtaTitle ?? "Pronto para Converter?")</h2>
|
|
||||||
<p class="text-green-light mb-4">@(ViewBag.FinalCtaSubtitle ?? "Use nossa ferramenta gratuita agora mesmo")</p>
|
|
||||||
<a href="#converter-container" class="btn btn-primary btn-lg hover-lift">
|
|
||||||
@(ViewBag.FinalCtaButtonText ?? "Começar Conversão")
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</section>
|
|
||||||
|
|
||||||
*@
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Sidebar Direita com Anúncios -->
|
<!-- Card do Conversor -->
|
||||||
<div class="col-xl-2 col-lg-2 d-none d-lg-block">
|
<div class="converter-area fade-in">
|
||||||
@{
|
<div class="converter-header">
|
||||||
ViewBag.AdPosition = "sidebar-right";
|
<h2 class="h3 mb-3">
|
||||||
ViewBag.AdSlotId = ViewBag.AdSlots?.SidebarRight ?? "7890123456";
|
<i class="fas fa-exchange-alt me-2"></i>
|
||||||
ViewBag.AdFormat = "vertical";
|
<span id="converter-title">Selecione um Conversor</span>
|
||||||
ViewBag.AdSize = "; width: 300px; height: 600px;";
|
</h2>
|
||||||
ViewBag.AdCssClass = "ad-container ad-sidebar";
|
<p class="mb-0" id="converter-description">
|
||||||
ViewBag.IsSticky = true;
|
Escolha uma ferramenta de conversão no menu ao lado
|
||||||
ViewBag.ShowOnMobile = false;
|
</p>
|
||||||
ViewBag.ShowOnDesktop = true;
|
</div>
|
||||||
}
|
|
||||||
@await Html.PartialAsync("_AdUnit")
|
|
||||||
|
|
||||||
<!-- Anúncio Quadrado Adicional na Sidebar -->
|
<!-- Steps do Processo -->
|
||||||
<div class="mt-4">
|
<div class="step-indicators">
|
||||||
@{
|
<div class="step-indicator">
|
||||||
ViewBag.AdPosition = "sidebar-square";
|
<div class="step-number">1</div>
|
||||||
ViewBag.AdSlotId = ViewBag.AdSlots?.SidebarSquare ?? "8901234567";
|
<h6 class="step-title">Entrada</h6>
|
||||||
ViewBag.AdFormat = "square";
|
<p class="step-description">Selecione ou cole seu conteúdo</p>
|
||||||
ViewBag.AdSize = "; width: 250px; height: 250px;";
|
</div>
|
||||||
ViewBag.AdCssClass = "ad-container";
|
<div class="step-indicator">
|
||||||
ViewBag.IsSticky = false;
|
<div class="step-number">2</div>
|
||||||
ViewBag.ShowOnMobile = false;
|
<h6 class="step-title">Processar</h6>
|
||||||
ViewBag.ShowOnDesktop = true;
|
<p class="step-description">Aguarde o processamento</p>
|
||||||
}
|
</div>
|
||||||
@await Html.PartialAsync("_AdUnit")
|
<div class="step-indicator">
|
||||||
|
<div class="step-number">3</div>
|
||||||
|
<h6 class="step-title">Resultado</h6>
|
||||||
|
<p class="step-description">Baixe ou copie o resultado</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Área do Módulo -->
|
||||||
|
<div id="converter-container" class="p-4">
|
||||||
|
<div class="text-center py-5">
|
||||||
|
<i class="fas fa-arrow-left fa-2x text-muted mb-3"></i>
|
||||||
|
<h5 class="text-muted">Selecione um conversor</h5>
|
||||||
|
<p class="text-muted">Escolha uma ferramenta no menu ao lado para começar</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Anúncio Retangular Pós-Conversor -->
|
||||||
|
<div class="ad-placeholder rectangle">
|
||||||
|
<i class="fas fa-rectangle-ad"></i>
|
||||||
|
<div>
|
||||||
|
<strong>Anúncio Pós-Conversão</strong><br>
|
||||||
|
<small>300x250</small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Sidebar Direita - Anúncios -->
|
||||||
|
<div class="col-xl-2 col-lg-2 d-none d-lg-block">
|
||||||
|
<div class="ad-placeholder sidebar">
|
||||||
|
<i class="fas fa-rectangle-ad"></i>
|
||||||
|
<div>
|
||||||
|
<strong>Anúncio Vertical</strong><br>
|
||||||
|
<small>160x600 ou 300x600</small>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Banner Inferior Mobile -->
|
<!-- Anúncio In-Feed -->
|
||||||
@{
|
<div class="container my-4">
|
||||||
ViewBag.AdPosition = "mobile-bottom";
|
<div class="ad-placeholder" style="min-height: 120px;">
|
||||||
ViewBag.AdSlotId = ViewBag.AdSlots?.MobileBottom ?? "9012345678";
|
<i class="fas fa-rectangle-ad"></i>
|
||||||
ViewBag.AdFormat = "banner";
|
<div>
|
||||||
ViewBag.AdSize = "; width: 320px; height: 50px;";
|
<strong>Anúncio In-Feed</strong><br>
|
||||||
ViewBag.AdCssClass = "fixed-bottom d-block d-md-none";
|
<small>Responsivo ou 728x90</small>
|
||||||
ViewBag.IsSticky = false;
|
</div>
|
||||||
ViewBag.ShowOnMobile = true;
|
</div>
|
||||||
ViewBag.ShowOnDesktop = false;
|
</div>
|
||||||
}
|
|
||||||
<div class="@ViewBag.AdCssClass bg-white border-top p-2" style="z-index: 1050;" id="mobile-bottom-ad">
|
<!-- Seção de Benefícios -->
|
||||||
<div class="d-flex justify-content-between align-items-center">
|
<div class="container">
|
||||||
@await Html.PartialAsync("_AdUnit")
|
<section class="benefits-section">
|
||||||
<button type="button" class="btn-close ms-2" onclick="document.getElementById('mobile-bottom-ad').style.display='none'"></button>
|
<div class="text-center mb-5">
|
||||||
|
<h2 class="h3 fw-bold mb-3">Por Que Usar Nossas Ferramentas?</h2>
|
||||||
|
<p class="text-muted">Descubra os benefícios de nossas soluções</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="feature-grid">
|
||||||
|
<div class="feature-item">
|
||||||
|
<div class="feature-icon">
|
||||||
|
<i class="fas fa-rocket"></i>
|
||||||
|
</div>
|
||||||
|
<h6 class="fw-bold mb-2">Rápido e Fácil</h6>
|
||||||
|
<p class="text-muted small mb-0">Conversão instantânea com poucos cliques</p>
|
||||||
|
</div>
|
||||||
|
<div class="feature-item">
|
||||||
|
<div class="feature-icon">
|
||||||
|
<i class="fas fa-shield-alt"></i>
|
||||||
|
</div>
|
||||||
|
<h6 class="fw-bold mb-2">Seguro</h6>
|
||||||
|
<p class="text-muted small mb-0">Seus dados estão protegidos</p>
|
||||||
|
</div>
|
||||||
|
<div class="feature-item">
|
||||||
|
<div class="feature-icon">
|
||||||
|
<i class="fas fa-users"></i>
|
||||||
|
</div>
|
||||||
|
<h6 class="fw-bold mb-2">Confiável</h6>
|
||||||
|
<p class="text-muted small mb-0">Milhares de usuários satisfeitos</p>
|
||||||
|
</div>
|
||||||
|
<div class="feature-item">
|
||||||
|
<div class="feature-icon">
|
||||||
|
<i class="fas fa-clock"></i>
|
||||||
|
</div>
|
||||||
|
<h6 class="fw-bold mb-2">24/7 Disponível</h6>
|
||||||
|
<p class="text-muted small mb-0">Acesso a qualquer hora</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Anúncio Multiplex -->
|
||||||
|
<div class="container my-4">
|
||||||
|
<div class="ad-placeholder" style="min-height: 200px;">
|
||||||
|
<i class="fas fa-th"></i>
|
||||||
|
<div>
|
||||||
|
<strong>Anúncio Multiplex</strong><br>
|
||||||
|
<small>Formato flexível</small>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Scripts específicos -->
|
|
||||||
@section Scripts {
|
@section Scripts {
|
||||||
<script src="~/js/converter.js"></script>
|
<script src="~/js/module-loader.js"></script>
|
||||||
@{
|
|
||||||
var converterType = ViewBag.ConverterType ?? "generic";
|
|
||||||
var jsFileName = $"~/js/converters/{converterType.ToString().ToLower()}-converter.js";
|
|
||||||
}
|
|
||||||
<script src="@jsFileName"></script>
|
|
||||||
|
|
||||||
<!-- Inicialização dos Anúncios -->
|
|
||||||
<script>
|
<script>
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
let currentModuleId = null;
|
||||||
// Inicializar conversor
|
|
||||||
if (typeof initializeConverter === 'function') {
|
|
||||||
initializeConverter();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Inicializar anúncios do Google
|
document.addEventListener('DOMContentLoaded', function () {
|
||||||
@if (ViewBag.GoogleAdsEnabled == true)
|
loadModulesList();
|
||||||
{
|
|
||||||
@Html.Raw("initializeGoogleAds();")
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
function initializeGoogleAds() {
|
// Auto-carregar primeiro módulo se não houver seleção
|
||||||
try {
|
|
||||||
// Encontrar todos os anúncios na página
|
|
||||||
const adElements = document.querySelectorAll('.adsbygoogle');
|
|
||||||
|
|
||||||
// Inicializar cada anúncio
|
|
||||||
adElements.forEach(ad => {
|
|
||||||
if (!ad.dataset.adsbygoogleStatus) {
|
|
||||||
(adsbygoogle = window.adsbygoogle || []).push({});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
console.log(`Initialized ${adElements.length} ad units`);
|
|
||||||
} catch (e) {
|
|
||||||
console.log('AdSense initialization error:', e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Refresh anúncios após conversão bem-sucedida
|
|
||||||
function refreshAdsAfterConversion() {
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
try {
|
const firstModule = document.querySelector('.module-item');
|
||||||
const adElements = document.querySelectorAll('.adsbygoogle[data-ad-position="rectangle-post-converter"]');
|
if (firstModule && !currentModuleId) {
|
||||||
adElements.forEach(ad => {
|
firstModule.click();
|
||||||
(adsbygoogle = window.adsbygoogle || []).push({});
|
|
||||||
});
|
|
||||||
} catch (e) {
|
|
||||||
console.log('Ad refresh error:', e);
|
|
||||||
}
|
}
|
||||||
}, 2000);
|
}, 2000);
|
||||||
|
});
|
||||||
|
|
||||||
|
async function loadModulesList() {
|
||||||
|
try {
|
||||||
|
console.log('🔄 Carregando lista de módulos...');
|
||||||
|
const response = await fetch('/api/menu/converters?language=pt');
|
||||||
|
const data = await response.json();
|
||||||
|
|
||||||
|
if (data.success && data.menu) {
|
||||||
|
renderModulesList(data.menu);
|
||||||
|
} else {
|
||||||
|
showModulesError('Não foi possível carregar os módulos');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('❌ Erro ao carregar módulos:', error);
|
||||||
|
showModulesError('Erro de conexão');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function renderModulesList(menuData) {
|
||||||
|
const container = document.getElementById('modules-list');
|
||||||
|
let html = '';
|
||||||
|
|
||||||
|
menuData.forEach(category => {
|
||||||
|
html += `<div class="category-header">${category.category}</div>`;
|
||||||
|
|
||||||
|
category.items.forEach(item => {
|
||||||
|
const isNew = item.isNew ? '<span class="badge bg-warning badge-sm ms-1">Novo</span>' : '';
|
||||||
|
|
||||||
|
html += `
|
||||||
|
<a href="#" class="module-item" data-module-id="${item.moduleId}" onclick="loadModule('${item.moduleId}', '${item.title}', '${item.description}')">
|
||||||
|
<div class="module-icon">
|
||||||
|
<i class="${item.icon || 'fas fa-exchange-alt'}"></i>
|
||||||
|
</div>
|
||||||
|
<div class="module-title">${item.title}${isNew}</div>
|
||||||
|
<div class="module-description">${item.description || 'Ferramenta de conversão'}</div>
|
||||||
|
</a>
|
||||||
|
`;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
if (html === '') {
|
||||||
|
html = `
|
||||||
|
<div class="text-center py-4 text-muted">
|
||||||
|
<i class="fas fa-puzzle-piece fa-2x mb-3"></i>
|
||||||
|
<p>Nenhum módulo disponível</p>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
container.innerHTML = html;
|
||||||
|
console.log('✅ Lista de módulos carregada');
|
||||||
|
}
|
||||||
|
|
||||||
|
function showModulesError(message) {
|
||||||
|
const container = document.getElementById('modules-list');
|
||||||
|
container.innerHTML = `
|
||||||
|
<div class="text-center py-4 text-danger">
|
||||||
|
<i class="fas fa-exclamation-triangle fa-2x mb-3"></i>
|
||||||
|
<p>${message}</p>
|
||||||
|
<button class="btn btn-outline-primary btn-sm" onclick="loadModulesList()">
|
||||||
|
Tentar Novamente
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadModule(moduleId, title, description) {
|
||||||
|
if (currentModuleId === moduleId) return;
|
||||||
|
|
||||||
|
// Atualizar UI
|
||||||
|
document.querySelectorAll('.module-item').forEach(item => {
|
||||||
|
item.classList.remove('active');
|
||||||
|
});
|
||||||
|
document.querySelector(`[data-module-id="${moduleId}"]`).classList.add('active');
|
||||||
|
|
||||||
|
// Atualizar título
|
||||||
|
document.getElementById('converter-title').textContent = title;
|
||||||
|
document.getElementById('converter-description').textContent = description;
|
||||||
|
|
||||||
|
// Carregar módulo
|
||||||
|
const container = document.getElementById('converter-container');
|
||||||
|
container.innerHTML = `
|
||||||
|
<div class="text-center py-4">
|
||||||
|
<div class="spinner-border text-primary mb-3" role="status">
|
||||||
|
<span class="visually-hidden">Carregando...</span>
|
||||||
|
</div>
|
||||||
|
<p>Carregando ${title}...</p>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const success = await window.ModuleLoader.loadModule(moduleId, 'converter-container', `/modules/${moduleId}`);
|
||||||
|
if (success) {
|
||||||
|
currentModuleId = moduleId;
|
||||||
|
console.log(`✅ Módulo ${moduleId} carregado com sucesso`);
|
||||||
|
|
||||||
|
// Analytics
|
||||||
|
if (typeof gtag !== 'undefined') {
|
||||||
|
gtag('event', 'module_loaded', {
|
||||||
|
'module_id': moduleId,
|
||||||
|
'module_title': title
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new Error('Falha ao carregar módulo');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('❌ Erro ao carregar módulo:', error);
|
||||||
|
container.innerHTML = `
|
||||||
|
<div class="text-center py-4 text-danger">
|
||||||
|
<i class="fas fa-exclamation-triangle fa-2x mb-3"></i>
|
||||||
|
<h6>Erro ao carregar ${title}</h6>
|
||||||
|
<p class="text-muted">${error.message}</p>
|
||||||
|
<button class="btn btn-outline-primary" onclick="loadModule('${moduleId}', '${title}', '${description}')">
|
||||||
|
Tentar Novamente
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function refreshModulesList() {
|
||||||
|
const container = document.getElementById('modules-list');
|
||||||
|
container.innerHTML = `
|
||||||
|
<div class="loading-shimmer rounded p-3 mb-2" style="height: 80px;"></div>
|
||||||
|
<div class="loading-shimmer rounded p-3 mb-2" style="height: 80px;"></div>
|
||||||
|
<div class="loading-shimmer rounded p-3 mb-2" style="height: 80px;"></div>
|
||||||
|
`;
|
||||||
|
loadModulesList();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Event listener para módulos carregados
|
||||||
|
document.addEventListener('moduleLoaded', function (event) {
|
||||||
|
console.log('🎯 Evento moduleLoaded:', event.detail);
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
}
|
}
|
||||||
@ -1,70 +1,91 @@
|
|||||||
<header class="navbar navbar-expand-lg navbar-light bg-white shadow-sm sticky-top">
|
<nav class="navbar navbar-expand-lg navbar-dark bg-primary sticky-top">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<a class="navbar-brand d-flex align-items-center" href="@ViewBag.HomeUrl">
|
<a class="navbar-brand d-flex align-items-center" href="/@ViewBag.Language">
|
||||||
<img src="@ViewBag.LogoUrl" alt="?" height="40" class="me-2">
|
<!-- Usando seu logo existente -->
|
||||||
<span class="fw-bold text-gradient">@ViewBag.SiteName</span>
|
<img src="~/img/logo-white.png" alt="Convert-it" height="32" class="me-2">
|
||||||
|
<strong>Convert-it</strong>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav"
|
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav">
|
||||||
aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
|
|
||||||
<span class="navbar-toggler-icon"></span>
|
<span class="navbar-toggler-icon"></span>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<div class="collapse navbar-collapse" id="navbarNav">
|
<div class="collapse navbar-collapse" id="navbarNav">
|
||||||
<ul class="navbar-nav me-auto">
|
<ul class="navbar-nav me-auto">
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link @(ViewBag.CurrentPage == "home" ? "active" : "")"
|
<a class="nav-link" href="/@ViewBag.Language">
|
||||||
href="@ViewBag.HomeUrl">
|
<i class="fas fa-home me-1"></i>
|
||||||
@ViewBag.MenuHome
|
@(ViewBag.Language == "pt" ? "Início" : ViewBag.Language == "es" ? "Inicio" : "Home")
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
@*
|
|
||||||
<li class="nav-item">
|
|
||||||
<a class="nav-link @(ViewBag.CurrentPage == "about" ? "active" : "")"
|
|
||||||
href="@ViewBag.AboutUrl">
|
|
||||||
@ViewBag.MenuAbout
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li class="nav-item">
|
|
||||||
<a class="nav-link @(ViewBag.CurrentPage == "contact" ? "active" : "")"
|
|
||||||
href="@ViewBag.ContactUrl">
|
|
||||||
@ViewBag.MenuContact
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
|
|
||||||
*@
|
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
|
<!-- Status dos Módulos -->
|
||||||
|
<div class="navbar-nav me-3">
|
||||||
|
<span class="navbar-text d-flex align-items-center">
|
||||||
|
<span class="status-indicator me-2" id="modules-status"></span>
|
||||||
|
<small id="modules-count">Carregando...</small>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Language Switcher -->
|
<!-- Language Switcher -->
|
||||||
<div class="dropdown me-3">
|
<div class="dropdown">
|
||||||
<button class="btn btn-outline-success btn-sm dropdown-toggle" type="button"
|
<button class="btn btn-outline-light dropdown-toggle btn-sm" type="button" data-bs-toggle="dropdown">
|
||||||
id="languageDropdown" data-bs-toggle="dropdown" aria-expanded="false">
|
|
||||||
<i class="fas fa-globe me-1"></i>
|
<i class="fas fa-globe me-1"></i>
|
||||||
@ViewBag.CurrentLanguageDisplay
|
@ViewBag.Language?.ToUpper()
|
||||||
</button>
|
</button>
|
||||||
<ul class="dropdown-menu" aria-labelledby="languageDropdown">
|
<ul class="dropdown-menu dropdown-menu-end">
|
||||||
<li>
|
<li>
|
||||||
<a class="dropdown-item @(ViewBag.Language == "pt" ? "active" : "")"
|
<a class="dropdown-item" href="/pt@(ViewContext.RouteData.Values["converter"] != null ? "/" + ViewContext.RouteData.Values["converter"] : "")">
|
||||||
href="@ViewBag.PtUrl">🇧🇷 Português</a>
|
<i class="flag-icon flag-icon-br me-2"></i>Português
|
||||||
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a class="dropdown-item @(ViewBag.Language == "en" ? "active" : "")"
|
<a class="dropdown-item" href="/en@(ViewContext.RouteData.Values["converter"] != null ? "/" + ViewContext.RouteData.Values["converter"] : "")">
|
||||||
href="@ViewBag.EnUrl">🇺🇸 English</a>
|
<i class="flag-icon flag-icon-us me-2"></i>English
|
||||||
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a class="dropdown-item @(ViewBag.Language == "es" ? "active" : "")"
|
<a class="dropdown-item" href="/es@(ViewContext.RouteData.Values["converter"] != null ? "/" + ViewContext.RouteData.Values["converter"] : "")">
|
||||||
href="@ViewBag.EsUrl">🇪🇸 Español</a>
|
<i class="flag-icon flag-icon-es me-2"></i>Español
|
||||||
|
</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
@*
|
|
||||||
<!-- CTA Button -->
|
|
||||||
<a href="#conversion-form" class="btn btn-primary btn-sm scroll-to-form hover-lift">
|
|
||||||
@ViewBag.CtaButtonText
|
|
||||||
</a>
|
|
||||||
*@
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</nav>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
// Atualizar status dos módulos no header
|
||||||
|
async function updateModulesStatus() {
|
||||||
|
try {
|
||||||
|
const response = await fetch('/api/menu/converters?language=@ViewBag.Language');
|
||||||
|
const data = await response.json();
|
||||||
|
|
||||||
|
const statusIndicator = document.getElementById('modules-status');
|
||||||
|
const countElement = document.getElementById('modules-count');
|
||||||
|
|
||||||
|
if (data.success && data.menu) {
|
||||||
|
const totalModules = data.menu.reduce((total, category) => total + category.items.length, 0);
|
||||||
|
const activeModules = data.menu.reduce((total, category) =>
|
||||||
|
total + category.items.filter(item => item.isHealthy !== false).length, 0);
|
||||||
|
|
||||||
|
statusIndicator.className = `status-indicator me-2 ${activeModules === totalModules ? 'bg-success' : 'bg-warning'}`;
|
||||||
|
statusIndicator.style.width = '8px';
|
||||||
|
statusIndicator.style.height = '8px';
|
||||||
|
statusIndicator.style.borderRadius = '50%';
|
||||||
|
statusIndicator.style.display = 'inline-block';
|
||||||
|
|
||||||
|
countElement.textContent = `${activeModules}/${totalModules} módulos`;
|
||||||
|
} else {
|
||||||
|
statusIndicator.className = 'status-indicator me-2 bg-danger';
|
||||||
|
countElement.textContent = 'Erro ao carregar';
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Erro ao atualizar status:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener('DOMContentLoaded', updateModulesStatus);
|
||||||
|
</script>
|
||||||
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user