fix: ajustes no dashboard
All checks were successful
BCards Deployment Pipeline / Run Tests (push) Successful in 2s
BCards Deployment Pipeline / PR Validation (push) Has been skipped
BCards Deployment Pipeline / Build and Push Image (push) Successful in 15m6s
BCards Deployment Pipeline / Deploy to Production (ARM - OCI) (push) Successful in 1m15s
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 1s

This commit is contained in:
Ricardo Carneiro 2025-08-31 02:34:39 -03:00
parent 6598dbdcdd
commit 7cc8f46a1a

View File

@ -2,6 +2,7 @@
@{ @{
ViewData["Title"] = "Dashboard - BCards"; ViewData["Title"] = "Dashboard - BCards";
Layout = "_Layout"; Layout = "_Layout";
var pageInCreation = Model.UserPages.FirstOrDefault(p => (p.LastModerationStatus ?? p.Status) == BCards.Web.ViewModels.PageStatus.Creating);
} }
<div class="container py-4"> <div class="container py-4">
@ -21,6 +22,27 @@
</div> </div>
</div> </div>
<!-- Acordeão de Instruções -->
@if (pageInCreation != null)
{
<div class="accordion mb-4" id="instructionsAccordion">
<div class="accordion-item border-0" style="background-color: #e9ecef;">
<h2 class="accordion-header" id="instructions-heading">
<button class="accordion-button collapsed fw-bold" type="button" data-bs-toggle="collapse" data-bs-target="#instructions-collapse" aria-expanded="false" aria-controls="instructions-collapse" style="background-color: #e0e5e9;">
<i class="fas fa-info-circle me-2"></i>Página em criação! Veja como continuar
</button>
</h2>
<div id="instructions-collapse" class="accordion-collapse collapse" aria-labelledby="instructions-heading">
<div class="accordion-body">
Você pode editar e testar sua página <b>"@pageInCreation.DisplayName"</b> quantas vezes quiser.
Ao terminar, use os botões no card da página para enviá-la para moderação.
<br><small class="text-muted">Dica: Ao segurar o dedo sobre um botão, uma dica será exibida.</small>
</div>
</div>
</div>
</div>
}
<!-- Lista de Páginas --> <!-- Lista de Páginas -->
<div class="row"> <div class="row">
@foreach (var pageItem in Model.UserPages) @foreach (var pageItem in Model.UserPages)
@ -37,48 +59,30 @@
<i class="fas fa-trash"></i> <i class="fas fa-trash"></i>
</button> </button>
</form> </form>
<input type="hidden" id="displayName_@pageItem.Id" value="@(pageItem.DisplayName)" />
</h6> </h6>
<p class="text-muted small mb-2">@(pageItem.Category)/@(pageItem.Slug)</p> <p class="text-muted small mb-2">@(pageItem.Category)/@(pageItem.Slug)</p>
<div class="mb-2"> <div class="mb-2">
@{ @switch (pageItem.LastModerationStatus ?? pageItem.Status)
var pageStatus = pageItem.Status;
if (pageItem.Status == BCards.Web.ViewModels.PageStatus.Inactive)
{
if (pageItem.LastModerationStatus.HasValue)
{
pageStatus = pageItem.LastModerationStatus.Value;
}
}
}
@switch (pageStatus)
{ {
case BCards.Web.ViewModels.PageStatus.Active: case BCards.Web.ViewModels.PageStatus.Active:
<span class="badge bg-success">Ativa</span> <span class="badge bg-success">Ativa</span>
break; break;
case BCards.Web.ViewModels.PageStatus.Expired:
<span class="badge bg-danger">Expirada</span>
break;
case BCards.Web.ViewModels.PageStatus.PendingPayment:
<span class="badge bg-warning">Pagamento Pendente</span>
break;
case BCards.Web.ViewModels.PageStatus.Inactive:
<span class="badge bg-secondary">Inativa</span>
break;
case BCards.Web.ViewModels.PageStatus.PendingModeration: case BCards.Web.ViewModels.PageStatus.PendingModeration:
<span class="badge bg-warning">Aguardando</span> <span class="badge bg-warning">Aguardando Moderação</span>
break; break;
case BCards.Web.ViewModels.PageStatus.Rejected: case BCards.Web.ViewModels.PageStatus.Rejected:
<span class="badge bg-danger">Rejeitada</span> <span class="badge bg-danger">Rejeitada</span>
break; break;
case BCards.Web.ViewModels.PageStatus.Creating: case BCards.Web.ViewModels.PageStatus.Creating:
<span class="badge bg-info"> <span class="badge bg-info"><i class="fas fa-edit me-1"></i>Em Criação</span>
<i class="fas fa-edit me-1"></i>Criando break;
</span> default:
<span class="badge bg-secondary">@pageItem.Status</span>
break; break;
} }
</div> </div>
@if (Model.CurrentPlan.AllowsAnalytics) @if (Model.CurrentPlan.AllowsAnalytics)
{ {
<div class="row text-center small mb-3"> <div class="row text-center small mb-3">
@ -92,149 +96,49 @@
</div> </div>
</div> </div>
} }
</div> </div>
<!-- Cards com Hover Effect -->
<div class="card-footer" data-page-id="@pageItem.Id" data-status="@pageItem.Status"> <div class="card-footer" data-page-id="@pageItem.Id" data-status="@pageItem.Status">
<div class="d-flex gap-2"> <div class="d-grid gap-2">
@if (pageItem.Status == BCards.Web.ViewModels.PageStatus.Active) @if (pageItem.Status == BCards.Web.ViewModels.PageStatus.Active)
{ {
<a href="/page/@pageItem.Category/@pageItem.Slug" target="_blank" <a href="/page/@pageItem.Category/@pageItem.Slug" target="_blank" class="btn btn-success">
class="btn btn-success flex-fill"> <i class="fas fa-eye me-1"></i>Ver Página Publicada
<i class="fas fa-eye me-1"></i>Ver Página </a>
<a href="@Url.Action("ManagePage", new { id = pageItem.Id })" class="btn btn-primary" data-bs-toggle="tooltip" title="Ir para o editor da página">
<i class="fas fa-edit me-1"></i>Editar
</a> </a>
} }
else if (pageItem.Status == BCards.Web.ViewModels.PageStatus.Creating || else if (pageItem.Status == BCards.Web.ViewModels.PageStatus.Creating || pageItem.Status == BCards.Web.ViewModels.PageStatus.Rejected)
pageItem.Status == BCards.Web.ViewModels.PageStatus.Rejected ||
pageItem.Status == BCards.Web.ViewModels.PageStatus.PendingModeration)
{ {
<button type="button" <button type="button" class="btn btn-outline-info" onclick="openPreview('@pageItem.Id')" data-page-category="@pageItem.Category" data-page-slug="@pageItem.Slug" data-bs-toggle="tooltip" title="Pré-visualizar a página antes da publicação">
class="btn btn-outline-info flex-fill"
onclick="openPreview('@pageItem.Id')"
data-page-category="@pageItem.Category"
data-page-slug="@pageItem.Slug">
<i class="fas fa-vial me-1"></i>Testar Página <i class="fas fa-vial me-1"></i>Testar Página
</button> </button>
} <a href="@Url.Action("ManagePage", new { id = pageItem.Id })" class="btn btn-primary" data-bs-toggle="tooltip" title="Ir para o editor da página">
else <i class="fas fa-edit me-1"></i>Editar
{
<button class="btn btn-secondary flex-fill" disabled>
<i class="fas fa-ban me-1"></i>Indisponível
</button>
}
<div class="btn-group">
<button class="btn btn-outline-secondary dropdown-toggle"
type="button"
id="dropdownMenuButton@(pageItem.Id)"
data-bs-toggle="dropdown"
aria-expanded="false"
title="Mais opções">
<i class="fas fa-cog"></i>
</button>
<ul class="dropdown-menu dropdown-menu-end" aria-labelledby="dropdownMenuButton@(pageItem.Id)">
<!-- Editar - sempre presente -->
@if (pageItem.Status == BCards.Web.ViewModels.PageStatus.PendingModeration)
{
<li>
<span class="dropdown-item disabled">
<i class="fas fa-edit me-2"></i>Editar
</span>
</li>
}
else
{
<li>
<a href="@Url.Action("ManagePage", new { id = pageItem.Id })"
class="dropdown-item">
<i class="fas fa-edit me-2"></i>Editar
</a> </a>
</li> <button type="button" class="btn btn-outline-info" onclick="submitForModeration('@pageItem.Id')" data-page-name="@pageItem.DisplayName" data-bs-toggle="tooltip" title="Enviar a página para revisão e aprovação">
} <i class="fas fa-paper-plane"></i> Enviar para Moderação
@if (pageItem.Status == BCards.Web.ViewModels.PageStatus.Creating ||
pageItem.Status == BCards.Web.ViewModels.PageStatus.Rejected)
{
<li><hr class="dropdown-divider"></li>
<li>
<button type="button"
class="dropdown-item"
onclick="submitForModeration('@pageItem.Id')"
data-page-name="@pageItem.DisplayName">
<i class="fas fa-paper-plane me-2"></i>Enviar para Moderação
</button> </button>
</li>
} }
else if (pageItem.Status == BCards.Web.ViewModels.PageStatus.PendingModeration) else if (pageItem.Status == BCards.Web.ViewModels.PageStatus.PendingModeration)
{ {
<li><hr class="dropdown-divider"></li> <button type="button" class="btn btn-outline-info" onclick="openPreview('@pageItem.Id')" data-page-category="@pageItem.Category" data-page-slug="@pageItem.Slug" data-bs-toggle="tooltip" title="Pré-visualizar a página antes da publicação">
<li> <i class="fas fa-vial me-1"></i>Testar Página
<span class="dropdown-item disabled"> </button>
<i class="fas fa-hourglass-half me-2"></i>Aguardando Moderação <button class="btn btn-secondary" disabled data-bs-toggle="tooltip" title="A página está em revisão e não pode ser editada ou enviada novamente.">
</span> <i class="fas fa-hourglass-half me-1"></i>Aguardando Moderação
</li> </button>
} }
</ul> else
</div>
</div>
<!-- Informações da página movidas para baixo dos botões -->
<div class="px-3 pt-2 pb-1">
<small class="text-muted">Criada em @(pageItem.CreatedAt.ToString("dd/MM/yyyy"))</small>
@if ((pageItem.LastModerationStatus ?? pageItem.Status) == BCards.Web.ViewModels.PageStatus.Rejected && !string.IsNullOrEmpty(pageItem.Motive))
{ {
<div class="alert alert-danger alert-dismissible fade show mt-2 mb-0" role="alert"> <button class="btn btn-secondary" disabled>
<div class="d-flex align-items-start"> <i class="fas fa-ban me-1"></i>Indisponível
<i class="fas fa-exclamation-triangle me-2 mt-1"></i> </button>
<div class="flex-grow-1">
<strong>Motivo da rejeição:</strong><br>
<small>@(pageItem.Motive)</small>
</div>
</div>
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
}
else if (pageItem.LastModerationStatus == BCards.Web.ViewModels.PageStatus.Active && !string.IsNullOrEmpty(pageItem.Motive))
{
<div class="alert alert-info alert-dismissible fade show mt-2 mb-0" role="alert">
<div class="d-flex align-items-start">
<i class="fas fa-exclamation-triangle me-2 mt-1"></i>
<div class="flex-grow-1">
<strong>Motivo:</strong><br>
<small>@(pageItem.Motive)</small>
</div>
</div>
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
} }
</div> </div>
</div> </div>
</div> </div>
</div> </div>
@if ((pageItem.LastModerationStatus ?? pageItem.Status) == BCards.Web.ViewModels.PageStatus.Creating)
{
<div class="col-12">
<div class="alert alert-secondary d-flex align-items-center alert-dismissible alert-permanent fade show">
<i class="fas fa-exclamation-triangle me-3"></i>
<div>
<strong>Página em criação!</strong>
Você pode editar e testar quantas vezes quiser. <br />
Ao terminar, clique em <i class="fas fa-ellipsis-v"></i> para enviar a página <b><span id="pageNameDisplay"></span></b> para moderação!
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
</div>
</div>
<script>
var pageNameDisplay = document.getElementById('pageNameDisplay');
var displayName = document.getElementById('displayName_@pageItem.Id');
pageNameDisplay.innerHTML = displayName.value;
</script>
}
} }
<!-- Card para Criar Nova Página --> <!-- Card para Criar Nova Página -->
@ -246,8 +150,7 @@
<div> <div>
<i class="fas fa-plus fa-2x text-muted mb-3"></i> <i class="fas fa-plus fa-2x text-muted mb-3"></i>
<h6 class="text-muted">Criar Nova Página</h6> <h6 class="text-muted">Criar Nova Página</h6>
<a href="@Url.Action("ManagePage", new { id = "new" })" <a href="@Url.Action("ManagePage", new { id = "new" })" class="btn btn-primary">Começar</a>
class="btn btn-primary">Começar</a>
</div> </div>
</div> </div>
</div> </div>
@ -255,66 +158,55 @@
} }
else if (!Model.UserPages.Any()) else if (!Model.UserPages.Any())
{ {
<!-- Primeira Página -->
<div class="col-12"> <div class="col-12">
<div class="card border-primary"> <div class="card border-primary">
<div class="card-body text-center p-5"> <div class="card-body text-center p-5">
<div class="mb-4"> <div class="mb-4"><i class="display-1 text-primary">🚀</i></div>
<i class="display-1 text-primary">🚀</i>
</div>
<h3>Crie sua primeira página!</h3> <h3>Crie sua primeira página!</h3>
<p class="text-muted mb-4"> <p class="text-muted mb-4">Comece criando sua página profissional personalizada com seus links organizados.</p>
Comece criando sua página profissional personalizada com seus links organizados. <a href="@Url.Action("ManagePage", new { id = "new" })" class="btn btn-primary btn-lg">Criar Minha Página</a>
</p>
<a href="@Url.Action("ManagePage", new { id = "new" })" class="btn btn-primary btn-lg">
Criar Minha Página
</a>
</div> </div>
</div> </div>
</div> </div>
} }
else else
{ {
<!-- Limite atingido -->
<div class="col-12"> <div class="col-12">
<div class="alert alert-warning d-flex align-items-center alert-permanent"> <div class="alert alert-warning d-flex align-items-center alert-permanent">
<i class="fas fa-exclamation-triangle me-3"></i> <i class="fas fa-exclamation-triangle me-3"></i>
<div> <div>
<strong>Limite atingido!</strong> <strong>Limite atingido!</strong> Você já criou o máximo de @Model.CurrentPlan.MaxPages página(s) para seu plano atual.
Você já criou o máximo de @Model.CurrentPlan.MaxPages página(s) para seu plano atual.
<a href="@Url.Action("Pricing", "Home")" class="alert-link ms-2">Fazer upgrade</a> <a href="@Url.Action("Pricing", "Home")" class="alert-link ms-2">Fazer upgrade</a>
</div> </div>
</div> </div>
</div> </div>
} }
</div> </div>
</div> </div>
<div class="col-md-4"> <div class="col-md-4">
<!-- Plano Atual --> <!-- Painel de Plano Atual como Acordeão -->
<div class="accordion" id="planAccordion">
<div class="card mb-4 @(Model.CurrentPlan.Type == BCards.Web.Models.PlanType.Trial ? "border-warning" : "")"> <div class="card mb-4 @(Model.CurrentPlan.Type == BCards.Web.Models.PlanType.Trial ? "border-warning" : "")">
<div class="card-header @(Model.CurrentPlan.Type == BCards.Web.Models.PlanType.Trial ? "bg-warning" : "bg-primary") text-white"> <div class="card-header @(Model.CurrentPlan.Type == BCards.Web.Models.PlanType.Trial ? "bg-warning" : "bg-primary") text-white">
<h6 class="mb-0"> <h2 class="mb-0">
<i class="fas fa-crown me-2"></i> <button class="btn btn-link text-white text-decoration-none w-100 d-flex justify-content-between align-items-center p-0" type="button" data-bs-toggle="collapse" data-bs-target="#plan-collapse" aria-expanded="true" aria-controls="plan-collapse">
Plano Atual <span><i class="fas fa-crown me-2"></i>Plano Atual</span>
</h6> <i id="plan-toggle-icon" class="fas fa-eye-slash"></i>
</button>
</h2>
</div> </div>
<div id="plan-collapse" class="accordion-collapse collapse">
<div class="card-body"> <div class="card-body">
<h5 class="text-capitalize mb-1">@Model.CurrentPlan.Name</h5> <h5 class="text-capitalize mb-1">@Model.CurrentPlan.Name</h5>
@if (Model.CurrentPlan.Type == BCards.Web.Models.PlanType.Trial) @if (Model.CurrentPlan.Type == BCards.Web.Models.PlanType.Trial)
{ {
<p class="text-warning mb-2"> <p class="text-warning mb-2"><i class="fas fa-clock me-1"></i>@Model.DaysRemaining dia(s) restante(s)</p>
<i class="fas fa-clock me-1"></i>
@Model.DaysRemaining dia(s) restante(s)
</p>
} }
else else
{ {
<p class="text-muted small mb-2">R$ @Model.CurrentPlan.Price.ToString("F2")/mês</p> <p class="text-muted small mb-2">R$ @Model.CurrentPlan.Price.ToString("F2")/mês</p>
} }
<div class="mb-3"> <div class="mb-3">
<div class="d-flex justify-content-between small mb-1"> <div class="d-flex justify-content-between small mb-1">
<span>Páginas</span> <span>Páginas</span>
@ -322,54 +214,33 @@
</div> </div>
<div class="progress" style="height: 6px;"> <div class="progress" style="height: 6px;">
@{ @{
var pagesPercentage = Model.CurrentPlan.MaxPages > 0 ? var pagesPercentage = Model.CurrentPlan.MaxPages > 0 ? (double)Model.UserPages.Count / Model.CurrentPlan.MaxPages * 100 : 0;
(double)Model.UserPages.Count / Model.CurrentPlan.MaxPages * 100 : 0;
} }
<div class="progress-bar @(pagesPercentage >= 80 ? "bg-warning" : "bg-primary")" <div class="progress-bar @(pagesPercentage >= 80 ? "bg-warning" : "bg-primary")" style="width: @pagesPercentage%"></div>
style="width: @pagesPercentage%"></div>
</div> </div>
</div> </div>
<div class="small mb-2"><i class="fas fa-link me-2"></i>Links por página: @(Model.CurrentPlan.MaxLinksPerPage == int.MaxValue ? "Ilimitado" : Model.CurrentPlan.MaxLinksPerPage.ToString())
<div class="small mb-2">
<i class="fas fa-link me-2"></i>
Links por página: @(Model.CurrentPlan.MaxLinksPerPage == int.MaxValue ? "Ilimitado" : Model.CurrentPlan.MaxLinksPerPage.ToString())
</div> </div>
<div class="small mb-2"> <div class="small mb-2"><i class="fas fa-chart-bar me-2"></i>Analytics: @(Model.CurrentPlan.AllowsAnalytics ? "✅" : "❌")</div>
<i class="fas fa-chart-bar me-2"></i> <div class="small mb-3"><i class="fas fa-palette me-2"></i>Temas premium: @(Model.CurrentPlan.AllowsCustomThemes ? "✅" : "❌")</div>
Analytics: @(Model.CurrentPlan.AllowsAnalytics ? "✅" : "❌")
</div>
<div class="small mb-3">
<i class="fas fa-palette me-2"></i>
Temas premium: @(Model.CurrentPlan.AllowsCustomThemes ? "✅" : "❌")
</div>
@if (Model.CurrentPlan.Type == BCards.Web.Models.PlanType.Trial) @if (Model.CurrentPlan.Type == BCards.Web.Models.PlanType.Trial)
{ {
<a href="@Url.Action("Pricing", "Home")" class="btn btn-warning w-100"> <a href="@Url.Action("Pricing", "Home")" class="btn btn-warning w-100"><i class="fas fa-rocket me-2"></i>Fazer Upgrade</a>
<i class="fas fa-rocket me-2"></i>
Fazer Upgrade
</a>
} }
else else
{ {
<a href="@Url.Action("ManageSubscription", "Payment")" class="btn btn-outline-secondary w-100"> <a href="@Url.Action("ManageSubscription", "Payment")" class="btn btn-outline-secondary w-100"><i class="fas fa-cog me-2"></i>Gerenciar Assinatura</a>
<i class="fas fa-cog me-2"></i>
Gerenciar Assinatura
</a>
} }
</div> </div>
</div> </div>
</div>
</div>
<!-- Estatísticas Rápidas --> <!-- Estatísticas e Dicas (mantidos como antes) -->
@if (Model.CurrentPlan.AllowsAnalytics && Model.UserPages.Any()) @if (Model.CurrentPlan.AllowsAnalytics && Model.UserPages.Any())
{ {
<div class="card mb-4"> <div class="card mb-4">
<div class="card-header"> <div class="card-header"><h6 class="mb-0"><i class="fas fa-chart-line me-2"></i>Estatísticas Gerais</h6></div>
<h6 class="mb-0">
<i class="fas fa-chart-line me-2"></i>
Estatísticas Gerais
</h6>
</div>
<div class="card-body"> <div class="card-body">
<div class="row text-center"> <div class="row text-center">
<div class="col-6"> <div class="col-6">
@ -385,46 +256,22 @@
{ {
<hr class="my-3"> <hr class="my-3">
<div class="text-center"> <div class="text-center">
<div class="h5 text-info mb-0"> <div class="h5 text-info mb-0">@((Model.UserPages.Sum(p => p.TotalClicks) * 100.0 / Model.UserPages.Sum(p => p.TotalViews)).ToString("F1"))%</div>
@((Model.UserPages.Sum(p => p.TotalClicks) * 100.0 / Model.UserPages.Sum(p => p.TotalViews)).ToString("F1"))%
</div>
<small class="text-muted">Taxa de Cliques</small> <small class="text-muted">Taxa de Cliques</small>
</div> </div>
} }
</div> </div>
</div> </div>
} }
<!-- Dicas -->
<div class="card"> <div class="card">
<div class="card-header"> <div class="card-header"><h6 class="mb-0"><i class="fas fa-lightbulb me-2"></i>💡 Dicas</h6></div>
<h6 class="mb-0">
<i class="fas fa-lightbulb me-2"></i>
💡 Dicas
</h6>
</div>
<div class="card-body"> <div class="card-body">
<ul class="list-unstyled small mb-0"> <ul class="list-unstyled small mb-0">
<li class="mb-2"> <li class="mb-2"><i class="fas fa-check text-success me-2"></i>Use uma bio clara e objetiva</li>
<i class="fas fa-check text-success me-2"></i> <li class="mb-2"><i class="fas fa-check text-success me-2"></i>Organize seus links por importância</li>
Use uma bio clara e objetiva <li class="mb-2"><i class="fas fa-check text-success me-2"></i>Escolha URLs fáceis de lembrar</li>
</li> <li class="mb-2"><i class="fas fa-check text-success me-2"></i>Atualize regularmente seus links</li>
<li class="mb-2"> <li class="mb-0"><i class="fas fa-check text-success me-2"></i>Monitore suas estatísticas</li>
<i class="fas fa-check text-success me-2"></i>
Organize seus links por importância
</li>
<li class="mb-2">
<i class="fas fa-check text-success me-2"></i>
Escolha URLs fáceis de lembrar
</li>
<li class="mb-2">
<i class="fas fa-check text-success me-2"></i>
Atualize regularmente seus links
</li>
<li class="mb-0">
<i class="fas fa-check text-success me-2"></i>
Monitore suas estatísticas
</li>
</ul> </ul>
</div> </div>
</div> </div>
@ -434,122 +281,144 @@
@section Scripts { @section Scripts {
<script> <script>
// Função para abrir preview com token fresh // Funções auxiliares para cookies
function setCookie(name, value, days) {
var expires = "";
if (days) {
var date = new Date();
date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
expires = "; expires=" + date.toUTCString();
}
document.cookie = name + "=" + (value || "") + expires + "; path=/";
}
function getCookie(name) {
var nameEQ = name + "=";
var ca = document.cookie.split(';');
for (var i = 0; i < ca.length; i++) {
var c = ca[i];
while (c.charAt(0) == ' ') c = c.substring(1, c.length);
if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length, c.length);
}
return null;
}
document.addEventListener('DOMContentLoaded', function () {
// Inicializar tooltips
var tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]'));
var tooltipList = tooltipTriggerList.map(function (tooltipTriggerEl) {
return new bootstrap.Tooltip(tooltipTriggerEl);
});
// Lógica para o acordeão de instruções
const instructionsAccordion = document.getElementById('instructions-collapse');
if (instructionsAccordion) {
if (getCookie('instructions_collapsed') === 'true') {
// Já começa fechado pelo `collapsed` no botão, então não fazemos nada
} else {
// Se não tem cookie ou está aberto, abre
new bootstrap.Collapse(instructionsAccordion, { toggle: true });
}
instructionsAccordion.addEventListener('show.bs.collapse', function () {
setCookie('instructions_collapsed', 'false', 365);
});
instructionsAccordion.addEventListener('hide.bs.collapse', function () {
setCookie('instructions_collapsed', 'true', 365);
});
}
// Lógica para o acordeão do plano
const planAccordion = document.getElementById('plan-collapse');
const planToggleIcon = document.getElementById('plan-toggle-icon');
if (planAccordion && planToggleIcon) {
if (getCookie('plan_collapsed') === 'true') {
planToggleIcon.classList.replace('fa-eye-slash', 'fa-eye');
} else {
new bootstrap.Collapse(planAccordion, { toggle: true });
planToggleIcon.classList.replace('fa-eye', 'fa-eye-slash');
}
planAccordion.addEventListener('show.bs.collapse', function () {
setCookie('plan_collapsed', 'false', 365);
planToggleIcon.classList.replace('fa-eye', 'fa-eye-slash');
});
planAccordion.addEventListener('hide.bs.collapse', function () {
setCookie('plan_collapsed', 'true', 365);
planToggleIcon.classList.replace('fa-eye-slash', 'fa-eye');
});
}
});
// Funções existentes (submitForModeration, openPreview, etc.)
async function openPreview(pageId) { async function openPreview(pageId) {
const button = event.target.closest('button'); const button = event.target.closest('button');
const category = button.dataset.pageCategory; const category = button.dataset.pageCategory;
const slug = button.dataset.pageSlug; const slug = button.dataset.pageSlug;
// Desabilitar botão temporariamente
const originalText = button.innerHTML;
button.disabled = true; button.disabled = true;
button.innerHTML = '<i class="fas fa-spinner fa-spin me-1"></i>Carregando...'; button.innerHTML = '<i class="fas fa-spinner fa-spin me-1"></i>Carregando...';
try { try {
// Gerar novo token
const response = await fetch(`/Admin/GeneratePreviewToken/${pageId}`, { const response = await fetch(`/Admin/GeneratePreviewToken/${pageId}`, {
method: 'POST', method: 'POST',
headers: { headers: { 'RequestVerificationToken': document.querySelector('input[name="__RequestVerificationToken"]').value }
'Content-Type': 'application/json',
'RequestVerificationToken': document.querySelector('input[name="__RequestVerificationToken"]').value
}
}); });
const result = await response.json(); const result = await response.json();
if (result.success) { if (result.success) {
// Abrir preview em nova aba com token novo window.open(`${window.location.origin}/page/${category}/${slug}?preview=${result.previewToken}`, '_blank');
const previewUrl = `${window.location.origin}/page/${category}/${slug}?preview=${result.previewToken}`;
window.open(previewUrl, '_blank');
} else { } else {
showToast(result.message || 'Erro ao gerar preview', 'error'); showToast(result.message || 'Erro ao gerar preview', 'error');
} }
} catch (error) { } catch (error) {
console.error('Erro ao gerar preview:', error);
showToast('Erro ao gerar preview. Tente novamente.', 'error'); showToast('Erro ao gerar preview. Tente novamente.', 'error');
} finally { } finally {
// Reabilitar botão
button.disabled = false; button.disabled = false;
button.innerHTML = originalText; button.innerHTML = '<i class="fas fa-vial me-1"></i>Testar Página';
} }
} }
async function submitForModeration(pageId) { async function submitForModeration(pageId) {
const pageName = event.target.dataset.pageName || 'esta página'; const pageName = event.target.closest('button').dataset.pageName || 'esta página';
if (!confirm(`Enviar "${pageName}" para moderação?\n\nApós enviar, você não poderá mais editá-la até receber o resultado da análise.`)) return;
if (!confirm(`Enviar "${pageName}" para moderação?\n\nApós enviar, você não poderá mais editá-la até receber o resultado da análise.`)) { const button = event.target.closest('button');
return;
}
// Desabilitar botão durante envio
const button = event.target;
const originalText = button.innerHTML;
button.disabled = true; button.disabled = true;
button.innerHTML = '<i class="fas fa-spinner fa-spin me-1"></i>Enviando...'; button.innerHTML = '<i class="fas fa-spinner fa-spin me-1"></i>Enviando...';
try { try {
const response = await fetch(`/Admin/SubmitForModeration/${pageId}`, { const response = await fetch(`/Admin/SubmitForModeration/${pageId}`, {
method: 'POST', method: 'POST',
headers: { headers: { 'RequestVerificationToken': document.querySelector('input[name="__RequestVerificationToken"]').value }
'Content-Type': 'application/json',
'RequestVerificationToken': document.querySelector('input[name="__RequestVerificationToken"]').value
}
}); });
const result = await response.json(); const result = await response.json();
if (result.success) { if (result.success) {
// Mostrar toast de sucesso
showToast(result.message, 'success'); showToast(result.message, 'success');
setTimeout(() => location.reload(), 2000);
// Recarregar página após 2 segundos
setTimeout(() => {
location.reload();
}, 2000);
} else { } else {
showToast(result.message || 'Erro ao enviar página', 'error');
// Reabilitar botão
button.disabled = false; button.disabled = false;
button.innerHTML = originalText; button.innerHTML = '<i class="fas fa-paper-plane"></i><small>Enviar para Moderação</small>';
showToast(result.message || 'Erro ao enviar página', 'error');
} }
} catch (error) { } catch (error) {
console.error('Erro:', error);
showToast('Erro ao enviar página para moderação', 'error');
// Reabilitar botão
button.disabled = false; button.disabled = false;
button.innerHTML = originalText; button.innerHTML = '<i class="fas fa-paper-plane"></i><small>Enviar para Moderação</small>';
showToast('Erro ao enviar página para moderação', 'error');
} }
} }
function showToast(message, type) { function showToast(message, type) {
const toastContainer = getOrCreateToastContainer(); const toastContainer = getOrCreateToastContainer();
const bgClass = type === 'success' ? 'bg-success' : type === 'error' ? 'bg-danger' : 'bg-info'; const bgClass = type === 'success' ? 'bg-success' : type === 'error' ? 'bg-danger' : 'bg-info';
const icon = type === 'success' ? 'fa-check-circle' : type === 'error' ? 'fa-exclamation-triangle' : 'fa-info-circle'; const icon = type === 'success' ? 'fa-check-circle' : type === 'error' ? 'fa-exclamation-triangle' : 'fa-info-circle';
const toastHtml =
const toastHtml = ` `<div class="toast" role="alert" aria-live="assertive" aria-atomic="true" data-bs-delay="5000">
<div class="toast" role="alert" aria-live="assertive" aria-atomic="true" data-bs-delay="5000">
<div class="toast-header ${bgClass} text-white"> <div class="toast-header ${bgClass} text-white">
<i class="fas ${icon} me-2"></i> <i class="fas ${icon} me-2"></i>
<strong class="me-auto">${type === 'success' ? 'Sucesso' : type === 'error' ? 'Erro' : 'Informação'}</strong> <strong class="me-auto">${type === 'success' ? 'Sucesso' : type === 'error' ? 'Erro' : 'Informação'}</strong>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="toast"></button> <button type="button" class="btn-close btn-close-white" data-bs-dismiss="toast"></button>
</div> </div>
<div class="toast-body">${message}</div> <div class="toast-body">${message}</div>
</div> </div>`;
`;
toastContainer.insertAdjacentHTML('beforeend', toastHtml); toastContainer.insertAdjacentHTML('beforeend', toastHtml);
const newToast = toastContainer.lastElementChild; const newToast = toastContainer.lastElementChild;
const toast = new bootstrap.Toast(newToast); const toast = new bootstrap.Toast(newToast);
toast.show(); toast.show();
newToast.addEventListener('hidden.bs.toast', () => newToast.remove());
// Remover toast após ser fechado
newToast.addEventListener('hidden.bs.toast', function() {
newToast.remove();
});
} }
function getOrCreateToastContainer() { function getOrCreateToastContainer() {
@ -562,7 +431,6 @@ function getOrCreateToastContainer() {
} }
return container; return container;
} }
</script> </script>
} }