diff --git a/src/BCards.Web/Areas/Support/Controllers/RatingsController.cs b/src/BCards.Web/Areas/Support/Controllers/RatingsController.cs new file mode 100644 index 0000000..0b05c3f --- /dev/null +++ b/src/BCards.Web/Areas/Support/Controllers/RatingsController.cs @@ -0,0 +1,86 @@ +using BCards.Web.Areas.Support.Models; +using BCards.Web.Areas.Support.Services; +using Microsoft.AspNetCore.Mvc; +using System.Security.Claims; + +namespace BCards.Web.Areas.Support.Controllers; + +[Area("Support")] +[Route("api/ratings")] +[ApiController] +public class RatingsController : ControllerBase +{ + private readonly IRatingService _ratingService; + private readonly ILogger _logger; + + public RatingsController(IRatingService ratingService, ILogger logger) + { + _ratingService = ratingService; + _logger = logger; + } + + [HttpPost] + public async Task SubmitRating([FromBody] RatingSubmissionDto dto) + { + if (!ModelState.IsValid) + { + _logger.LogWarning("Rating inválido submetido: {Errors}", ModelState.Values.SelectMany(v => v.Errors).Select(e => e.ErrorMessage)); + return BadRequest(ModelState); + } + + try + { + var userId = User?.FindFirst(ClaimTypes.NameIdentifier)?.Value; + var success = await _ratingService.SubmitRatingAsync(dto, userId, HttpContext); + + if (success) + { + _logger.LogInformation("Rating de {Stars} estrelas submetido com sucesso", dto.RatingValue); + return Ok(new { message = "Avaliação enviada com sucesso! Obrigado pelo feedback." }); + } + + _logger.LogError("Falha ao submeter rating"); + return StatusCode(503, new { message = "Erro ao processar sua avaliação. Tente novamente mais tarde." }); + } + catch (Exception ex) + { + _logger.LogError(ex, "Erro ao processar rating"); + return StatusCode(500, new { message = "Erro interno ao processar sua avaliação." }); + } + } + + [HttpGet("average")] + public async Task GetAverageRating() + { + try + { + var average = await _ratingService.GetAverageRatingAsync(); + var total = await _ratingService.GetTotalCountAsync(); + + return Ok(new { average = Math.Round(average, 2), total }); + } + catch (Exception ex) + { + _logger.LogError(ex, "Erro ao buscar média de ratings"); + return StatusCode(500, new { message = "Erro ao buscar avaliações" }); + } + } + + [HttpGet("recent")] + public async Task GetRecentRatings([FromQuery] int limit = 10) + { + try + { + if (limit < 1 || limit > 50) + limit = 10; + + var ratings = await _ratingService.GetRecentRatingsAsync(limit); + return Ok(ratings); + } + catch (Exception ex) + { + _logger.LogError(ex, "Erro ao buscar ratings recentes"); + return StatusCode(500, new { message = "Erro ao buscar avaliações recentes" }); + } + } +} diff --git a/src/BCards.Web/Areas/Support/Controllers/SupportController.cs b/src/BCards.Web/Areas/Support/Controllers/SupportController.cs new file mode 100644 index 0000000..fe673e0 --- /dev/null +++ b/src/BCards.Web/Areas/Support/Controllers/SupportController.cs @@ -0,0 +1,46 @@ +using BCards.Web.Areas.Support.Services; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using System.Security.Claims; + +namespace BCards.Web.Areas.Support.Controllers; + +[Area("Support")] +[Authorize] +public class SupportController : Controller +{ + private readonly ISupportService _supportService; + private readonly ILogger _logger; + + public SupportController(ISupportService supportService, ILogger logger) + { + _supportService = supportService; + _logger = logger; + } + + [HttpGet] + public async Task ContactForm() + { + var userId = User.FindFirst(ClaimTypes.NameIdentifier)?.Value; + var options = await _supportService.GetAvailableOptionsAsync(userId); + + if (!options.CanUseContactForm) + { + _logger.LogWarning("Usuário {UserId} tentou acessar formulário sem permissão", userId); + TempData["Error"] = "Seu plano atual não tem acesso ao formulário de contato. Faça upgrade para o plano Básico ou superior."; + return RedirectToAction("Index", "Home", new { area = "" }); + } + + return View(options); + } + + [HttpGet] + [Route("Support/Index")] + public async Task Index() + { + var userId = User.FindFirst(ClaimTypes.NameIdentifier)?.Value; + var options = await _supportService.GetAvailableOptionsAsync(userId); + + return View(options); + } +} diff --git a/src/BCards.Web/Areas/Support/Models/Rating.cs b/src/BCards.Web/Areas/Support/Models/Rating.cs new file mode 100644 index 0000000..f01b6e5 --- /dev/null +++ b/src/BCards.Web/Areas/Support/Models/Rating.cs @@ -0,0 +1,24 @@ +using MongoDB.Bson; +using MongoDB.Bson.Serialization.Attributes; + +namespace BCards.Web.Areas.Support.Models; + +public class Rating +{ + [BsonId] + [BsonRepresentation(BsonType.ObjectId)] + public string Id { get; set; } = string.Empty; + + public int RatingValue { get; set; } // 1-5 stars + public string? Name { get; set; } + public string? Email { get; set; } + public string? Comment { get; set; } + + public string? UserId { get; set; } // null para anônimos + public string? IpAddress { get; set; } + public string? UserAgent { get; set; } + public string? Culture { get; set; } + public string? Url { get; set; } + + public DateTime CreatedAt { get; set; } = DateTime.UtcNow; +} diff --git a/src/BCards.Web/Areas/Support/Models/RatingSubmissionDto.cs b/src/BCards.Web/Areas/Support/Models/RatingSubmissionDto.cs new file mode 100644 index 0000000..ad7bb03 --- /dev/null +++ b/src/BCards.Web/Areas/Support/Models/RatingSubmissionDto.cs @@ -0,0 +1,19 @@ +using System.ComponentModel.DataAnnotations; + +namespace BCards.Web.Areas.Support.Models; + +public class RatingSubmissionDto +{ + [Required] + [Range(1, 5, ErrorMessage = "A avaliação deve ser entre 1 e 5 estrelas")] + public int RatingValue { get; set; } + + [StringLength(100, ErrorMessage = "O nome deve ter no máximo 100 caracteres")] + public string? Name { get; set; } + + [EmailAddress(ErrorMessage = "Email inválido")] + public string? Email { get; set; } + + [StringLength(500, ErrorMessage = "O comentário deve ter no máximo 500 caracteres")] + public string? Comment { get; set; } +} diff --git a/src/BCards.Web/Areas/Support/Models/SupportOptions.cs b/src/BCards.Web/Areas/Support/Models/SupportOptions.cs new file mode 100644 index 0000000..8ebd038 --- /dev/null +++ b/src/BCards.Web/Areas/Support/Models/SupportOptions.cs @@ -0,0 +1,11 @@ +namespace BCards.Web.Areas.Support.Models; + +public class SupportOptions +{ + public bool CanRate { get; set; } + public bool CanUseContactForm { get; set; } + public bool CanAccessTelegram { get; set; } + public string? TelegramUrl { get; set; } + public string? FormspreeUrl { get; set; } + public string UserPlan { get; set; } = "Trial"; +} diff --git a/src/BCards.Web/Areas/Support/Repositories/IRatingRepository.cs b/src/BCards.Web/Areas/Support/Repositories/IRatingRepository.cs new file mode 100644 index 0000000..aaa9e69 --- /dev/null +++ b/src/BCards.Web/Areas/Support/Repositories/IRatingRepository.cs @@ -0,0 +1,12 @@ +using BCards.Web.Areas.Support.Models; + +namespace BCards.Web.Areas.Support.Repositories; + +public interface IRatingRepository +{ + Task CreateAsync(Rating rating); + Task> GetRecentAsync(int limit = 10); + Task GetAverageRatingAsync(); + Task GetTotalCountAsync(); + Task> GetByUserIdAsync(string userId); +} diff --git a/src/BCards.Web/Areas/Support/Repositories/RatingRepository.cs b/src/BCards.Web/Areas/Support/Repositories/RatingRepository.cs new file mode 100644 index 0000000..fb94717 --- /dev/null +++ b/src/BCards.Web/Areas/Support/Repositories/RatingRepository.cs @@ -0,0 +1,123 @@ +using BCards.Web.Areas.Support.Models; +using MongoDB.Driver; + +namespace BCards.Web.Areas.Support.Repositories; + +public class RatingRepository : IRatingRepository +{ + private readonly IMongoCollection _ratings; + private readonly ILogger _logger; + + public RatingRepository(IMongoDatabase database, ILogger logger) + { + _ratings = database.GetCollection("ratings"); + _logger = logger; + + // Criar índices + CreateIndexes(); + } + + private void CreateIndexes() + { + try + { + var indexKeysDefinition = Builders.IndexKeys.Descending(r => r.CreatedAt); + var indexModel = new CreateIndexModel(indexKeysDefinition); + _ratings.Indexes.CreateOne(indexModel); + + var userIdIndexKeys = Builders.IndexKeys.Ascending(r => r.UserId); + var userIdIndexModel = new CreateIndexModel(userIdIndexKeys); + _ratings.Indexes.CreateOne(userIdIndexModel); + + var ratingValueIndexKeys = Builders.IndexKeys.Ascending(r => r.RatingValue); + var ratingValueIndexModel = new CreateIndexModel(ratingValueIndexKeys); + _ratings.Indexes.CreateOne(ratingValueIndexModel); + + var cultureIndexKeys = Builders.IndexKeys.Ascending(r => r.Culture); + var cultureIndexModel = new CreateIndexModel(cultureIndexKeys); + _ratings.Indexes.CreateOne(cultureIndexModel); + } + catch (Exception ex) + { + _logger.LogWarning(ex, "Não foi possível criar índices para a collection ratings"); + } + } + + public async Task CreateAsync(Rating rating) + { + try + { + await _ratings.InsertOneAsync(rating); + _logger.LogInformation("Rating criado com sucesso: {RatingId}", rating.Id); + return rating; + } + catch (Exception ex) + { + _logger.LogError(ex, "Erro ao criar rating"); + throw; + } + } + + public async Task> GetRecentAsync(int limit = 10) + { + try + { + return await _ratings + .Find(_ => true) + .SortByDescending(r => r.CreatedAt) + .Limit(limit) + .ToListAsync(); + } + catch (Exception ex) + { + _logger.LogError(ex, "Erro ao buscar ratings recentes"); + return new List(); + } + } + + public async Task GetAverageRatingAsync() + { + try + { + var ratings = await _ratings.Find(_ => true).ToListAsync(); + if (ratings.Count == 0) + return 0; + + return ratings.Average(r => r.RatingValue); + } + catch (Exception ex) + { + _logger.LogError(ex, "Erro ao calcular média de ratings"); + return 0; + } + } + + public async Task GetTotalCountAsync() + { + try + { + return (int)await _ratings.CountDocumentsAsync(_ => true); + } + catch (Exception ex) + { + _logger.LogError(ex, "Erro ao contar ratings"); + return 0; + } + } + + public async Task> GetByUserIdAsync(string userId) + { + try + { + return await _ratings + .Find(r => r.UserId == userId) + .SortByDescending(r => r.CreatedAt) + .ToListAsync(); + } + catch (Exception ex) + { + _logger.LogError(ex, "Erro ao buscar ratings do usuário {UserId}", userId); + return new List(); + } + } +} diff --git a/src/BCards.Web/Areas/Support/Services/IRatingService.cs b/src/BCards.Web/Areas/Support/Services/IRatingService.cs new file mode 100644 index 0000000..097eb7b --- /dev/null +++ b/src/BCards.Web/Areas/Support/Services/IRatingService.cs @@ -0,0 +1,11 @@ +using BCards.Web.Areas.Support.Models; + +namespace BCards.Web.Areas.Support.Services; + +public interface IRatingService +{ + Task SubmitRatingAsync(RatingSubmissionDto dto, string? userId, HttpContext httpContext); + Task GetAverageRatingAsync(); + Task GetTotalCountAsync(); + Task> GetRecentRatingsAsync(int limit = 10); +} diff --git a/src/BCards.Web/Areas/Support/Services/ISupportService.cs b/src/BCards.Web/Areas/Support/Services/ISupportService.cs new file mode 100644 index 0000000..a72900e --- /dev/null +++ b/src/BCards.Web/Areas/Support/Services/ISupportService.cs @@ -0,0 +1,8 @@ +using BCards.Web.Areas.Support.Models; + +namespace BCards.Web.Areas.Support.Services; + +public interface ISupportService +{ + Task GetAvailableOptionsAsync(string? userId); +} diff --git a/src/BCards.Web/Areas/Support/Services/RatingService.cs b/src/BCards.Web/Areas/Support/Services/RatingService.cs new file mode 100644 index 0000000..de157bc --- /dev/null +++ b/src/BCards.Web/Areas/Support/Services/RatingService.cs @@ -0,0 +1,61 @@ +using BCards.Web.Areas.Support.Models; +using BCards.Web.Areas.Support.Repositories; +using System.Globalization; + +namespace BCards.Web.Areas.Support.Services; + +public class RatingService : IRatingService +{ + private readonly IRatingRepository _repository; + private readonly ILogger _logger; + + public RatingService(IRatingRepository repository, ILogger logger) + { + _repository = repository; + _logger = logger; + } + + public async Task SubmitRatingAsync(RatingSubmissionDto dto, string? userId, HttpContext httpContext) + { + try + { + var rating = new Rating + { + RatingValue = dto.RatingValue, + Name = dto.Name, + Email = dto.Email, + Comment = dto.Comment, + UserId = userId, + IpAddress = httpContext.Connection.RemoteIpAddress?.ToString(), + UserAgent = httpContext.Request.Headers["User-Agent"].ToString(), + Culture = CultureInfo.CurrentCulture.Name, + Url = httpContext.Request.Headers["Referer"].ToString(), + CreatedAt = DateTime.UtcNow + }; + + await _repository.CreateAsync(rating); + _logger.LogInformation("Rating submetido com sucesso por usuário {UserId}", userId ?? "anônimo"); + return true; + } + catch (Exception ex) + { + _logger.LogError(ex, "Erro ao submeter rating"); + return false; + } + } + + public async Task GetAverageRatingAsync() + { + return await _repository.GetAverageRatingAsync(); + } + + public async Task GetTotalCountAsync() + { + return await _repository.GetTotalCountAsync(); + } + + public async Task> GetRecentRatingsAsync(int limit = 10) + { + return await _repository.GetRecentAsync(limit); + } +} diff --git a/src/BCards.Web/Areas/Support/Services/SupportService.cs b/src/BCards.Web/Areas/Support/Services/SupportService.cs new file mode 100644 index 0000000..e147e74 --- /dev/null +++ b/src/BCards.Web/Areas/Support/Services/SupportService.cs @@ -0,0 +1,101 @@ +using BCards.Web.Areas.Support.Models; +using BCards.Web.Configuration; +using BCards.Web.Models; +using BCards.Web.Repositories; +using Microsoft.Extensions.Options; + +namespace BCards.Web.Areas.Support.Services; + +public class SupportService : ISupportService +{ + private readonly IUserRepository _userRepository; + private readonly IOptions _settings; + private readonly ILogger _logger; + + public SupportService( + IUserRepository userRepository, + IOptions settings, + ILogger logger) + { + _userRepository = userRepository; + _settings = settings; + _logger = logger; + } + + public async Task GetAvailableOptionsAsync(string? userId) + { + var options = new SupportOptions + { + CanRate = _settings.Value.EnableRatingForAllUsers, + CanUseContactForm = false, + CanAccessTelegram = false, + TelegramUrl = _settings.Value.TelegramUrl, + FormspreeUrl = _settings.Value.FormspreeUrl, + UserPlan = "Trial" + }; + + // Usuário não autenticado ou trial + if (string.IsNullOrEmpty(userId)) + { + _logger.LogDebug("Usuário não autenticado - apenas rating disponível"); + return options; + } + + try + { + var user = await _userRepository.GetByIdAsync(userId); + if (user == null) + { + _logger.LogWarning("Usuário {UserId} não encontrado", userId); + return options; + } + + var planName = user.CurrentPlan?.ToLower() ?? "trial"; + options.UserPlan = planName; + + _logger.LogDebug("Verificando opções de suporte para usuário {UserId} com plano {Plan}", userId, planName); + + // Trial: apenas rating + if (planName == "trial") + { + _logger.LogDebug("Plano Trial - apenas rating disponível"); + return options; + } + + // Básico: rating + formulário + if (planName == "basic" || planName == "básico") + { + options.CanUseContactForm = true; + options.UserPlan = "Básico"; + _logger.LogDebug("Plano Básico - rating + formulário disponíveis"); + return options; + } + + // Profissional: rating + formulário (sem telegram) + if (planName == "professional" || planName == "profissional") + { + options.CanUseContactForm = true; + options.UserPlan = "Profissional"; + _logger.LogDebug("Plano Profissional - rating + formulário disponíveis"); + return options; + } + + // Premium e PremiumAffiliate: tudo + if (planName == "premium" || planName == "premiumaffiliate" || planName == "premium+afiliados") + { + options.CanUseContactForm = true; + options.CanAccessTelegram = true; + options.UserPlan = planName.Contains("affiliate") || planName.Contains("afiliados") ? "Premium+Afiliados" : "Premium"; + _logger.LogDebug("Plano {Plan} - todas as opções disponíveis", planName); + return options; + } + + return options; + } + catch (Exception ex) + { + _logger.LogError(ex, "Erro ao verificar opções de suporte para usuário {UserId}", userId); + return options; + } + } +} diff --git a/src/BCards.Web/Areas/Support/ViewComponents/SupportFabViewComponent.cs b/src/BCards.Web/Areas/Support/ViewComponents/SupportFabViewComponent.cs new file mode 100644 index 0000000..3ab2db7 --- /dev/null +++ b/src/BCards.Web/Areas/Support/ViewComponents/SupportFabViewComponent.cs @@ -0,0 +1,36 @@ +using BCards.Web.Areas.Support.Services; +using Microsoft.AspNetCore.Mvc; +using System.Security.Claims; + +namespace BCards.Web.Areas.Support.ViewComponents; + +public class SupportFabViewComponent : ViewComponent +{ + private readonly ISupportService _supportService; + private readonly ILogger _logger; + + public SupportFabViewComponent(ISupportService supportService, ILogger logger) + { + _supportService = supportService; + _logger = logger; + } + + public async Task InvokeAsync() + { + try + { + var userId = UserClaimsPrincipal?.FindFirst(ClaimTypes.NameIdentifier)?.Value; + var options = await _supportService.GetAvailableOptionsAsync(userId); + + _logger.LogDebug("SupportFab invocado para usuário {UserId} - Opções: Rating={CanRate}, Form={CanUseContactForm}, Telegram={CanAccessTelegram}", + userId ?? "anônimo", options.CanRate, options.CanUseContactForm, options.CanAccessTelegram); + + return View(options); + } + catch (Exception ex) + { + _logger.LogError(ex, "Erro ao carregar SupportFab ViewComponent"); + return Content(string.Empty); + } + } +} diff --git a/src/BCards.Web/Areas/Support/Views/Shared/Components/SupportFab/Default.cshtml b/src/BCards.Web/Areas/Support/Views/Shared/Components/SupportFab/Default.cshtml new file mode 100644 index 0000000..6b324f2 --- /dev/null +++ b/src/BCards.Web/Areas/Support/Views/Shared/Components/SupportFab/Default.cshtml @@ -0,0 +1,134 @@ +@model BCards.Web.Areas.Support.Models.SupportOptions + + +
+ + + +
+ + + + +@section Scripts { + + + + +} diff --git a/src/BCards.Web/Areas/Support/Views/Support/ContactForm.cshtml b/src/BCards.Web/Areas/Support/Views/Support/ContactForm.cshtml new file mode 100644 index 0000000..14636d5 --- /dev/null +++ b/src/BCards.Web/Areas/Support/Views/Support/ContactForm.cshtml @@ -0,0 +1,155 @@ +@model BCards.Web.Areas.Support.Models.SupportOptions +@{ + ViewData["Title"] = "Formulário de Contato"; + Layout = "_Layout"; +} + +
+
+
+
+
+

+ Fale Conosco +

+ +
+ + Tempo de resposta: Normalmente respondemos em até 24-48 horas. +
+ +
+
+ + +
+ +
+ + + Usaremos este email para responder sua mensagem. +
+ +
+ + +
+ + @if (Model.CanAccessTelegram) + { +
+ + +
+ } + else + { + + } + +
+ + + + 0/2000 caracteres + +
+ + + + + +
+ +
+
+ + @if (Model.CanAccessTelegram) + { +
+
+

Ou se preferir, entre em contato direto via Telegram:

+ + Abrir Telegram + +
+ } +
+
+ +
+

Perguntas Frequentes

+
+
+

+ +

+
+
+ Você pode fazer upgrade a qualquer momento através da página de Planos e Preços. +
+
+
+
+

+ +

+
+
+ Sim! Você pode cancelar sua assinatura a qualquer momento sem multas ou taxas adicionais. +
+
+
+
+

+ +

+
+
+ Respondemos em até 24-48h para planos Básico. Usuários Premium têm suporte prioritário com resposta em até 12h. +
+
+
+
+
+
+
+
+ +@section Scripts { + +} diff --git a/src/BCards.Web/Areas/Support/Views/Support/Index.cshtml b/src/BCards.Web/Areas/Support/Views/Support/Index.cshtml new file mode 100644 index 0000000..70b5a2e --- /dev/null +++ b/src/BCards.Web/Areas/Support/Views/Support/Index.cshtml @@ -0,0 +1,90 @@ +@model BCards.Web.Areas.Support.Models.SupportOptions +@{ + ViewData["Title"] = "Central de Suporte"; + Layout = "_Layout"; +} + +
+
+
+

+ Central de Suporte +

+ +
+ Plano Atual: @Model.UserPlan +
+ +
+ @if (Model.CanAccessTelegram) + { +
+
+
+
+ +
+
Telegram
+

Fale conosco diretamente pelo Telegram para suporte prioritário.

+ + Abrir Telegram + +
+
+
+ } + + @if (Model.CanUseContactForm) + { +
+
+
+
+ +
+
Formulário de Contato
+

Envie sua dúvida ou problema através do nosso formulário.

+ + Enviar Mensagem + +
+
+
+ } + + @if (Model.CanRate) + { +
+
+
+
+ +
+
Avalie-nos
+

Conte-nos sobre sua experiência com a plataforma.

+ +
+
+
+ } +
+ + @if (!Model.CanUseContactForm && !Model.CanAccessTelegram) + { +
+
Desbloqueie Mais Recursos de Suporte!
+

Seu plano atual tem acesso limitado. Faça upgrade para:

+
    +
  • Plano Básico: Formulário de contato + Avaliações
  • +
  • Plano Premium: Telegram + Formulário + Suporte Prioritário
  • +
+ + Ver Planos e Preços + +
+ } +
+
+
diff --git a/src/BCards.Web/Configuration/SupportSettings.cs b/src/BCards.Web/Configuration/SupportSettings.cs new file mode 100644 index 0000000..3caa76a --- /dev/null +++ b/src/BCards.Web/Configuration/SupportSettings.cs @@ -0,0 +1,10 @@ +namespace BCards.Web.Configuration; + +public class SupportSettings +{ + public string TelegramUrl { get; set; } = string.Empty; + public string FormspreeUrl { get; set; } = string.Empty; + public List EnableTelegramForPlans { get; set; } = new(); + public List EnableFormForPlans { get; set; } = new(); + public bool EnableRatingForAllUsers { get; set; } = true; +} diff --git a/src/BCards.Web/Program.cs b/src/BCards.Web/Program.cs index 9ea0f6c..8245bb2 100644 --- a/src/BCards.Web/Program.cs +++ b/src/BCards.Web/Program.cs @@ -484,6 +484,14 @@ builder.Services.AddScoped(); builder.Services.AddScoped(); +// Support Area - Rating and Contact System +builder.Services.Configure( + builder.Configuration.GetSection("Support")); + +builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); + // Configure upload limits for file handling (images up to 5MB) builder.Services.Configure(options => { @@ -721,6 +729,12 @@ if (app.Environment.IsDevelopment()) app.UseResponseCaching(); +// Support Area Routes +app.MapAreaControllerRoute( + name: "support-area", + areaName: "Support", + pattern: "Support/{controller=Support}/{action=Index}/{id?}"); + app.MapControllerRoute( name: "userpage-preview-path", pattern: "page/preview/{category}/{slug}", diff --git a/src/BCards.Web/Views/Shared/Components/SupportFab/Default.cshtml b/src/BCards.Web/Views/Shared/Components/SupportFab/Default.cshtml new file mode 100644 index 0000000..0dc5495 --- /dev/null +++ b/src/BCards.Web/Views/Shared/Components/SupportFab/Default.cshtml @@ -0,0 +1,129 @@ +@model BCards.Web.Areas.Support.Models.SupportOptions + + +
+ + + +
+ + + + + diff --git a/src/BCards.Web/Views/Shared/_Layout.cshtml b/src/BCards.Web/Views/Shared/_Layout.cshtml index 6626dd4..5ddea93 100644 --- a/src/BCards.Web/Views/Shared/_Layout.cshtml +++ b/src/BCards.Web/Views/Shared/_Layout.cshtml @@ -36,6 +36,8 @@ + + @await RenderSectionAsync("Styles", required: false) @@ -207,6 +209,9 @@ + + @await Component.InvokeAsync("SupportFab") + @@ -238,10 +243,10 @@ @if (User.Identity?.IsAuthenticated == true) { Comece Agora - } - else - { - Comece Agora + } + else + { + Comece Agora } @@ -253,6 +258,8 @@ + +