BCards/src/BCards.Web/Views/Admin/CreatePage.cshtml
Ricardo Carneiro 3d2ce1f8cf
All checks were successful
BCards Deployment Pipeline / Run Tests (push) Successful in 4s
BCards Deployment Pipeline / PR Validation (push) Has been skipped
BCards Deployment Pipeline / Build and Push Image (push) Successful in 15m22s
BCards Deployment Pipeline / Deploy to Production (ARM - OCI) (push) Successful in 1m54s
BCards Deployment Pipeline / Deploy to Test (x86 - Local) (push) Has been skipped
BCards Deployment Pipeline / Cleanup Old Resources (push) Has been skipped
BCards Deployment Pipeline / Deployment Summary (push) Successful in 0s
fix: Increase session timeout to 7 days and set SameSite=None for Cloudflare compatibility
🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-18 12:32:42 -03:00

618 lines
24 KiB
Plaintext

@model BCards.Web.ViewModels.CreatePageViewModel
@{
ViewData["Title"] = "Criar Página";
Layout = "_Layout";
}
<div class="container-fluid">
<div class="row">
<div class="col-12 col-lg-8 mx-auto">
<div class="card shadow-sm">
<div class="card-header bg-primary text-white">
<h4 class="mb-0">
<i class="fas fa-magic"></i>
Criar Sua Página de Links
</h4>
</div>
<div class="card-body">
<!-- Progress Bar -->
<div class="progress mb-4" style="height: 8px;">
<div class="progress-bar" role="progressbar" style="width: 20%" id="wizardProgress"></div>
</div>
<form asp-action="CreatePage" method="post" id="createPageForm">
<!-- Step 1: Informações Básicas -->
<div class="wizard-step" id="step1">
<h5 class="step-title">
<span class="step-number">1</span>
Informações Básicas
</h5>
<div class="row">
<div class="col-md-6">
<div class="mb-3">
<label asp-for="DisplayName" class="form-label">Nome da Página</label>
<input asp-for="DisplayName" class="form-control" placeholder="Ex: João Silva">
<span asp-validation-for="DisplayName" class="text-danger"></span>
</div>
</div>
<div class="col-md-6">
<div class="mb-3">
<label asp-for="Category" class="form-label">Categoria</label>
<select asp-for="Category" class="form-select">
<option value="">Selecione uma categoria</option>
@foreach (var category in ViewBag.Categories as List<BCards.Web.Models.Category> ?? new List<BCards.Web.Models.Category>())
{
<option value="@category.Name">@category.Name</option>
}
</select>
<span asp-validation-for="Category" class="text-danger"></span>
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="mb-3">
<label asp-for="BusinessType" class="form-label">Tipo</label>
<select asp-for="BusinessType" class="form-select">
<option value="individual">Pessoa Física</option>
<option value="company">Empresa</option>
</select>
</div>
</div>
<div class="col-md-6">
<div class="mb-3">
<label for="slugPreview" class="form-label">URL da Página</label>
<div class="input-group">
<span class="input-group-text">page/</span>
<span class="input-group-text" id="categorySlug">categoria</span>
<span class="input-group-text">/</span>
<input type="text" class="form-control" id="slugPreview" readonly>
<input asp-for="Slug" type="hidden">
</div>
<small class="form-text text-muted">URL gerada automaticamente</small>
</div>
</div>
</div>
<div class="mb-3">
<label asp-for="Bio" class="form-label">Bio/Descrição</label>
<textarea asp-for="Bio" class="form-control" rows="3" placeholder="Uma breve descrição sobre você ou sua empresa..."></textarea>
<span asp-validation-for="Bio" class="text-danger"></span>
</div>
</div>
<!-- Step 2: Seleção de Tema -->
<div class="wizard-step d-none" id="step2">
<h5 class="step-title">
<span class="step-number">2</span>
Escolha Seu Tema Visual
</h5>
<div class="row">
@foreach (var theme in ViewBag.Themes as List<BCards.Web.Models.PageTheme> ?? new List<BCards.Web.Models.PageTheme>())
{
<div class="col-md-4 mb-3">
<div class="theme-card" data-theme="@theme.Name.ToLower()">
<div class="theme-preview" style="background: @theme.BackgroundColor; color: @theme.TextColor;">
<div class="theme-header" style="background-color: @theme.PrimaryColor;">
<div class="theme-avatar"></div>
<h6>@theme.Name</h6>
</div>
<div class="theme-links">
<div class="theme-link" style="background-color: @theme.PrimaryColor;"></div>
<div class="theme-link" style="background-color: @theme.SecondaryColor;"></div>
</div>
</div>
<div class="theme-name">
@theme.Name
@if (theme.IsPremium)
{
<span class="badge bg-warning">Premium</span>
}
</div>
</div>
</div>
}
</div>
<input asp-for="SelectedTheme" type="hidden">
</div>
<!-- Step 3: Links Principais -->
<div class="wizard-step d-none" id="step3">
<h5 class="step-title">
<span class="step-number">3</span>
Links Principais
</h5>
<div id="linksContainer">
<!-- Links will be added dynamically -->
</div>
<button type="button" class="btn btn-outline-primary" id="addLinkBtn">
<i class="fas fa-plus"></i> Adicionar Link
</button>
</div>
<!-- Step 4: Redes Sociais -->
<div class="wizard-step d-none" id="step4">
<h5 class="step-title">
<span class="step-number">4</span>
Redes Sociais
</h5>
<div class="row">
<div class="col-md-6">
<div class="mb-3">
<label asp-for="WhatsAppNumber" class="form-label">
<i class="fab fa-whatsapp text-success"></i>
WhatsApp
</label>
<input asp-for="WhatsAppNumber" class="form-control" placeholder="+55 11 99999-9999">
<span asp-validation-for="WhatsAppNumber" class="text-danger"></span>
</div>
</div>
<div class="col-md-6">
<div class="mb-3">
<label asp-for="FacebookUrl" class="form-label">
<i class="fab fa-facebook text-primary"></i>
Facebook
</label>
<input asp-for="FacebookUrl" class="form-control" placeholder="https://facebook.com/seu-perfil">
<span asp-validation-for="FacebookUrl" class="text-danger"></span>
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="mb-3">
<label asp-for="TwitterUrl" class="form-label">
<i class="fab fa-x-twitter"></i>
X / Twitter
</label>
<input asp-for="TwitterUrl" class="form-control" placeholder="https://x.com/seu-perfil">
<span asp-validation-for="TwitterUrl" class="text-danger"></span>
</div>
</div>
<div class="col-md-6">
<div class="mb-3">
<label asp-for="InstagramUrl" class="form-label">
<i class="fab fa-instagram text-danger"></i>
Instagram
</label>
<input asp-for="InstagramUrl" class="form-control" placeholder="https://instagram.com/seu-perfil">
<span asp-validation-for="InstagramUrl" class="text-danger"></span>
</div>
</div>
</div>
</div>
<!-- Step 5: Preview e Finalização -->
<div class="wizard-step d-none" id="step5">
<h5 class="step-title">
<span class="step-number">5</span>
Preview e Finalização
</h5>
<div class="preview-container">
<div class="preview-phone">
<div class="preview-screen" id="previewScreen">
<!-- Preview will be generated here -->
</div>
</div>
</div>
<div class="text-center mt-4">
<p class="text-muted">Sua página estará disponível em:</p>
<strong id="finalUrl">page/categoria/seu-slug</strong>
</div>
</div>
<!-- Navigation Buttons -->
<div class="wizard-navigation mt-4">
<button type="button" class="btn btn-secondary" id="prevBtn" style="display: none;">
<i class="fas fa-arrow-left"></i> Anterior
</button>
<button type="button" class="btn btn-primary float-end" id="nextBtn">
Próximo <i class="fas fa-arrow-right"></i>
</button>
<button type="submit" class="btn btn-success float-end d-none" id="submitBtn">
<i class="fas fa-check"></i> Criar Página
</button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
<style>
.wizard-step {
min-height: 400px;
}
.step-title {
color: #495057;
margin-bottom: 2rem;
padding-bottom: 1rem;
border-bottom: 2px solid #e9ecef;
}
.step-number {
display: inline-block;
width: 30px;
height: 30px;
line-height: 30px;
background-color: #007bff;
color: white;
border-radius: 50%;
text-align: center;
margin-right: 0.5rem;
font-size: 0.875rem;
font-weight: 600;
}
.theme-card {
cursor: pointer;
border: 2px solid transparent;
border-radius: 8px;
overflow: hidden;
transition: all 0.3s ease;
}
.theme-card:hover,
.theme-card.selected {
border-color: #007bff;
box-shadow: 0 4px 12px rgba(0, 123, 255, 0.3);
}
.theme-preview {
height: 120px;
position: relative;
padding: 1rem;
}
.theme-header {
display: flex;
align-items: center;
gap: 0.5rem;
padding: 0.5rem;
border-radius: 4px;
margin-bottom: 1rem;
}
.theme-avatar {
width: 20px;
height: 20px;
border-radius: 50%;
background-color: rgba(255, 255, 255, 0.3);
}
.theme-header h6 {
margin: 0;
font-size: 0.75rem;
color: white;
}
.theme-links {
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.theme-link {
height: 8px;
border-radius: 4px;
opacity: 0.8;
}
.theme-name {
padding: 0.75rem;
text-align: center;
font-weight: 500;
background-color: #f8f9fa;
}
.link-input-group {
background-color: #f8f9fa;
border: 1px solid #dee2e6;
border-radius: 8px;
padding: 1rem;
margin-bottom: 1rem;
}
.preview-container {
display: flex;
justify-content: center;
align-items: center;
min-height: 300px;
}
.preview-phone {
width: 300px;
height: 400px;
border: 8px solid #333;
border-radius: 20px;
background-color: #000;
padding: 20px 10px;
position: relative;
}
.preview-screen {
width: 100%;
height: 100%;
background-color: #fff;
border-radius: 12px;
overflow-y: auto;
padding: 1rem;
}
.wizard-navigation {
border-top: 1px solid #dee2e6;
padding-top: 1rem;
}
</style>
<script>
let currentStep = 1;
const totalSteps = 5;
let linkCount = 0;
$(document).ready(function() {
initializeWizard();
// Generate slug when name or category changes
$('#DisplayName, #Category').on('input change', function() {
generateSlug();
});
// Theme selection
$('.theme-card').on('click', function() {
$('.theme-card').removeClass('selected');
$(this).addClass('selected');
const themeName = $(this).data('theme');
$('#SelectedTheme').val(themeName);
});
// Navigation
$('#nextBtn').on('click', function() {
if (validateCurrentStep()) {
nextStep();
}
});
$('#prevBtn').on('click', function() {
prevStep();
});
// Add link functionality
$('#addLinkBtn').on('click', function() {
addLinkInput();
});
// Form submission
$('#createPageForm').on('submit', function(e) {
generateLinksData();
});
});
function initializeWizard() {
updateProgressBar();
updateNavigationButtons();
addLinkInput(); // Add first link input
}
function nextStep() {
if (currentStep < totalSteps) {
$(`#step${currentStep}`).addClass('d-none');
currentStep++;
$(`#step${currentStep}`).removeClass('d-none');
if (currentStep === 5) {
generatePreview();
}
updateProgressBar();
updateNavigationButtons();
}
}
function prevStep() {
if (currentStep > 1) {
$(`#step${currentStep}`).addClass('d-none');
currentStep--;
$(`#step${currentStep}`).removeClass('d-none');
updateProgressBar();
updateNavigationButtons();
}
}
function updateProgressBar() {
const progress = (currentStep / totalSteps) * 100;
$('#wizardProgress').css('width', progress + '%');
}
function updateNavigationButtons() {
$('#prevBtn').toggle(currentStep > 1);
if (currentStep === totalSteps) {
$('#nextBtn').addClass('d-none');
$('#submitBtn').removeClass('d-none');
} else {
$('#nextBtn').removeClass('d-none');
$('#submitBtn').addClass('d-none');
}
}
function validateCurrentStep() {
let isValid = true;
switch (currentStep) {
case 1:
if (!$('#DisplayName').val() || !$('#Category').val()) {
alert('Por favor, preencha o nome e a categoria.');
isValid = false;
}
break;
case 2:
if (!$('#SelectedTheme').val()) {
alert('Por favor, selecione um tema.');
isValid = false;
}
break;
}
return isValid;
}
function generateSlug() {
const name = $('#DisplayName').val();
const category = $('#Category').val();
if (name && category) {
$.post('/Admin/GenerateSlug', { category: category, name: name })
.done(function(data) {
$('#Slug').val(data.slug);
$('#slugPreview').val(data.slug);
$('#categorySlug').text(category);
$('#finalUrl').text(`page/${category}/${data.slug}`);
});
}
}
function addLinkInput() {
linkCount++;
const linkHtml = `
<div class="link-input-group" data-link="${linkCount}">
<div class="d-flex justify-content-between align-items-center mb-2">
<h6 class="mb-0">Link ${linkCount}</h6>
<button type="button" class="btn btn-sm btn-outline-danger remove-link-btn">
<i class="fas fa-trash"></i>
</button>
</div>
<div class="row">
<div class="col-md-6">
<div class="mb-2">
<label class="form-label">Título</label>
<input type="text" class="form-control link-title" placeholder="Ex: Meu Site">
</div>
</div>
<div class="col-md-6">
<div class="mb-2">
<label class="form-label">URL</label>
<input type="url" class="form-control link-url" placeholder="https://exemplo.com">
</div>
</div>
</div>
<div class="mb-2">
<label class="form-label">Descrição (opcional)</label>
<input type="text" class="form-control link-description" placeholder="Breve descrição do link">
</div>
</div>
`;
$('#linksContainer').append(linkHtml);
// Add remove functionality
$('.remove-link-btn').off('click').on('click', function() {
$(this).closest('.link-input-group').remove();
});
}
function generateLinksData() {
const links = [];
$('.link-input-group').each(function() {
const title = $(this).find('.link-title').val();
const url = $(this).find('.link-url').val();
const description = $(this).find('.link-description').val();
if (title && url) {
links.push({
Title: title,
Url: url,
Description: description,
Icon: ''
});
}
});
// Remove existing hidden link inputs
$('input[name^="Links["]').remove();
// Create hidden inputs for links directly in the form
links.forEach((link, index) => {
$('#createPageForm').append(`
<input type="hidden" name="Links[${index}].Title" value="${link.Title}" />
<input type="hidden" name="Links[${index}].Url" value="${link.Url}" />
<input type="hidden" name="Links[${index}].Description" value="${link.Description}" />
<input type="hidden" name="Links[${index}].Icon" value="${link.Icon}" />
`);
});
// Debug: Log what we're sending
console.log('=== DEBUG GENERATELINKSDATA ===');
console.log('Links found:', links.length);
links.forEach((link, index) => {
console.log(`Link ${index}:`, link);
});
console.log('=== FIM DEBUG ===');
}
function generatePreview() {
const name = $('#DisplayName').val();
const bio = $('#Bio').val();
const selectedTheme = $('#SelectedTheme').val();
let previewHtml = `
<div class="text-center">
<div class="mb-3">
<div style="width: 60px; height: 60px; background-color: #ddd; border-radius: 50%; margin: 0 auto;"></div>
</div>
<h5 class="mb-2">${name}</h5>
<p class="text-muted small mb-3">${bio}</p>
<div class="d-grid gap-2">
`;
// Add links preview
$('.link-input-group').each(function() {
const title = $(this).find('.link-title').val();
if (title) {
previewHtml += `<div class="btn btn-primary btn-sm">${title}</div>`;
}
});
// Add social media preview
if ($('#WhatsAppNumber').val()) {
previewHtml += `<div class="btn btn-success btn-sm"><i class="fab fa-whatsapp"></i> WhatsApp</div>`;
}
if ($('#FacebookUrl').val()) {
previewHtml += `<div class="btn btn-primary btn-sm"><i class="fab fa-facebook"></i> Facebook</div>`;
}
if ($('#TwitterUrl').val()) {
previewHtml += `<div class="btn btn-dark btn-sm"><i class="fab fa-x-twitter"></i> X / Twitter</div>`;
}
if ($('#InstagramUrl').val()) {
previewHtml += `<div class="btn btn-danger btn-sm"><i class="fab fa-instagram"></i> Instagram</div>`;
}
previewHtml += `</div></div>`;
$('#previewScreen').html(previewHtml);
}
</script>
@section Scripts {
@{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}