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
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:
parent
6598dbdcdd
commit
7cc8f46a1a
@ -2,6 +2,7 @@
|
||||
@{
|
||||
ViewData["Title"] = "Dashboard - BCards";
|
||||
Layout = "_Layout";
|
||||
var pageInCreation = Model.UserPages.FirstOrDefault(p => (p.LastModerationStatus ?? p.Status) == BCards.Web.ViewModels.PageStatus.Creating);
|
||||
}
|
||||
|
||||
<div class="container py-4">
|
||||
@ -16,11 +17,32 @@
|
||||
@if (!string.IsNullOrEmpty(Model.CurrentUser.ProfileImage))
|
||||
{
|
||||
<img src="@Model.CurrentUser.ProfileImage" alt="@Model.CurrentUser.Name"
|
||||
class="rounded-circle" style="width: 60px; height: 60px; object-fit: cover;">
|
||||
class="rounded-circle" style="width: 60px; height: 60px; object-fit: cover;">
|
||||
}
|
||||
</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 -->
|
||||
<div class="row">
|
||||
@foreach (var pageItem in Model.UserPages)
|
||||
@ -33,52 +55,34 @@
|
||||
<form method="post" action="/Admin/DeletePage/@(pageItem.Id)" style="display: inline;" onsubmit="return confirm('Tem certeza que deseja excluir esta página?')">
|
||||
@Html.AntiForgeryToken()
|
||||
<button type="submit" class="btn btn-link text-danger p-0" title="Excluir página"
|
||||
style="font-size: 12px; text-decoration: none;">
|
||||
style="font-size: 12px; text-decoration: none;">
|
||||
<i class="fas fa-trash"></i>
|
||||
</button>
|
||||
</form>
|
||||
<input type="hidden" id="displayName_@pageItem.Id" value="@(pageItem.DisplayName)" />
|
||||
</h6>
|
||||
<p class="text-muted small mb-2">@(pageItem.Category)/@(pageItem.Slug)</p>
|
||||
|
||||
<div class="mb-2">
|
||||
@{
|
||||
var pageStatus = pageItem.Status;
|
||||
if (pageItem.Status == BCards.Web.ViewModels.PageStatus.Inactive)
|
||||
{
|
||||
if (pageItem.LastModerationStatus.HasValue)
|
||||
{
|
||||
pageStatus = pageItem.LastModerationStatus.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
@switch (pageStatus)
|
||||
@switch (pageItem.LastModerationStatus ?? pageItem.Status)
|
||||
{
|
||||
case BCards.Web.ViewModels.PageStatus.Active:
|
||||
<span class="badge bg-success">Ativa</span>
|
||||
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:
|
||||
<span class="badge bg-warning">Aguardando</span>
|
||||
<span class="badge bg-warning">Aguardando Moderação</span>
|
||||
break;
|
||||
case BCards.Web.ViewModels.PageStatus.Rejected:
|
||||
<span class="badge bg-danger">Rejeitada</span>
|
||||
break;
|
||||
case BCards.Web.ViewModels.PageStatus.Creating:
|
||||
<span class="badge bg-info">
|
||||
<i class="fas fa-edit me-1"></i>Criando
|
||||
</span>
|
||||
<span class="badge bg-info"><i class="fas fa-edit me-1"></i>Em Criação</span>
|
||||
break;
|
||||
default:
|
||||
<span class="badge bg-secondary">@pageItem.Status</span>
|
||||
break;
|
||||
}
|
||||
</div>
|
||||
|
||||
@if (Model.CurrentPlan.AllowsAnalytics)
|
||||
{
|
||||
<div class="row text-center small mb-3">
|
||||
@ -92,149 +96,49 @@
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
</div>
|
||||
<!-- Cards com Hover Effect -->
|
||||
<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)
|
||||
{
|
||||
<a href="/page/@pageItem.Category/@pageItem.Slug" target="_blank"
|
||||
class="btn btn-success flex-fill">
|
||||
<i class="fas fa-eye me-1"></i>Ver Página
|
||||
<a href="/page/@pageItem.Category/@pageItem.Slug" target="_blank" class="btn btn-success">
|
||||
<i class="fas fa-eye me-1"></i>Ver Página Publicada
|
||||
</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>
|
||||
}
|
||||
else if (pageItem.Status == BCards.Web.ViewModels.PageStatus.Creating ||
|
||||
pageItem.Status == BCards.Web.ViewModels.PageStatus.Rejected ||
|
||||
pageItem.Status == BCards.Web.ViewModels.PageStatus.PendingModeration)
|
||||
else if (pageItem.Status == BCards.Web.ViewModels.PageStatus.Creating || pageItem.Status == BCards.Web.ViewModels.PageStatus.Rejected)
|
||||
{
|
||||
<button type="button"
|
||||
class="btn btn-outline-info flex-fill"
|
||||
onclick="openPreview('@pageItem.Id')"
|
||||
data-page-category="@pageItem.Category"
|
||||
data-page-slug="@pageItem.Slug">
|
||||
<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">
|
||||
<i class="fas fa-vial me-1"></i>Testar Página
|
||||
</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">
|
||||
<i class="fas fa-edit me-1"></i>Editar
|
||||
</a>
|
||||
<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
|
||||
</button>
|
||||
}
|
||||
else if (pageItem.Status == BCards.Web.ViewModels.PageStatus.PendingModeration)
|
||||
{
|
||||
<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">
|
||||
<i class="fas fa-vial me-1"></i>Testar Página
|
||||
</button>
|
||||
<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.">
|
||||
<i class="fas fa-hourglass-half me-1"></i>Aguardando Moderação
|
||||
</button>
|
||||
}
|
||||
else
|
||||
{
|
||||
<button class="btn btn-secondary flex-fill" disabled>
|
||||
<button class="btn btn-secondary" 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>
|
||||
</li>
|
||||
}
|
||||
|
||||
@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>
|
||||
</li>
|
||||
}
|
||||
else if (pageItem.Status == BCards.Web.ViewModels.PageStatus.PendingModeration)
|
||||
{
|
||||
<li><hr class="dropdown-divider"></li>
|
||||
<li>
|
||||
<span class="dropdown-item disabled">
|
||||
<i class="fas fa-hourglass-half me-2"></i>Aguardando Moderação
|
||||
</span>
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
</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">
|
||||
<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 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>
|
||||
|
||||
|
||||
@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 -->
|
||||
@ -246,8 +150,7 @@
|
||||
<div>
|
||||
<i class="fas fa-plus fa-2x text-muted mb-3"></i>
|
||||
<h6 class="text-muted">Criar Nova Página</h6>
|
||||
<a href="@Url.Action("ManagePage", new { id = "new" })"
|
||||
class="btn btn-primary">Começar</a>
|
||||
<a href="@Url.Action("ManagePage", new { id = "new" })" class="btn btn-primary">Começar</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -255,121 +158,89 @@
|
||||
}
|
||||
else if (!Model.UserPages.Any())
|
||||
{
|
||||
<!-- Primeira Página -->
|
||||
<div class="col-12">
|
||||
<div class="card border-primary">
|
||||
<div class="card-body text-center p-5">
|
||||
<div class="mb-4">
|
||||
<i class="display-1 text-primary">🚀</i>
|
||||
</div>
|
||||
<div class="mb-4"><i class="display-1 text-primary">🚀</i></div>
|
||||
<h3>Crie sua primeira página!</h3>
|
||||
<p class="text-muted mb-4">
|
||||
Comece criando sua página profissional personalizada com seus links organizados.
|
||||
</p>
|
||||
<a href="@Url.Action("ManagePage", new { id = "new" })" class="btn btn-primary btn-lg">
|
||||
Criar Minha Página
|
||||
</a>
|
||||
<p class="text-muted mb-4">Comece criando sua página profissional personalizada com seus links organizados.</p>
|
||||
<a href="@Url.Action("ManagePage", new { id = "new" })" class="btn btn-primary btn-lg">Criar Minha Página</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<!-- Limite atingido -->
|
||||
<div class="col-12">
|
||||
<div class="alert alert-warning d-flex align-items-center alert-permanent">
|
||||
<i class="fas fa-exclamation-triangle me-3"></i>
|
||||
<div>
|
||||
<strong>Limite atingido!</strong>
|
||||
Você já criou o máximo de @Model.CurrentPlan.MaxPages página(s) para seu plano atual.
|
||||
<strong>Limite atingido!</strong> 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>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-4">
|
||||
<!-- Plano Atual -->
|
||||
<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">
|
||||
<h6 class="mb-0">
|
||||
<i class="fas fa-crown me-2"></i>
|
||||
Plano Atual
|
||||
</h6>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<h5 class="text-capitalize mb-1">@Model.CurrentPlan.Name</h5>
|
||||
|
||||
@if (Model.CurrentPlan.Type == BCards.Web.Models.PlanType.Trial)
|
||||
{
|
||||
<p class="text-warning mb-2">
|
||||
<i class="fas fa-clock me-1"></i>
|
||||
@Model.DaysRemaining dia(s) restante(s)
|
||||
</p>
|
||||
}
|
||||
else
|
||||
{
|
||||
<p class="text-muted small mb-2">R$ @Model.CurrentPlan.Price.ToString("F2")/mês</p>
|
||||
}
|
||||
|
||||
<div class="mb-3">
|
||||
<div class="d-flex justify-content-between small mb-1">
|
||||
<span>Páginas</span>
|
||||
<span>@Model.UserPages.Count/@Model.CurrentPlan.MaxPages</span>
|
||||
</div>
|
||||
<div class="progress" style="height: 6px;">
|
||||
@{
|
||||
var pagesPercentage = Model.CurrentPlan.MaxPages > 0 ?
|
||||
(double)Model.UserPages.Count / Model.CurrentPlan.MaxPages * 100 : 0;
|
||||
<!-- 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-header @(Model.CurrentPlan.Type == BCards.Web.Models.PlanType.Trial ? "bg-warning" : "bg-primary") text-white">
|
||||
<h2 class="mb-0">
|
||||
<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">
|
||||
<span><i class="fas fa-crown me-2"></i>Plano Atual</span>
|
||||
<i id="plan-toggle-icon" class="fas fa-eye-slash"></i>
|
||||
</button>
|
||||
</h2>
|
||||
</div>
|
||||
<div id="plan-collapse" class="accordion-collapse collapse">
|
||||
<div class="card-body">
|
||||
<h5 class="text-capitalize mb-1">@Model.CurrentPlan.Name</h5>
|
||||
@if (Model.CurrentPlan.Type == BCards.Web.Models.PlanType.Trial)
|
||||
{
|
||||
<p class="text-warning mb-2"><i class="fas fa-clock me-1"></i>@Model.DaysRemaining dia(s) restante(s)</p>
|
||||
}
|
||||
else
|
||||
{
|
||||
<p class="text-muted small mb-2">R$ @Model.CurrentPlan.Price.ToString("F2")/mês</p>
|
||||
}
|
||||
<div class="mb-3">
|
||||
<div class="d-flex justify-content-between small mb-1">
|
||||
<span>Páginas</span>
|
||||
<span>@Model.UserPages.Count/@Model.CurrentPlan.MaxPages</span>
|
||||
</div>
|
||||
<div class="progress" style="height: 6px;">
|
||||
@{
|
||||
var pagesPercentage = Model.CurrentPlan.MaxPages > 0 ? (double)Model.UserPages.Count / Model.CurrentPlan.MaxPages * 100 : 0;
|
||||
}
|
||||
<div class="progress-bar @(pagesPercentage >= 80 ? "bg-warning" : "bg-primary")" style="width: @pagesPercentage%"></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>
|
||||
<div class="small mb-2"><i class="fas fa-chart-bar me-2"></i>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)
|
||||
{
|
||||
<a href="@Url.Action("Pricing", "Home")" class="btn btn-warning w-100"><i class="fas fa-rocket me-2"></i>Fazer Upgrade</a>
|
||||
}
|
||||
else
|
||||
{
|
||||
<a href="@Url.Action("ManageSubscription", "Payment")" class="btn btn-outline-secondary w-100"><i class="fas fa-cog me-2"></i>Gerenciar Assinatura</a>
|
||||
}
|
||||
<div class="progress-bar @(pagesPercentage >= 80 ? "bg-warning" : "bg-primary")"
|
||||
style="width: @pagesPercentage%"></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>
|
||||
<div class="small mb-2">
|
||||
<i class="fas fa-chart-bar me-2"></i>
|
||||
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)
|
||||
{
|
||||
<a href="@Url.Action("Pricing", "Home")" class="btn btn-warning w-100">
|
||||
<i class="fas fa-rocket me-2"></i>
|
||||
Fazer Upgrade
|
||||
</a>
|
||||
}
|
||||
else
|
||||
{
|
||||
<a href="@Url.Action("ManageSubscription", "Payment")" class="btn btn-outline-secondary w-100">
|
||||
<i class="fas fa-cog me-2"></i>
|
||||
Gerenciar Assinatura
|
||||
</a>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Estatísticas Rápidas -->
|
||||
<!-- Estatísticas e Dicas (mantidos como antes) -->
|
||||
@if (Model.CurrentPlan.AllowsAnalytics && Model.UserPages.Any())
|
||||
{
|
||||
<div class="card mb-4">
|
||||
<div class="card-header">
|
||||
<h6 class="mb-0">
|
||||
<i class="fas fa-chart-line me-2"></i>
|
||||
Estatísticas Gerais
|
||||
</h6>
|
||||
</div>
|
||||
<div class="card-header"><h6 class="mb-0"><i class="fas fa-chart-line me-2"></i>Estatísticas Gerais</h6></div>
|
||||
<div class="card-body">
|
||||
<div class="row text-center">
|
||||
<div class="col-6">
|
||||
@ -385,46 +256,22 @@
|
||||
{
|
||||
<hr class="my-3">
|
||||
<div class="text-center">
|
||||
<div class="h5 text-info mb-0">
|
||||
@((Model.UserPages.Sum(p => p.TotalClicks) * 100.0 / Model.UserPages.Sum(p => p.TotalViews)).ToString("F1"))%
|
||||
</div>
|
||||
<div class="h5 text-info mb-0">@((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>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
<!-- Dicas -->
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h6 class="mb-0">
|
||||
<i class="fas fa-lightbulb me-2"></i>
|
||||
💡 Dicas
|
||||
</h6>
|
||||
</div>
|
||||
<div class="card-header"><h6 class="mb-0"><i class="fas fa-lightbulb me-2"></i>💡 Dicas</h6></div>
|
||||
<div class="card-body">
|
||||
<ul class="list-unstyled small mb-0">
|
||||
<li class="mb-2">
|
||||
<i class="fas fa-check text-success me-2"></i>
|
||||
Use uma bio clara e objetiva
|
||||
</li>
|
||||
<li class="mb-2">
|
||||
<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>
|
||||
<li class="mb-2"><i class="fas fa-check text-success me-2"></i>Use uma bio clara e objetiva</li>
|
||||
<li class="mb-2"><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>
|
||||
</div>
|
||||
</div>
|
||||
@ -434,135 +281,156 @@
|
||||
|
||||
@section Scripts {
|
||||
<script>
|
||||
// Função para abrir preview com token fresh
|
||||
async function openPreview(pageId) {
|
||||
const button = event.target.closest('button');
|
||||
const category = button.dataset.pageCategory;
|
||||
const slug = button.dataset.pageSlug;
|
||||
// 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;
|
||||
}
|
||||
|
||||
// Desabilitar botão temporariamente
|
||||
const originalText = button.innerHTML;
|
||||
button.disabled = true;
|
||||
button.innerHTML = '<i class="fas fa-spinner fa-spin me-1"></i>Carregando...';
|
||||
|
||||
try {
|
||||
// Gerar novo token
|
||||
const response = await fetch(`/Admin/GeneratePreviewToken/${pageId}`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'RequestVerificationToken': document.querySelector('input[name="__RequestVerificationToken"]').value
|
||||
}
|
||||
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);
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (result.success) {
|
||||
// Abrir preview em nova aba com token novo
|
||||
const previewUrl = `${window.location.origin}/page/${category}/${slug}?preview=${result.previewToken}`;
|
||||
window.open(previewUrl, '_blank');
|
||||
} else {
|
||||
showToast(result.message || 'Erro ao gerar preview', 'error');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Erro ao gerar preview:', error);
|
||||
showToast('Erro ao gerar preview. Tente novamente.', 'error');
|
||||
} finally {
|
||||
// Reabilitar botão
|
||||
button.disabled = false;
|
||||
button.innerHTML = originalText;
|
||||
}
|
||||
}
|
||||
|
||||
async function submitForModeration(pageId) {
|
||||
const pageName = event.target.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;
|
||||
}
|
||||
|
||||
// Desabilitar botão durante envio
|
||||
const button = event.target;
|
||||
const originalText = button.innerHTML;
|
||||
button.disabled = true;
|
||||
button.innerHTML = '<i class="fas fa-spinner fa-spin me-1"></i>Enviando...';
|
||||
|
||||
try {
|
||||
const response = await fetch(`/Admin/SubmitForModeration/${pageId}`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'RequestVerificationToken': document.querySelector('input[name="__RequestVerificationToken"]').value
|
||||
// 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 });
|
||||
}
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (result.success) {
|
||||
// Mostrar toast de sucesso
|
||||
showToast(result.message, 'success');
|
||||
|
||||
// Recarregar página após 2 segundos
|
||||
setTimeout(() => {
|
||||
location.reload();
|
||||
}, 2000);
|
||||
} else {
|
||||
showToast(result.message || 'Erro ao enviar página', 'error');
|
||||
|
||||
// Reabilitar botão
|
||||
button.disabled = false;
|
||||
button.innerHTML = originalText;
|
||||
instructionsAccordion.addEventListener('show.bs.collapse', function () {
|
||||
setCookie('instructions_collapsed', 'false', 365);
|
||||
});
|
||||
instructionsAccordion.addEventListener('hide.bs.collapse', function () {
|
||||
setCookie('instructions_collapsed', 'true', 365);
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Erro:', error);
|
||||
showToast('Erro ao enviar página para moderação', 'error');
|
||||
|
||||
// Reabilitar botão
|
||||
button.disabled = false;
|
||||
button.innerHTML = originalText;
|
||||
}
|
||||
}
|
||||
// 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');
|
||||
}
|
||||
|
||||
function showToast(message, type) {
|
||||
const toastContainer = getOrCreateToastContainer();
|
||||
|
||||
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 toastHtml = `
|
||||
<div class="toast" role="alert" aria-live="assertive" aria-atomic="true" data-bs-delay="5000">
|
||||
<div class="toast-header ${bgClass} text-white">
|
||||
<i class="fas ${icon} me-2"></i>
|
||||
<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>
|
||||
</div>
|
||||
<div class="toast-body">${message}</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
toastContainer.insertAdjacentHTML('beforeend', toastHtml);
|
||||
|
||||
const newToast = toastContainer.lastElementChild;
|
||||
const toast = new bootstrap.Toast(newToast);
|
||||
toast.show();
|
||||
|
||||
// Remover toast após ser fechado
|
||||
newToast.addEventListener('hidden.bs.toast', function() {
|
||||
newToast.remove();
|
||||
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');
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function getOrCreateToastContainer() {
|
||||
let container = document.querySelector('.toast-container');
|
||||
if (!container) {
|
||||
container = document.createElement('div');
|
||||
container.className = 'toast-container position-fixed top-0 end-0 p-3';
|
||||
container.style.zIndex = '1055';
|
||||
document.body.appendChild(container);
|
||||
// Funções existentes (submitForModeration, openPreview, etc.)
|
||||
async function openPreview(pageId) {
|
||||
const button = event.target.closest('button');
|
||||
const category = button.dataset.pageCategory;
|
||||
const slug = button.dataset.pageSlug;
|
||||
button.disabled = true;
|
||||
button.innerHTML = '<i class="fas fa-spinner fa-spin me-1"></i>Carregando...';
|
||||
try {
|
||||
const response = await fetch(`/Admin/GeneratePreviewToken/${pageId}`, {
|
||||
method: 'POST',
|
||||
headers: { 'RequestVerificationToken': document.querySelector('input[name="__RequestVerificationToken"]').value }
|
||||
});
|
||||
const result = await response.json();
|
||||
if (result.success) {
|
||||
window.open(`${window.location.origin}/page/${category}/${slug}?preview=${result.previewToken}`, '_blank');
|
||||
} else {
|
||||
showToast(result.message || 'Erro ao gerar preview', 'error');
|
||||
}
|
||||
} catch (error) {
|
||||
showToast('Erro ao gerar preview. Tente novamente.', 'error');
|
||||
} finally {
|
||||
button.disabled = false;
|
||||
button.innerHTML = '<i class="fas fa-vial me-1"></i>Testar Página';
|
||||
}
|
||||
}
|
||||
return container;
|
||||
}
|
||||
|
||||
async function submitForModeration(pageId) {
|
||||
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;
|
||||
const button = event.target.closest('button');
|
||||
button.disabled = true;
|
||||
button.innerHTML = '<i class="fas fa-spinner fa-spin me-1"></i>Enviando...';
|
||||
try {
|
||||
const response = await fetch(`/Admin/SubmitForModeration/${pageId}`, {
|
||||
method: 'POST',
|
||||
headers: { 'RequestVerificationToken': document.querySelector('input[name="__RequestVerificationToken"]').value }
|
||||
});
|
||||
const result = await response.json();
|
||||
if (result.success) {
|
||||
showToast(result.message, 'success');
|
||||
setTimeout(() => location.reload(), 2000);
|
||||
} else {
|
||||
button.disabled = false;
|
||||
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) {
|
||||
button.disabled = false;
|
||||
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) {
|
||||
const toastContainer = getOrCreateToastContainer();
|
||||
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 toastHtml =
|
||||
`<div class="toast" role="alert" aria-live="assertive" aria-atomic="true" data-bs-delay="5000">
|
||||
<div class="toast-header ${bgClass} text-white">
|
||||
<i class="fas ${icon} me-2"></i>
|
||||
<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>
|
||||
</div>
|
||||
<div class="toast-body">${message}</div>
|
||||
</div>`;
|
||||
toastContainer.insertAdjacentHTML('beforeend', toastHtml);
|
||||
const newToast = toastContainer.lastElementChild;
|
||||
const toast = new bootstrap.Toast(newToast);
|
||||
toast.show();
|
||||
newToast.addEventListener('hidden.bs.toast', () => newToast.remove());
|
||||
}
|
||||
|
||||
function getOrCreateToastContainer() {
|
||||
let container = document.querySelector('.toast-container');
|
||||
if (!container) {
|
||||
container = document.createElement('div');
|
||||
container.className = 'toast-container position-fixed top-0 end-0 p-3';
|
||||
container.style.zIndex = '1055';
|
||||
document.body.appendChild(container);
|
||||
}
|
||||
return container;
|
||||
}
|
||||
</script>
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user