Compare commits
2 Commits
ce705c51ec
...
7cc8f46a1a
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7cc8f46a1a | ||
|
|
6598dbdcdd |
@ -8,7 +8,7 @@ public class SeoService : ISeoService
|
||||
|
||||
public SeoService(IConfiguration configuration)
|
||||
{
|
||||
_baseUrl = configuration["BaseUrl"] ?? "https://vcart.me";
|
||||
_baseUrl = configuration["BaseUrl"] ?? "https://bcards.site";
|
||||
}
|
||||
|
||||
public SeoSettings GenerateSeoSettings(UserPage userPage, Category category)
|
||||
|
||||
@ -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">
|
||||
@ -21,6 +22,27 @@
|
||||
</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)
|
||||
@ -37,48 +59,30 @@
|
||||
<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>
|
||||
}
|
||||
else
|
||||
{
|
||||
<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 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>
|
||||
</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 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>
|
||||
</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>
|
||||
<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>
|
||||
}
|
||||
</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))
|
||||
else
|
||||
{
|
||||
<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>
|
||||
<button class="btn btn-secondary" disabled>
|
||||
<i class="fas fa-ban me-1"></i>Indisponível
|
||||
</button>
|
||||
}
|
||||
</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,66 +158,55 @@
|
||||
}
|
||||
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 -->
|
||||
<!-- 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">
|
||||
<h6 class="mb-0">
|
||||
<i class="fas fa-crown me-2"></i>
|
||||
Plano Atual
|
||||
</h6>
|
||||
<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>
|
||||
<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>
|
||||
@ -322,54 +214,33 @@
|
||||
</div>
|
||||
<div class="progress" style="height: 6px;">
|
||||
@{
|
||||
var pagesPercentage = Model.CurrentPlan.MaxPages > 0 ?
|
||||
(double)Model.UserPages.Count / Model.CurrentPlan.MaxPages * 100 : 0;
|
||||
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 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 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>
|
||||
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
</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,122 +281,144 @@
|
||||
|
||||
@section Scripts {
|
||||
<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) {
|
||||
const button = event.target.closest('button');
|
||||
const category = button.dataset.pageCategory;
|
||||
const slug = button.dataset.pageSlug;
|
||||
|
||||
// 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
|
||||
}
|
||||
headers: { 'RequestVerificationToken': document.querySelector('input[name="__RequestVerificationToken"]').value }
|
||||
});
|
||||
|
||||
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');
|
||||
window.open(`${window.location.origin}/page/${category}/${slug}?preview=${result.previewToken}`, '_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;
|
||||
button.innerHTML = '<i class="fas fa-vial me-1"></i>Testar Página';
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
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: {
|
||||
'Content-Type': 'application/json',
|
||||
'RequestVerificationToken': document.querySelector('input[name="__RequestVerificationToken"]').value
|
||||
}
|
||||
headers: { 'RequestVerificationToken': document.querySelector('input[name="__RequestVerificationToken"]').value }
|
||||
});
|
||||
|
||||
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);
|
||||
setTimeout(() => location.reload(), 2000);
|
||||
} else {
|
||||
showToast(result.message || 'Erro ao enviar página', 'error');
|
||||
|
||||
// Reabilitar botão
|
||||
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) {
|
||||
console.error('Erro:', error);
|
||||
showToast('Erro ao enviar página para moderação', 'error');
|
||||
|
||||
// Reabilitar botão
|
||||
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) {
|
||||
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">
|
||||
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>
|
||||
`;
|
||||
|
||||
</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();
|
||||
});
|
||||
newToast.addEventListener('hidden.bs.toast', () => newToast.remove());
|
||||
}
|
||||
|
||||
function getOrCreateToastContainer() {
|
||||
@ -562,7 +431,6 @@ function getOrCreateToastContainer() {
|
||||
}
|
||||
return container;
|
||||
}
|
||||
|
||||
</script>
|
||||
}
|
||||
|
||||
|
||||
@ -97,7 +97,7 @@
|
||||
<i class="fs-2 text-primary">🔗</i>
|
||||
</div>
|
||||
<h5>URLs Organizadas</h5>
|
||||
<p class="text-muted">Suas URLs são organizadas por categoria: vcart.me/corretor/seu-nome</p>
|
||||
<p class="text-muted">Suas URLs são organizadas por categoria: bcards.site/corretor/seu-nome</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -55,7 +55,7 @@
|
||||
<li><strong>Informação sobre Compartilhamento:</strong> O direito de saber com quais entidades públicas e privadas compartilhamos seus dados.</li>
|
||||
<li><strong>Revogação do Consentimento:</strong> O direito de revogar seu consentimento a qualquer momento.</li>
|
||||
</ul>
|
||||
<p>Para exercer seus direitos, entre em contato com nosso Encarregado de Proteção de Dados (DPO) através do e-mail <a href="mailto:dpo@vcart.me">dpo@vcart.me</a>. O prazo para resposta é de até 15 dias, conforme a legislação.</p>
|
||||
<p>Para exercer seus direitos, entre em contato com nosso Encarregado de Proteção de Dados (DPO) através do e-mail <a class="email-obfuscated" data-user="dpo" data-domain="bcards.site">[carregando e-mail...]</a>. O prazo para resposta é de até 15 dias, conforme a legislação.</p>
|
||||
|
||||
<h4 class="mt-5 fw-bold">5. Cookies e Tecnologias de Rastreamento</h4>
|
||||
<p>Utilizamos cookies para melhorar sua experiência. Cookies são pequenos arquivos de texto armazenados em seu dispositivo. Você pode gerenciar suas preferências de cookies através do nosso banner de consentimento ou nas configurações do seu navegador.</p>
|
||||
|
||||
@ -47,7 +47,7 @@
|
||||
<li><strong>Oposición:</strong> El derecho a oponerse al tratamiento de sus datos para ciertos fines.</li>
|
||||
<li><strong>Portabilidad:</strong> El derecho a recibir sus datos en un formato estructurado.</li>
|
||||
</ul>
|
||||
<p>Para ejercer sus derechos (conocidos como derechos ARCO en México y Chile), por favor contacte a nuestro Oficial de Protección de Datos (DPO) a través del correo electrónico <a href="mailto:dpo@vcart.me">dpo@vcart.me</a>.</p>
|
||||
<p>Para ejercer sus derechos (conocidos como derechos ARCO en México y Chile), por favor contacte a nuestro Oficial de Protección de Datos (DPO) a través del correo electrónico <a class="email-obfuscated" data-user="dpo" data-domain="bcards.site">[cargando email...]</a>.</p>
|
||||
|
||||
<h4 class="mt-5 fw-bold">5. Retención de Datos</h4>
|
||||
<p>Mantendremos sus datos personales mientras su cuenta esté activa. Si su cuenta es desactivada o permanece inactiva por más de 12 meses, sus datos serán anonimizados o eliminados, excepto aquellos que necesitemos retener para cumplir con obligaciones legales.</p>
|
||||
|
||||
@ -21,7 +21,7 @@
|
||||
<div class="alert alert-secondary text-center">
|
||||
<h5 class="alert-heading">Canal de Atendimento ao Titular</h5>
|
||||
<p class="mb-0">Envie um e-mail para:</p>
|
||||
<a href="mailto:dpo@vcart.me" class="fs-5 fw-bold">dpo@vcart.me</a>
|
||||
<a class="email-obfuscated fs-5 fw-bold" data-user="dpo" data-domain="bcards.site">[carregando e-mail...]</a>
|
||||
</div>
|
||||
|
||||
<p>No seu e-mail, por favor, inclua:</p>
|
||||
|
||||
@ -64,7 +64,7 @@
|
||||
<ul>
|
||||
<li><strong>Legislação Aplicável:</strong> Estes Termos serão regidos e interpretados de acordo com as leis da República Federativa do Brasil, sem consideração com o conflito de disposições legais.</li>
|
||||
<li><strong>Alterações nos Termos:</strong> Podemos modificar estes Termos a qualquer momento. Se fizermos alterações materiais, forneceremos um aviso com antecedência razoável. Ao continuar a usar o BCards após as alterações entrarem em vigor, você concorda em ficar vinculado aos termos revisados.</li>
|
||||
<li><strong>Contato:</strong> Para qualquer dúvida sobre estes Termos, entre em contato conosco pelo e-mail <a href="mailto:suporte@vcart.me">suporte@vcart.me</a>.</li>
|
||||
<li><strong>Contato:</strong> Para qualquer dúvida sobre estes Termos, entre em contato conosco pelo e-mail <a class="email-obfuscated" data-user="suporte" data-domain="bcards.site">[carregando e-mail...]</a>.</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -57,7 +57,7 @@
|
||||
<ul>
|
||||
<li><strong>Legislación Aplicable:</strong> Estos Términos se regirán por las leyes de Brasil para todos los usuarios. Para disputas específicas en Colombia, Chile o México, se pueden considerar las leyes locales.</li>
|
||||
<li><strong>Cambios en los Términos:</strong> Podemos modificar estos Términos en cualquier momento. Le notificaremos con antelación.</li>
|
||||
<li><strong>Contacto:</strong> Para cualquier pregunta sobre estos Términos, contáctenos en <a href="mailto:suporte@vcart.me">suporte@vcart.me</a>.</li>
|
||||
<li><strong>Contacto:</strong> Para cualquier pregunta sobre estos Términos, contáctenos en <a class="email-obfuscated" data-user="suporte" data-domain="bcards.site">[cargando email...]</a>.</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -242,6 +242,8 @@
|
||||
<script src="~/lib/jquery/jquery.min.js"></script>
|
||||
<script src="~/lib/bootstrap/js/bootstrap.bundle.min.js"></script>
|
||||
<script src="~/js/site.js" asp-append-version="true"></script>
|
||||
<script src="~/js/cookie-consent.js" asp-append-version="true"></script>
|
||||
<script src="~/js/email-handler.js" asp-append-version="true"></script>
|
||||
|
||||
<!-- Scripts para menu ativo e barra de carregamento -->
|
||||
<script>
|
||||
@ -346,4 +348,3 @@
|
||||
@await RenderSectionAsync("Scripts", required: false)
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
24
src/BCards.Web/wwwroot/js/email-handler.js
Normal file
24
src/BCards.Web/wwwroot/js/email-handler.js
Normal file
@ -0,0 +1,24 @@
|
||||
/*!
|
||||
* Script para Ofuscação de E-mail
|
||||
* BCards - 2025
|
||||
*/
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
const emailElements = document.querySelectorAll('.email-obfuscated');
|
||||
|
||||
emailElements.forEach(el => {
|
||||
try {
|
||||
const user = el.getAttribute('data-user');
|
||||
const domain = el.getAttribute('data-domain');
|
||||
|
||||
if (user && domain) {
|
||||
const email = user + '@' + domain;
|
||||
el.href = 'mailto:' + email;
|
||||
el.textContent = email;
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Falha ao ofuscar e-mail:', e);
|
||||
el.textContent = 'E-mail indisponível';
|
||||
}
|
||||
});
|
||||
});
|
||||
Loading…
Reference in New Issue
Block a user