961 lines
36 KiB
C#
961 lines
36 KiB
C#
using BCards.Web.Models;
|
|
using BCards.Web.Services;
|
|
using BCards.Web.Utils;
|
|
using BCards.Web.ViewModels;
|
|
using Microsoft.AspNetCore.Authorization;
|
|
using Microsoft.AspNetCore.Mvc;
|
|
using System.Security.Claims;
|
|
|
|
namespace BCards.Web.Controllers;
|
|
|
|
[Authorize]
|
|
[Route("Admin")]
|
|
public class AdminController : Controller
|
|
{
|
|
private readonly IAuthService _authService;
|
|
private readonly IUserPageService _userPageService;
|
|
private readonly ICategoryService _categoryService;
|
|
private readonly IThemeService _themeService;
|
|
private readonly IModerationService _moderationService;
|
|
private readonly IEmailService _emailService;
|
|
private readonly ILivePageService _livePageService;
|
|
private readonly ILogger<AdminController> _logger;
|
|
|
|
public AdminController(
|
|
IAuthService authService,
|
|
IUserPageService userPageService,
|
|
ICategoryService categoryService,
|
|
IThemeService themeService,
|
|
IModerationService moderationService,
|
|
IEmailService emailService,
|
|
ILivePageService livePageService,
|
|
ILogger<AdminController> logger)
|
|
{
|
|
_authService = authService;
|
|
_userPageService = userPageService;
|
|
_categoryService = categoryService;
|
|
_themeService = themeService;
|
|
_moderationService = moderationService;
|
|
_emailService = emailService;
|
|
_livePageService = livePageService;
|
|
_logger = logger;
|
|
}
|
|
|
|
|
|
[HttpGet]
|
|
[Route("Dashboard")]
|
|
public async Task<IActionResult> Dashboard()
|
|
{
|
|
ViewBag.IsHomePage = false; // Menu normal do dashboard
|
|
|
|
var user = await _authService.GetCurrentUserAsync(User);
|
|
if (user == null)
|
|
return RedirectToAction("Login", "Auth");
|
|
|
|
var userPlanType = Enum.TryParse<PlanType>(user.CurrentPlan, true, out var planType) ? planType : PlanType.Trial;
|
|
var userPages = await _userPageService.GetUserPagesAsync(user.Id);
|
|
|
|
var dashboardModel = new DashboardViewModel
|
|
{
|
|
CurrentUser = user,
|
|
UserPages = userPages.Select(p => new UserPageSummary
|
|
{
|
|
Id = p.Id,
|
|
DisplayName = p.DisplayName,
|
|
Slug = p.Slug,
|
|
Category = p.Category,
|
|
Status = p.Status,
|
|
TotalClicks = p.Analytics?.TotalClicks ?? 0,
|
|
TotalViews = p.Analytics?.TotalViews ?? 0,
|
|
PreviewToken = p.PreviewToken,
|
|
CreatedAt = p.CreatedAt,
|
|
LastModerationStatus = p.ModerationHistory == null || p.ModerationHistory.Count == 0 || p.ModerationHistory.Last().Status == "rejected"
|
|
? null
|
|
: Enum.Parse<PageStatus>(p.ModerationHistory.Last().Status, true),
|
|
Motive = p.ModerationHistory == null || p.ModerationHistory.Count == 0 || p.ModerationHistory.Last().Status != "rejected"
|
|
? ""
|
|
: p.ModerationHistory.Last().Reason
|
|
}).ToList(),
|
|
CurrentPlan = new PlanInfo
|
|
{
|
|
Type = userPlanType,
|
|
Name = userPlanType.GetDisplayName(),
|
|
MaxPages = userPlanType.GetMaxPages(),
|
|
MaxLinksPerPage = userPlanType.GetMaxLinksPerPage(),
|
|
DurationDays = userPlanType.GetTrialDays(),
|
|
Price = userPlanType.GetPrice(),
|
|
AllowsAnalytics = userPlanType.AllowsAnalytics(),
|
|
AllowsCustomThemes = userPlanType.AllowsCustomThemes()
|
|
},
|
|
CanCreateNewPage = userPages.Count < userPlanType.GetMaxPages(),
|
|
DaysRemaining = userPlanType == PlanType.Trial ? CalculateTrialDaysRemaining(user) : 0
|
|
};
|
|
|
|
return View(dashboardModel);
|
|
}
|
|
|
|
private int CalculateTrialDaysRemaining(User user)
|
|
{
|
|
// This would be calculated based on subscription data
|
|
// For now, return a default value
|
|
return 7;
|
|
}
|
|
|
|
[HttpGet]
|
|
[Route("ManagePage")]
|
|
public async Task<IActionResult> ManagePage(string id = null)
|
|
{
|
|
ViewBag.IsHomePage = false;
|
|
|
|
var user = await _authService.GetCurrentUserAsync(User);
|
|
if (user == null)
|
|
return RedirectToAction("Login", "Auth");
|
|
|
|
var userPlanType = Enum.TryParse<PlanType>(user.CurrentPlan, true, out var planType) ? planType : PlanType.Trial;
|
|
var categories = await _categoryService.GetAllCategoriesAsync();
|
|
var themes = await _themeService.GetAvailableThemesAsync();
|
|
|
|
if (string.IsNullOrEmpty(id) || id == "new")
|
|
{
|
|
// Check if user can create new page
|
|
var existingPages = await _userPageService.GetUserPagesAsync(user.Id);
|
|
var maxPages = userPlanType.GetMaxPages();
|
|
|
|
if (existingPages.Count >= maxPages)
|
|
{
|
|
TempData["Error"] = $"Você já atingiu o limite de {maxPages} página(s) do seu plano atual. Faça upgrade para criar mais páginas.";
|
|
return RedirectToAction("Dashboard");
|
|
}
|
|
|
|
// CRIAR NOVA PÁGINA
|
|
var model = new ManagePageViewModel
|
|
{
|
|
IsNewPage = true,
|
|
AvailableCategories = categories,
|
|
AvailableThemes = themes.Where(t => !t.IsPremium || userPlanType.AllowsCustomThemes()).ToList(),
|
|
MaxLinksAllowed = userPlanType.GetMaxLinksPerPage()
|
|
};
|
|
return View(model);
|
|
}
|
|
else
|
|
{
|
|
// EDITAR PÁGINA EXISTENTE
|
|
var page = await _userPageService.GetPageByIdAsync(id);
|
|
if (page == null || page.UserId != user.Id)
|
|
return NotFound();
|
|
|
|
var model = MapToManageViewModel(page, categories, themes, userPlanType);
|
|
return View(model);
|
|
}
|
|
}
|
|
|
|
[HttpPost]
|
|
[Route("ManagePage")]
|
|
public async Task<IActionResult> ManagePage(ManagePageViewModel model)
|
|
{
|
|
ViewBag.IsHomePage = false;
|
|
|
|
var user = await _authService.GetCurrentUserAsync(User);
|
|
if (user == null)
|
|
return RedirectToAction("Login", "Auth");
|
|
|
|
_logger.LogInformation($"ManagePage POST: IsNewPage={model.IsNewPage}, DisplayName={model.DisplayName}, Category={model.Category}, Links={model.Links?.Count ?? 0}");
|
|
|
|
if (!ModelState.IsValid)
|
|
{
|
|
_logger.LogWarning("ModelState is invalid:");
|
|
foreach (var error in ModelState)
|
|
{
|
|
_logger.LogWarning($"Key: {error.Key}, Errors: {string.Join(", ", error.Value.Errors.Select(e => e.ErrorMessage))}");
|
|
}
|
|
|
|
// Repopulate dropdowns
|
|
model.AvailableCategories = await _categoryService.GetAllCategoriesAsync();
|
|
model.AvailableThemes = await _themeService.GetAvailableThemesAsync();
|
|
return View(model);
|
|
}
|
|
|
|
if (model.IsNewPage)
|
|
{
|
|
// Generate slug if not provided
|
|
if (string.IsNullOrEmpty(model.Slug))
|
|
{
|
|
model.Slug = await _userPageService.GenerateSlugAsync(model.Category, model.DisplayName);
|
|
}
|
|
|
|
// Check if slug is available
|
|
if (!await _userPageService.ValidateSlugAsync(model.Category, model.Slug))
|
|
{
|
|
ModelState.AddModelError("Slug", "Esta URL já está em uso. Tente outra.");
|
|
model.AvailableCategories = await _categoryService.GetAllCategoriesAsync();
|
|
model.AvailableThemes = await _themeService.GetAvailableThemesAsync();
|
|
return View(model);
|
|
}
|
|
|
|
// Check if user can create the requested number of links
|
|
var activeLinksCount = model.Links?.Count ?? 0;
|
|
if (activeLinksCount > model.MaxLinksAllowed)
|
|
{
|
|
ModelState.AddModelError("", $"Você excedeu o limite de {model.MaxLinksAllowed} links do seu plano atual.");
|
|
model.AvailableCategories = await _categoryService.GetAllCategoriesAsync();
|
|
model.AvailableThemes = await _themeService.GetAvailableThemesAsync();
|
|
return View(model);
|
|
}
|
|
|
|
try
|
|
{
|
|
// Create new page
|
|
var userPage = await MapToUserPage(model, user.Id);
|
|
_logger.LogInformation($"Mapped to UserPage: {userPage.DisplayName}, Category: {userPage.Category}, Slug: {userPage.Slug}");
|
|
|
|
// Set status to Creating for new pages
|
|
userPage.Status = ViewModels.PageStatus.Creating;
|
|
|
|
await _userPageService.CreatePageAsync(userPage);
|
|
_logger.LogInformation("Page created successfully!");
|
|
|
|
// Generate preview token for development
|
|
var previewToken = await _moderationService.GeneratePreviewTokenAsync(userPage.Id);
|
|
var previewUrl = $"{Request.Scheme}://{Request.Host}/page/{userPage.Category}/{userPage.Slug}?preview={previewToken}";
|
|
userPage.PreviewToken = previewToken;
|
|
userPage.PreviewTokenExpiry = DateTime.UtcNow.AddHours(4);
|
|
await _userPageService.UpdatePageAsync(userPage);
|
|
|
|
TempData["Success"] = "Página criada com sucesso! Use o botão 'Enviar para Moderação' quando estiver pronta.";
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, "Error creating page");
|
|
ModelState.AddModelError("", "Erro ao criar página. Tente novamente.");
|
|
model.AvailableCategories = await _categoryService.GetAllCategoriesAsync();
|
|
model.AvailableThemes = await _themeService.GetAvailableThemesAsync();
|
|
TempData["Error"] = $"Erro ao criar página. Tente novamente. TechMsg: {ex.Message}";
|
|
return View(model);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Update existing page
|
|
var existingPage = await _userPageService.GetPageByIdAsync(model.Id);
|
|
if (existingPage == null || existingPage.UserId != user.Id)
|
|
return NotFound();
|
|
|
|
// Check if user can create pages (for users with rejected pages)
|
|
var canCreatePage = await _moderationService.CanUserCreatePageAsync(user.Id);
|
|
if (!canCreatePage)
|
|
{
|
|
TempData["Error"] = "Você não pode editar páginas devido a muitas rejeições. Entre em contato com o suporte.";
|
|
return RedirectToAction("Dashboard");
|
|
}
|
|
|
|
UpdateUserPageFromModel(existingPage, model);
|
|
|
|
// Set status to PendingModeration for updates
|
|
existingPage.Status = ViewModels.PageStatus.Creating;
|
|
existingPage.ModerationAttempts = existingPage.ModerationAttempts;
|
|
|
|
await _userPageService.UpdatePageAsync(existingPage);
|
|
|
|
// Generate new preview token
|
|
var previewToken = await _moderationService.GeneratePreviewTokenAsync(existingPage.Id);
|
|
var previewUrl = $"{Request.Scheme}://{Request.Host}/page/{existingPage.Category}/{existingPage.Slug}?preview={previewToken}";
|
|
|
|
// Send email to user
|
|
await _emailService.SendModerationStatusAsync(
|
|
user.Email,
|
|
user.Name,
|
|
existingPage.DisplayName,
|
|
"pending",
|
|
null,
|
|
previewUrl);
|
|
|
|
TempData["Success"] = "Página atualizada e enviada para moderação!";
|
|
}
|
|
|
|
return RedirectToAction("Dashboard");
|
|
}
|
|
|
|
[HttpPost]
|
|
[Route("CreatePage")]
|
|
public async Task<IActionResult> CreatePage(CreatePageViewModel model)
|
|
{
|
|
var user = await _authService.GetCurrentUserAsync(User);
|
|
if (user == null)
|
|
return RedirectToAction("Login", "Auth");
|
|
|
|
// Check if user already has a page
|
|
var existingPage = await _userPageService.GetUserPageAsync(user.Id);
|
|
if (existingPage != null)
|
|
return RedirectToAction("EditPage");
|
|
|
|
if (!ModelState.IsValid)
|
|
{
|
|
var categories = await _categoryService.GetAllCategoriesAsync();
|
|
var themes = await _themeService.GetAvailableThemesAsync();
|
|
ViewBag.Categories = categories;
|
|
ViewBag.Themes = themes;
|
|
return View(model);
|
|
}
|
|
|
|
// Generate slug if not provided
|
|
if (string.IsNullOrEmpty(model.Slug))
|
|
{
|
|
model.Slug = await _userPageService.GenerateSlugAsync(model.Category, model.DisplayName);
|
|
}
|
|
|
|
// Check if slug is available
|
|
if (!await _userPageService.ValidateSlugAsync(model.Category, model.Slug))
|
|
{
|
|
ModelState.AddModelError("Slug", "Esta URL já está em uso. Tente outra.");
|
|
var categories = await _categoryService.GetAllCategoriesAsync();
|
|
var themes = await _themeService.GetAvailableThemesAsync();
|
|
ViewBag.Categories = categories;
|
|
ViewBag.Themes = themes;
|
|
return View(model);
|
|
}
|
|
|
|
// Check if user can create the requested number of links
|
|
var activeLinksCount = model.Links?.Count ?? 0;
|
|
if (!await _userPageService.CanCreateLinksAsync(user.Id, activeLinksCount))
|
|
{
|
|
ModelState.AddModelError("", "Você excedeu o limite de links do seu plano atual.");
|
|
var categories = await _categoryService.GetAllCategoriesAsync();
|
|
var themes = await _themeService.GetAvailableThemesAsync();
|
|
ViewBag.Categories = categories;
|
|
ViewBag.Themes = themes;
|
|
return View(model);
|
|
}
|
|
|
|
// Convert ViewModel to UserPage
|
|
var userPage = new UserPage
|
|
{
|
|
UserId = user.Id,
|
|
DisplayName = model.DisplayName,
|
|
Category = model.Category,
|
|
BusinessType = model.BusinessType,
|
|
Bio = model.Bio,
|
|
Slug = model.Slug,
|
|
Theme = await _themeService.GetThemeByNameAsync(model.SelectedTheme) ?? _themeService.GetDefaultTheme(),
|
|
Links = model.Links?.Select(l => new LinkItem
|
|
{
|
|
Title = l.Title,
|
|
Url = l.Url,
|
|
Description = l.Description,
|
|
Icon = l.Icon,
|
|
IsActive = true,
|
|
Order = model.Links.IndexOf(l)
|
|
}).ToList() ?? new List<LinkItem>()
|
|
};
|
|
|
|
// Add social media links
|
|
var socialLinks = new List<LinkItem>();
|
|
if (!string.IsNullOrEmpty(model.WhatsAppNumber))
|
|
{
|
|
socialLinks.Add(new LinkItem
|
|
{
|
|
Title = "WhatsApp",
|
|
Url = $"https://wa.me/{model.WhatsAppNumber.Replace("+", "").Replace(" ", "").Replace("-", "").Replace("(", "").Replace(")", "")}",
|
|
Icon = "fab fa-whatsapp",
|
|
IsActive = true,
|
|
Order = userPage.Links.Count + socialLinks.Count
|
|
});
|
|
}
|
|
|
|
if (!string.IsNullOrEmpty(model.FacebookUrl))
|
|
{
|
|
socialLinks.Add(new LinkItem
|
|
{
|
|
Title = "Facebook",
|
|
Url = model.FacebookUrl,
|
|
Icon = "fab fa-facebook",
|
|
IsActive = true,
|
|
Order = userPage.Links.Count + socialLinks.Count
|
|
});
|
|
}
|
|
|
|
if (!string.IsNullOrEmpty(model.TwitterUrl))
|
|
{
|
|
socialLinks.Add(new LinkItem
|
|
{
|
|
Title = "X / Twitter",
|
|
Url = model.TwitterUrl,
|
|
Icon = "fab fa-x-twitter",
|
|
IsActive = true,
|
|
Order = userPage.Links.Count + socialLinks.Count
|
|
});
|
|
}
|
|
|
|
if (!string.IsNullOrEmpty(model.InstagramUrl))
|
|
{
|
|
socialLinks.Add(new LinkItem
|
|
{
|
|
Title = "Instagram",
|
|
Url = model.InstagramUrl,
|
|
Icon = "fab fa-instagram",
|
|
IsActive = true,
|
|
Order = userPage.Links.Count + socialLinks.Count
|
|
});
|
|
}
|
|
|
|
userPage.Links.AddRange(socialLinks);
|
|
|
|
await _userPageService.CreatePageAsync(userPage);
|
|
|
|
TempData["Success"] = "Página criada com sucesso!";
|
|
return RedirectToAction("Dashboard");
|
|
}
|
|
|
|
[HttpGet]
|
|
[Route("EditPage")]
|
|
public async Task<IActionResult> EditPage()
|
|
{
|
|
var user = await _authService.GetCurrentUserAsync(User);
|
|
if (user == null)
|
|
return RedirectToAction("Login", "Auth");
|
|
|
|
var userPage = await _userPageService.GetUserPageAsync(user.Id);
|
|
var categories = await _categoryService.GetAllCategoriesAsync();
|
|
var themes = await _themeService.GetAvailableThemesAsync();
|
|
|
|
ViewBag.Categories = categories;
|
|
ViewBag.Themes = themes;
|
|
ViewBag.IsNew = userPage == null;
|
|
|
|
return View(userPage ?? new UserPage { UserId = user.Id });
|
|
}
|
|
|
|
[HttpPost]
|
|
[Route("EditPage")]
|
|
public async Task<IActionResult> EditPage(UserPage model)
|
|
{
|
|
var user = await _authService.GetCurrentUserAsync(User);
|
|
if (user == null)
|
|
return RedirectToAction("Login", "Auth");
|
|
|
|
if (!ModelState.IsValid)
|
|
{
|
|
var categories = await _categoryService.GetAllCategoriesAsync();
|
|
var themes = await _themeService.GetAvailableThemesAsync();
|
|
ViewBag.Categories = categories;
|
|
ViewBag.Themes = themes;
|
|
return View(model);
|
|
}
|
|
|
|
// Check if user can create the requested number of links
|
|
var activeLinksCount = model.Links?.Count(l => l.IsActive) ?? 0;
|
|
if (!await _userPageService.CanCreateLinksAsync(user.Id, activeLinksCount))
|
|
{
|
|
ModelState.AddModelError("", "Você excedeu o limite de links do seu plano atual.");
|
|
var categories = await _categoryService.GetAllCategoriesAsync();
|
|
var themes = await _themeService.GetAvailableThemesAsync();
|
|
ViewBag.Categories = categories;
|
|
ViewBag.Themes = themes;
|
|
return View(model);
|
|
}
|
|
|
|
model.UserId = user.Id;
|
|
|
|
// Check if slug is available
|
|
if (!await _userPageService.ValidateSlugAsync(model.Category, model.Slug, model.Id))
|
|
{
|
|
ModelState.AddModelError("Slug", "Esta URL já está em uso. Tente outra.");
|
|
var categories = await _categoryService.GetAllCategoriesAsync();
|
|
var themes = await _themeService.GetAvailableThemesAsync();
|
|
ViewBag.Categories = categories;
|
|
ViewBag.Themes = themes;
|
|
return View(model);
|
|
}
|
|
|
|
if (string.IsNullOrEmpty(model.Id))
|
|
{
|
|
await _userPageService.CreatePageAsync(model);
|
|
}
|
|
else
|
|
{
|
|
await _userPageService.UpdatePageAsync(model);
|
|
}
|
|
|
|
TempData["Success"] = "Página salva com sucesso!";
|
|
return RedirectToAction("Dashboard");
|
|
}
|
|
|
|
[HttpPost]
|
|
[Route("CheckSlugAvailability")]
|
|
public async Task<IActionResult> CheckSlugAvailability(string category, string slug, string? excludeId = null)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(category) || string.IsNullOrWhiteSpace(slug))
|
|
return Json(new { available = false, message = "Categoria e slug são obrigatórios." });
|
|
|
|
var isValid = await _userPageService.ValidateSlugAsync(category, slug, excludeId);
|
|
|
|
return Json(new { available = isValid, message = isValid ? "URL disponível!" : "Esta URL já está em uso." });
|
|
}
|
|
|
|
[HttpPost]
|
|
[Route("GenerateSlug")]
|
|
public async Task<IActionResult> GenerateSlug(string category, string name)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(category) || string.IsNullOrWhiteSpace(name))
|
|
return Json(new { slug = "", category = "" });
|
|
|
|
var slug = await _userPageService.GenerateSlugAsync(category, name);
|
|
var categorySlug = SlugHelper.CreateCategorySlug(category).ToLower();
|
|
return Json(new { slug = slug, category = categorySlug });
|
|
}
|
|
|
|
[HttpGet]
|
|
[Route("Analytics")]
|
|
public async Task<IActionResult> Analytics()
|
|
{
|
|
ViewBag.IsHomePage = false;
|
|
|
|
var user = await _authService.GetCurrentUserAsync(User);
|
|
if (user == null)
|
|
return RedirectToAction("Login", "Auth");
|
|
|
|
var userPage = await _userPageService.GetUserPageAsync(user.Id);
|
|
if (userPage == null || !userPage.PlanLimitations.AllowAnalytics)
|
|
return RedirectToAction("Dashboard");
|
|
|
|
return View(userPage);
|
|
}
|
|
|
|
[HttpPost]
|
|
[Route("DeletePage/{id}")]
|
|
public async Task<IActionResult> DeletePage(string id)
|
|
{
|
|
var user = await _authService.GetCurrentUserAsync(User);
|
|
if (user == null)
|
|
return RedirectToAction("Login", "Auth");
|
|
|
|
var userPage = await _userPageService.GetPageByIdAsync(id);
|
|
if (userPage == null || userPage.UserId != user.Id)
|
|
{
|
|
TempData["Error"] = "Página não encontrada!";
|
|
return RedirectToAction("Dashboard");
|
|
}
|
|
|
|
await _userPageService.DeletePageAsync(userPage.Id);
|
|
TempData["Success"] = "Página excluída com sucesso!";
|
|
|
|
return RedirectToAction("Dashboard");
|
|
}
|
|
|
|
private ManagePageViewModel MapToManageViewModel(UserPage page, List<Category> categories, List<PageTheme> themes, PlanType userPlanType)
|
|
{
|
|
return new ManagePageViewModel
|
|
{
|
|
Id = page.Id,
|
|
IsNewPage = false,
|
|
DisplayName = page.DisplayName,
|
|
Category = page.Category,
|
|
BusinessType = page.BusinessType,
|
|
Bio = page.Bio,
|
|
Slug = page.Slug,
|
|
SelectedTheme = page.Theme?.Name ?? "minimalist",
|
|
Links = page.Links?.Select((l, index) => new ManageLinkViewModel
|
|
{
|
|
Id = $"link_{index}",
|
|
Title = l.Title,
|
|
Url = l.Url,
|
|
Description = l.Description,
|
|
Icon = l.Icon,
|
|
Order = l.Order,
|
|
IsActive = l.IsActive,
|
|
Type = l.Type,
|
|
ProductTitle = l.ProductTitle,
|
|
ProductImage = l.ProductImage,
|
|
ProductPrice = l.ProductPrice,
|
|
ProductDescription = l.ProductDescription,
|
|
ProductDataCachedAt = l.ProductDataCachedAt
|
|
}).ToList() ?? new List<ManageLinkViewModel>(),
|
|
AvailableCategories = categories,
|
|
AvailableThemes = themes.Where(t => !t.IsPremium || userPlanType.AllowsCustomThemes()).ToList(),
|
|
MaxLinksAllowed = userPlanType.GetMaxLinksPerPage()
|
|
};
|
|
}
|
|
|
|
private async Task<UserPage> MapToUserPage(ManagePageViewModel model, string userId)
|
|
{
|
|
var theme = await _themeService.GetThemeByNameAsync(model.SelectedTheme) ?? _themeService.GetDefaultTheme();
|
|
|
|
var userPage = new UserPage
|
|
{
|
|
UserId = userId,
|
|
DisplayName = model.DisplayName,
|
|
Category = SlugHelper.ConvertCategory(model.Category.ToLower()),
|
|
BusinessType = model.BusinessType,
|
|
Bio = model.Bio,
|
|
Slug = SlugHelper.CreateSlug(model.Slug.ToLower()),
|
|
Theme = theme,
|
|
Status = ViewModels.PageStatus.Active,
|
|
Links = new List<LinkItem>()
|
|
};
|
|
|
|
// Add regular links
|
|
if (model.Links?.Any() == true)
|
|
{
|
|
userPage.Links.AddRange(model.Links.Where(l => !string.IsNullOrEmpty(l.Title) && !string.IsNullOrEmpty(l.Url))
|
|
.Select((l, index) => new LinkItem
|
|
{
|
|
Title = l.Title,
|
|
Url = l.Url.ToLower(),
|
|
Description = l.Description,
|
|
Icon = l.Icon,
|
|
IsActive = l.IsActive,
|
|
Order = index,
|
|
Type = l.Type,
|
|
ProductTitle = l.ProductTitle,
|
|
ProductImage = l.ProductImage,
|
|
ProductPrice = l.ProductPrice,
|
|
ProductDescription = l.ProductDescription,
|
|
ProductDataCachedAt = l.ProductDataCachedAt
|
|
}));
|
|
}
|
|
|
|
// Add social media links
|
|
var socialLinks = new List<LinkItem>();
|
|
var currentOrder = userPage.Links.Count;
|
|
|
|
if (!string.IsNullOrEmpty(model.WhatsAppNumber))
|
|
{
|
|
socialLinks.Add(new LinkItem
|
|
{
|
|
Title = "WhatsApp",
|
|
Url = $"https://wa.me/{model.WhatsAppNumber.Replace("+", "").Replace(" ", "").Replace("-", "").Replace("(", "").Replace(")", "")}",
|
|
Icon = "fab fa-whatsapp",
|
|
IsActive = true,
|
|
Order = currentOrder++
|
|
});
|
|
}
|
|
|
|
if (!string.IsNullOrEmpty(model.FacebookUrl))
|
|
{
|
|
socialLinks.Add(new LinkItem
|
|
{
|
|
Title = "Facebook",
|
|
Url = model.FacebookUrl,
|
|
Icon = "fab fa-facebook",
|
|
IsActive = true,
|
|
Order = currentOrder++
|
|
});
|
|
}
|
|
|
|
if (!string.IsNullOrEmpty(model.TwitterUrl))
|
|
{
|
|
socialLinks.Add(new LinkItem
|
|
{
|
|
Title = "X / Twitter",
|
|
Url = model.TwitterUrl,
|
|
Icon = "fab fa-x-twitter",
|
|
IsActive = true,
|
|
Order = currentOrder++
|
|
});
|
|
}
|
|
|
|
if (!string.IsNullOrEmpty(model.InstagramUrl))
|
|
{
|
|
socialLinks.Add(new LinkItem
|
|
{
|
|
Title = "Instagram",
|
|
Url = model.InstagramUrl,
|
|
Icon = "fab fa-instagram",
|
|
IsActive = true,
|
|
Order = currentOrder++
|
|
});
|
|
}
|
|
|
|
userPage.Links.AddRange(socialLinks);
|
|
return userPage;
|
|
}
|
|
|
|
private void UpdateUserPageFromModel(UserPage page, ManagePageViewModel model)
|
|
{
|
|
page.DisplayName = model.DisplayName;
|
|
page.Category = model.Category;
|
|
page.BusinessType = model.BusinessType;
|
|
page.Bio = model.Bio;
|
|
page.Slug = model.Slug;
|
|
page.UpdatedAt = DateTime.UtcNow;
|
|
|
|
// Update links
|
|
page.Links = new List<LinkItem>();
|
|
|
|
// Add regular links
|
|
if (model.Links?.Any() == true)
|
|
{
|
|
page.Links.AddRange(model.Links.Where(l => !string.IsNullOrEmpty(l.Title) && !string.IsNullOrEmpty(l.Url))
|
|
.Select((l, index) => new LinkItem
|
|
{
|
|
Title = l.Title,
|
|
Url = l.Url,
|
|
Description = l.Description,
|
|
Icon = l.Icon,
|
|
IsActive = l.IsActive,
|
|
Order = index,
|
|
Type = l.Type,
|
|
ProductTitle = l.ProductTitle,
|
|
ProductImage = l.ProductImage,
|
|
ProductPrice = l.ProductPrice,
|
|
ProductDescription = l.ProductDescription,
|
|
ProductDataCachedAt = l.ProductDataCachedAt
|
|
}));
|
|
}
|
|
|
|
// Add social media links (same logic as create)
|
|
var socialLinks = new List<LinkItem>();
|
|
var currentOrder = page.Links.Count;
|
|
|
|
if (!string.IsNullOrEmpty(model.WhatsAppNumber))
|
|
{
|
|
socialLinks.Add(new LinkItem
|
|
{
|
|
Title = "WhatsApp",
|
|
Url = $"https://wa.me/{model.WhatsAppNumber.Replace("+", "").Replace(" ", "").Replace("-", "").Replace("(", "").Replace(")", "")}",
|
|
Icon = "fab fa-whatsapp",
|
|
IsActive = true,
|
|
Order = currentOrder++
|
|
});
|
|
}
|
|
|
|
if (!string.IsNullOrEmpty(model.FacebookUrl))
|
|
{
|
|
socialLinks.Add(new LinkItem
|
|
{
|
|
Title = "Facebook",
|
|
Url = model.FacebookUrl,
|
|
Icon = "fab fa-facebook",
|
|
IsActive = true,
|
|
Order = currentOrder++
|
|
});
|
|
}
|
|
|
|
if (!string.IsNullOrEmpty(model.TwitterUrl))
|
|
{
|
|
socialLinks.Add(new LinkItem
|
|
{
|
|
Title = "X / Twitter",
|
|
Url = model.TwitterUrl,
|
|
Icon = "fab fa-x-twitter",
|
|
IsActive = true,
|
|
Order = currentOrder++
|
|
});
|
|
}
|
|
|
|
if (!string.IsNullOrEmpty(model.InstagramUrl))
|
|
{
|
|
socialLinks.Add(new LinkItem
|
|
{
|
|
Title = "Instagram",
|
|
Url = model.InstagramUrl,
|
|
Icon = "fab fa-instagram",
|
|
IsActive = true,
|
|
Order = currentOrder++
|
|
});
|
|
}
|
|
|
|
page.Links.AddRange(socialLinks);
|
|
}
|
|
|
|
[HttpPost]
|
|
[Route("SubmitForModeration/{id}")]
|
|
public async Task<IActionResult> SubmitForModeration(string id)
|
|
{
|
|
var user = await _authService.GetCurrentUserAsync(User);
|
|
if (user == null)
|
|
return Json(new { success = false, message = "Usuário não autenticado" });
|
|
|
|
var pageItem = await _userPageService.GetPageByIdAsync(id);
|
|
if (pageItem == null || pageItem.UserId != user.Id)
|
|
return Json(new { success = false, message = "Página não encontrada" });
|
|
|
|
// Validar status atual
|
|
if (pageItem.Status != ViewModels.PageStatus.Creating && pageItem.Status != ViewModels.PageStatus.Rejected)
|
|
return Json(new { success = false, message = "Página não pode ser enviada para moderação neste momento" });
|
|
|
|
// Validar se tem pelo menos 1 link ativo
|
|
var activeLinksCount = pageItem.Links?.Count(l => l.IsActive) ?? 0;
|
|
if (activeLinksCount < 1)
|
|
return Json(new { success = false, message = "Página deve ter pelo menos 1 link ativo para ser enviada" });
|
|
|
|
try
|
|
{
|
|
// Mudar status para PendingModeration
|
|
pageItem.Status = ViewModels.PageStatus.PendingModeration;
|
|
pageItem.ModerationAttempts++;
|
|
pageItem.UpdatedAt = DateTime.UtcNow;
|
|
|
|
await _userPageService.UpdatePageAsync(pageItem);
|
|
|
|
// Enviar email de notificação ao usuário
|
|
await _emailService.SendModerationStatusAsync(
|
|
user.Email,
|
|
user.Name,
|
|
pageItem.DisplayName,
|
|
"pending",
|
|
null,
|
|
$"{Request.Scheme}://{Request.Host}/page/{pageItem.Category}/{pageItem.Slug}?preview={pageItem.PreviewToken}");
|
|
|
|
_logger.LogInformation($"Page {pageItem.Id} submitted for moderation by user {user.Id}");
|
|
|
|
return Json(new {
|
|
success = true,
|
|
message = "Página enviada para moderação com sucesso! Você receberá um email quando for processada.",
|
|
newStatus = "PendingModeration"
|
|
});
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, $"Error submitting page {id} for moderation");
|
|
return Json(new { success = false, message = "Erro interno. Tente novamente." });
|
|
}
|
|
}
|
|
|
|
[HttpPost]
|
|
[Route("RefreshPreviewToken/{id}")]
|
|
public async Task<IActionResult> RefreshPreviewToken(string id)
|
|
{
|
|
var user = await _authService.GetCurrentUserAsync(User);
|
|
if (user == null)
|
|
return Json(new { success = false, message = "Não autorizado" });
|
|
|
|
var pageItem = await _userPageService.GetPageByIdAsync(id);
|
|
if (pageItem == null || pageItem.UserId != user.Id)
|
|
return Json(new { success = false, message = "Página não encontrada" });
|
|
|
|
// Só renovar token para páginas "Creating"
|
|
if (pageItem.Status != ViewModels.PageStatus.Creating)
|
|
return Json(new { success = false, message = "Token só pode ser renovado para páginas em desenvolvimento" });
|
|
|
|
try
|
|
{
|
|
// Gerar novo token com 4 horas de validade
|
|
var newToken = await _moderationService.GeneratePreviewTokenAsync(pageItem.Id);
|
|
|
|
var previewUrl = $"{Request.Scheme}://{Request.Host}/page/{pageItem.Category}/{pageItem.Slug}?preview={newToken}";
|
|
|
|
return Json(new {
|
|
success = true,
|
|
previewToken = newToken,
|
|
previewUrl = previewUrl,
|
|
expiresAt = DateTime.UtcNow.AddHours(4).ToString("yyyy-MM-dd HH:mm:ss")
|
|
});
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, $"Error refreshing preview token for page {id}");
|
|
return Json(new { success = false, message = "Erro ao renovar token" });
|
|
}
|
|
}
|
|
|
|
[HttpPost]
|
|
[Route("GeneratePreviewToken/{id}")]
|
|
public async Task<IActionResult> GeneratePreviewToken(string id)
|
|
{
|
|
var user = await _authService.GetCurrentUserAsync(User);
|
|
if (user == null)
|
|
return Json(new { success = false, message = "Usuário não autenticado" });
|
|
|
|
var pageItem = await _userPageService.GetPageByIdAsync(id);
|
|
if (pageItem == null || pageItem.UserId != user.Id)
|
|
return Json(new { success = false, message = "Página não encontrada" });
|
|
|
|
// Verificar se página pode ter preview
|
|
if (pageItem.Status != ViewModels.PageStatus.Creating &&
|
|
pageItem.Status != ViewModels.PageStatus.PendingModeration &&
|
|
pageItem.Status != ViewModels.PageStatus.Rejected)
|
|
{
|
|
return Json(new { success = false, message = "Preview não disponível para este status" });
|
|
}
|
|
|
|
try
|
|
{
|
|
// Gerar novo token com 4 horas de validade
|
|
var newToken = await _moderationService.GeneratePreviewTokenAsync(pageItem.Id);
|
|
|
|
_logger.LogInformation($"Preview token generated for page {pageItem.Id} by user {user.Id}");
|
|
|
|
return Json(new {
|
|
success = true,
|
|
previewToken = newToken,
|
|
message = "Preview gerado com sucesso!",
|
|
expiresAt = DateTime.UtcNow.AddHours(4).ToString("yyyy-MM-dd HH:mm:ss")
|
|
});
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, $"Error generating preview token for page {id}");
|
|
return Json(new { success = false, message = "Erro interno. Tente novamente." });
|
|
}
|
|
}
|
|
|
|
[HttpPost]
|
|
[Route("MigrateToLivePages")]
|
|
public async Task<IActionResult> MigrateToLivePages()
|
|
{
|
|
var user = await _authService.GetCurrentUserAsync(User);
|
|
if (user == null)
|
|
return Json(new { success = false, message = "Usuário não autenticado" });
|
|
|
|
try
|
|
{
|
|
// Buscar todas as páginas ativas do usuário atual
|
|
var activePages = await _userPageService.GetUserPagesAsync(user.Id);
|
|
var eligiblePages = activePages.Where(p => p.Status == ViewModels.PageStatus.Active).ToList();
|
|
|
|
if (!eligiblePages.Any())
|
|
{
|
|
return Json(new {
|
|
success = false,
|
|
message = "Nenhuma página ativa encontrada para migração"
|
|
});
|
|
}
|
|
|
|
int successCount = 0;
|
|
int errorCount = 0;
|
|
var errors = new List<string>();
|
|
|
|
foreach (var page in eligiblePages)
|
|
{
|
|
try
|
|
{
|
|
await _livePageService.SyncFromUserPageAsync(page.Id);
|
|
successCount++;
|
|
_logger.LogInformation($"Successfully migrated page {page.Id} ({page.DisplayName}) to LivePages");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
errorCount++;
|
|
var errorMsg = $"Erro ao migrar '{page.DisplayName}': {ex.Message}";
|
|
errors.Add(errorMsg);
|
|
_logger.LogError(ex, $"Failed to migrate page {page.Id} to LivePages");
|
|
}
|
|
}
|
|
|
|
var message = $"Migração concluída: {successCount} páginas migradas com sucesso";
|
|
if (errorCount > 0)
|
|
{
|
|
message += $", {errorCount} erros encontrados";
|
|
}
|
|
|
|
return Json(new {
|
|
success = errorCount == 0,
|
|
message = message,
|
|
details = new {
|
|
totalPages = eligiblePages.Count,
|
|
successCount = successCount,
|
|
errorCount = errorCount,
|
|
errors = errors
|
|
}
|
|
});
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, "Error during LivePages migration");
|
|
return Json(new {
|
|
success = false,
|
|
message = $"Erro durante migração: {ex.Message}"
|
|
});
|
|
}
|
|
}
|
|
} |