diff --git a/Controllers/AccountController.cs b/Controllers/AccountController.cs index 9094659..8459cb3 100644 --- a/Controllers/AccountController.cs +++ b/Controllers/AccountController.cs @@ -1,326 +1,326 @@ -using Microsoft.AspNetCore.Authentication; -using Microsoft.AspNetCore.Authentication.Cookies; -using Microsoft.AspNetCore.Authentication.Google; -using Microsoft.AspNetCore.Authentication.MicrosoftAccount; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Mvc; -using QRRapidoApp.Models.ViewModels; -using QRRapidoApp.Services; -using System.Security.Claims; -using System.Text.Json; -using System.Text; -using Microsoft.AspNetCore.DataProtection; - -namespace QRRapidoApp.Controllers -{ - public class AccountController : Controller - { - private readonly IUserService _userService; - private readonly AdDisplayService _adDisplayService; - private readonly ILogger _logger; - private readonly IConfiguration _configuration; - private readonly IDataProtector _protector; - - public AccountController(IUserService userService, AdDisplayService adDisplayService, - ILogger logger, IConfiguration configuration, - IDataProtectionProvider dataProtection) - { - _userService = userService; - _adDisplayService = adDisplayService; - _logger = logger; - _configuration = configuration; - _protector = dataProtection.CreateProtector("OAuth.StateProtection"); - } - - [HttpGet] - public IActionResult Login(string returnUrl = "/") - { - _adDisplayService.SetViewBagAds(ViewBag); - ViewBag.ReturnUrl = returnUrl; - return View(); - } - - [HttpGet] - public IActionResult LoginGoogle(string returnUrl = "/") - { - var baseUrl = _configuration.GetSection("App:BaseUrl").Value; - - // Criar state com dados criptografados em vez de sessão - var stateData = new OAuthStateData - { - ReturnUrl = returnUrl, - Nonce = Guid.NewGuid().ToString(), - Timestamp = DateTimeOffset.UtcNow.ToUnixTimeSeconds() - }; - - var stateJson = JsonSerializer.Serialize(stateData); - var protectedState = _protector.Protect(stateJson); - var encodedState = Convert.ToBase64String(Encoding.UTF8.GetBytes(protectedState)); - - var properties = new AuthenticationProperties - { - RedirectUri = $"{baseUrl}{Url.Action("GoogleCallback")}", - Items = { { "state", encodedState } } - }; - - return Challenge(properties, GoogleDefaults.AuthenticationScheme); - } - - [HttpGet] - public IActionResult LoginMicrosoft(string returnUrl = "/") - { - var baseUrl = _configuration.GetSection("App:BaseUrl").Value; - - // Mesmo processo para Microsoft - var stateData = new OAuthStateData - { - ReturnUrl = returnUrl, - Nonce = Guid.NewGuid().ToString(), - Timestamp = DateTimeOffset.UtcNow.ToUnixTimeSeconds() - }; - - var stateJson = JsonSerializer.Serialize(stateData); - var protectedState = _protector.Protect(stateJson); - var encodedState = Convert.ToBase64String(Encoding.UTF8.GetBytes(protectedState)); - - var redirectUrl = returnUrl == "/" - ? $"{baseUrl}/Account/MicrosoftCallback" - : $"{baseUrl}/Account/MicrosoftCallback"; - - var properties = new AuthenticationProperties - { - RedirectUri = redirectUrl, - Items = { { "state", encodedState } } - }; - - return Challenge(properties, MicrosoftAccountDefaults.AuthenticationScheme); - } - - [HttpGet] - public async Task GoogleCallback(string state = null) - { - var returnUrl = await HandleExternalLoginCallbackAsync(GoogleDefaults.AuthenticationScheme, state); - return Redirect(returnUrl ?? "/"); - } - - [HttpGet] - public async Task MicrosoftCallback(string state = null) - { - var returnUrl = await HandleExternalLoginCallbackAsync(MicrosoftAccountDefaults.AuthenticationScheme, state); - return Redirect(returnUrl ?? "/"); - } - - private async Task HandleExternalLoginCallbackAsync(string scheme, string state = null) - { - try - { - _adDisplayService.SetViewBagAds(ViewBag); - - // Recuperar returnUrl do state em vez da sessão - string returnUrl = "/"; - if (!string.IsNullOrEmpty(state)) - { - try - { - var decodedState = Encoding.UTF8.GetString(Convert.FromBase64String(state)); - var unprotectedState = _protector.Unprotect(decodedState); - var stateData = JsonSerializer.Deserialize(unprotectedState); - - // Validar timestamp (não mais que 10 minutos) - var maxAge = DateTimeOffset.UtcNow.ToUnixTimeSeconds() - (10 * 60); - if (stateData.Timestamp > maxAge) - { - returnUrl = stateData.ReturnUrl ?? "/"; - } - else - { - _logger.LogWarning($"OAuth state expired for scheme {scheme}"); - } - } - catch (Exception ex) - { - _logger.LogWarning(ex, $"Failed to decode OAuth state for scheme {scheme}"); - } - } - - var result = await HttpContext.AuthenticateAsync(scheme); - if (!result.Succeeded) - { - _logger.LogWarning($"External authentication failed for scheme {scheme}"); - return "/Account/Login"; - } - - var email = result.Principal?.FindFirst(ClaimTypes.Email)?.Value; - var name = result.Principal?.FindFirst(ClaimTypes.Name)?.Value; - var providerId = result.Principal?.FindFirst(ClaimTypes.NameIdentifier)?.Value; - - if (string.IsNullOrEmpty(email) || string.IsNullOrEmpty(providerId)) - { - _logger.LogWarning($"Missing required claims from {scheme} authentication"); - return "/Account/Login"; - } - - // Find or create user - var user = await _userService.GetUserByProviderAsync(scheme, providerId); - if (user == null) - { - user = await _userService.CreateUserAsync(email, name ?? email, scheme, providerId); - } - else - { - await _userService.UpdateLastLoginAsync(user.Id); - } - - // Create application claims - var claims = new List - { - new Claim(ClaimTypes.NameIdentifier, user.Id), - new Claim(ClaimTypes.Email, user.Email), - new Claim(ClaimTypes.Name, user.Name), - new Claim("Provider", user.Provider), - new Claim("IsPremium", user.IsPremium.ToString()) - }; - - var claimsIdentity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme); - var authProperties = new AuthenticationProperties - { - IsPersistent = true, - ExpiresUtc = DateTimeOffset.UtcNow.AddDays(30) - }; - - await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, - new ClaimsPrincipal(claimsIdentity), authProperties); - - return returnUrl; - } - catch (Exception ex) - { - _logger.LogError(ex, $"Error in external login callback for {scheme}"); - return "/Account/Login"; - } - } - - [HttpPost] - [Authorize] - public async Task Logout() - { - await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme); - return RedirectToAction("Index", "Home"); - } - - [HttpGet] - [Authorize] - public async Task Profile() - { - var userId = User.FindFirst(ClaimTypes.NameIdentifier)?.Value; - if (string.IsNullOrEmpty(userId)) - { - return RedirectToAction("Login"); - } - - var user = await _userService.GetUserAsync(userId); - if (user == null) - { - return RedirectToAction("Login"); - } - - ViewBag.QRHistory = await _userService.GetUserQRHistoryAsync(userId, 10); - ViewBag.MonthlyQRCount = await _userService.GetQRCountThisMonthAsync(userId); - ViewBag.IsPremium = await _adDisplayService.HasValidPremiumSubscription(userId); - _adDisplayService.SetViewBagAds(ViewBag); - - return View(user); - } - - [HttpGet] - public async Task AdFreeStatus() - { - var userId = User?.FindFirst(ClaimTypes.NameIdentifier)?.Value; - - if (string.IsNullOrEmpty(userId)) - { - return Json(new AdFreeStatusViewModel - { - IsAdFree = false, - TimeRemaining = 0, - IsPremium = false - }); - } - - var shouldShowAds = await _adDisplayService.ShouldShowAds(userId); - var isPremium = await _adDisplayService.HasValidPremiumSubscription(userId); - var expiryDate = await _adDisplayService.GetAdFreeExpiryDate(userId); - var status = await _adDisplayService.GetAdFreeStatusAsync(userId); - - return Json(new AdFreeStatusViewModel - { - IsAdFree = !shouldShowAds, - TimeRemaining = isPremium ? int.MaxValue : 0, - IsPremium = isPremium, - ExpiryDate = expiryDate, - SessionType = status - }); - } - - [HttpPost] - [Authorize] - public async Task ExtendAdFreeTime(int minutes) - { - var userId = User.FindFirst(ClaimTypes.NameIdentifier)?.Value; - - // Método removido - sem extensão de tempo ad-free - return Json(new { success = false, message = "Feature not available" }); - } - - [HttpGet] - [Authorize] - public async Task History() - { - _adDisplayService.SetViewBagAds(ViewBag); - var userId = User.FindFirst(ClaimTypes.NameIdentifier)?.Value; - if (string.IsNullOrEmpty(userId)) - { - return RedirectToAction("Login"); - } - - var history = await _userService.GetUserQRHistoryAsync(userId, 50); - return View(history); - } - - [HttpPost] - [Authorize] - public async Task UpdatePreferences(string language) - { - var userId = User.FindFirst(ClaimTypes.NameIdentifier)?.Value; - if (string.IsNullOrEmpty(userId)) - { - return Json(new { success = false }); - } - - try - { - var user = await _userService.GetUserAsync(userId); - if (user != null) - { - user.PreferredLanguage = language; - await _userService.UpdateUserAsync(user); - return Json(new { success = true }); - } - } - catch (Exception ex) - { - _logger.LogError(ex, $"Error updating preferences for user {userId}"); - } - - return Json(new { success = false }); - } - } - - // Classe para dados do state - public class OAuthStateData - { - public string ReturnUrl { get; set; } = "/"; - public string Nonce { get; set; } = ""; - public long Timestamp { get; set; } - } +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Authentication.Cookies; +using Microsoft.AspNetCore.Authentication.Google; +using Microsoft.AspNetCore.Authentication.MicrosoftAccount; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using QRRapidoApp.Models.ViewModels; +using QRRapidoApp.Services; +using System.Security.Claims; +using System.Text.Json; +using System.Text; +using Microsoft.AspNetCore.DataProtection; + +namespace QRRapidoApp.Controllers +{ + public class AccountController : Controller + { + private readonly IUserService _userService; + private readonly AdDisplayService _adDisplayService; + private readonly ILogger _logger; + private readonly IConfiguration _configuration; + private readonly IDataProtector _protector; + + public AccountController(IUserService userService, AdDisplayService adDisplayService, + ILogger logger, IConfiguration configuration, + IDataProtectionProvider dataProtection) + { + _userService = userService; + _adDisplayService = adDisplayService; + _logger = logger; + _configuration = configuration; + _protector = dataProtection.CreateProtector("OAuth.StateProtection"); + } + + [HttpGet] + public IActionResult Login(string returnUrl = "/") + { + _adDisplayService.SetViewBagAds(ViewBag); + ViewBag.ReturnUrl = returnUrl; + return View(); + } + + [HttpGet] + public IActionResult LoginGoogle(string returnUrl = "/") + { + var baseUrl = _configuration.GetSection("App:BaseUrl").Value; + + // Criar state com dados criptografados em vez de sessão + var stateData = new OAuthStateData + { + ReturnUrl = returnUrl, + Nonce = Guid.NewGuid().ToString(), + Timestamp = DateTimeOffset.UtcNow.ToUnixTimeSeconds() + }; + + var stateJson = JsonSerializer.Serialize(stateData); + var protectedState = _protector.Protect(stateJson); + var encodedState = Convert.ToBase64String(Encoding.UTF8.GetBytes(protectedState)); + + var properties = new AuthenticationProperties + { + RedirectUri = $"{baseUrl}{Url.Action("GoogleCallback")}", + Items = { { "state", encodedState } } + }; + + return Challenge(properties, GoogleDefaults.AuthenticationScheme); + } + + [HttpGet] + public IActionResult LoginMicrosoft(string returnUrl = "/") + { + var baseUrl = _configuration.GetSection("App:BaseUrl").Value; + + // Mesmo processo para Microsoft + var stateData = new OAuthStateData + { + ReturnUrl = returnUrl, + Nonce = Guid.NewGuid().ToString(), + Timestamp = DateTimeOffset.UtcNow.ToUnixTimeSeconds() + }; + + var stateJson = JsonSerializer.Serialize(stateData); + var protectedState = _protector.Protect(stateJson); + var encodedState = Convert.ToBase64String(Encoding.UTF8.GetBytes(protectedState)); + + var redirectUrl = returnUrl == "/" + ? $"{baseUrl}/Account/MicrosoftCallback" + : $"{baseUrl}/Account/MicrosoftCallback"; + + var properties = new AuthenticationProperties + { + RedirectUri = redirectUrl, + Items = { { "state", encodedState } } + }; + + return Challenge(properties, MicrosoftAccountDefaults.AuthenticationScheme); + } + + [HttpGet] + public async Task GoogleCallback(string state = null) + { + var returnUrl = await HandleExternalLoginCallbackAsync(GoogleDefaults.AuthenticationScheme, state); + return Redirect(returnUrl ?? "/"); + } + + [HttpGet] + public async Task MicrosoftCallback(string state = null) + { + var returnUrl = await HandleExternalLoginCallbackAsync(MicrosoftAccountDefaults.AuthenticationScheme, state); + return Redirect(returnUrl ?? "/"); + } + + private async Task HandleExternalLoginCallbackAsync(string scheme, string state = null) + { + try + { + _adDisplayService.SetViewBagAds(ViewBag); + + // Recuperar returnUrl do state em vez da sessão + string returnUrl = "/"; + if (!string.IsNullOrEmpty(state)) + { + try + { + var decodedState = Encoding.UTF8.GetString(Convert.FromBase64String(state)); + var unprotectedState = _protector.Unprotect(decodedState); + var stateData = JsonSerializer.Deserialize(unprotectedState); + + // Validar timestamp (não mais que 10 minutos) + var maxAge = DateTimeOffset.UtcNow.ToUnixTimeSeconds() - (10 * 60); + if (stateData.Timestamp > maxAge) + { + returnUrl = stateData.ReturnUrl ?? "/"; + } + else + { + _logger.LogWarning($"OAuth state expired for scheme {scheme}"); + } + } + catch (Exception ex) + { + _logger.LogWarning(ex, $"Failed to decode OAuth state for scheme {scheme}"); + } + } + + var result = await HttpContext.AuthenticateAsync(scheme); + if (!result.Succeeded) + { + _logger.LogWarning($"External authentication failed for scheme {scheme}"); + return "/Account/Login"; + } + + var email = result.Principal?.FindFirst(ClaimTypes.Email)?.Value; + var name = result.Principal?.FindFirst(ClaimTypes.Name)?.Value; + var providerId = result.Principal?.FindFirst(ClaimTypes.NameIdentifier)?.Value; + + if (string.IsNullOrEmpty(email) || string.IsNullOrEmpty(providerId)) + { + _logger.LogWarning($"Missing required claims from {scheme} authentication"); + return "/Account/Login"; + } + + // Find or create user + var user = await _userService.GetUserByProviderAsync(scheme, providerId); + if (user == null) + { + user = await _userService.CreateUserAsync(email, name ?? email, scheme, providerId); + } + else + { + await _userService.UpdateLastLoginAsync(user.Id); + } + + // Create application claims + var claims = new List + { + new Claim(ClaimTypes.NameIdentifier, user.Id), + new Claim(ClaimTypes.Email, user.Email), + new Claim(ClaimTypes.Name, user.Name), + new Claim("Provider", user.Provider), + new Claim("IsPremium", user.IsPremium.ToString()) + }; + + var claimsIdentity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme); + var authProperties = new AuthenticationProperties + { + IsPersistent = true, + ExpiresUtc = DateTimeOffset.UtcNow.AddDays(30) + }; + + await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, + new ClaimsPrincipal(claimsIdentity), authProperties); + + return returnUrl; + } + catch (Exception ex) + { + _logger.LogError(ex, $"Error in external login callback for {scheme}"); + return "/Account/Login"; + } + } + + [HttpPost] + [Authorize] + public async Task Logout() + { + await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme); + return RedirectToAction("Index", "Home"); + } + + [HttpGet] + [Authorize] + public async Task Profile() + { + var userId = User.FindFirst(ClaimTypes.NameIdentifier)?.Value; + if (string.IsNullOrEmpty(userId)) + { + return RedirectToAction("Login"); + } + + var user = await _userService.GetUserAsync(userId); + if (user == null) + { + return RedirectToAction("Login"); + } + + ViewBag.QRHistory = await _userService.GetUserQRHistoryAsync(userId, 10); + ViewBag.MonthlyQRCount = await _userService.GetQRCountThisMonthAsync(userId); + ViewBag.IsPremium = await _adDisplayService.HasValidPremiumSubscription(userId); + _adDisplayService.SetViewBagAds(ViewBag); + + return View(user); + } + + [HttpGet] + public async Task AdFreeStatus() + { + var userId = User?.FindFirst(ClaimTypes.NameIdentifier)?.Value; + + if (string.IsNullOrEmpty(userId)) + { + return Json(new AdFreeStatusViewModel + { + IsAdFree = false, + TimeRemaining = 0, + IsPremium = false + }); + } + + var shouldShowAds = await _adDisplayService.ShouldShowAds(userId); + var isPremium = await _adDisplayService.HasValidPremiumSubscription(userId); + var expiryDate = await _adDisplayService.GetAdFreeExpiryDate(userId); + var status = await _adDisplayService.GetAdFreeStatusAsync(userId); + + return Json(new AdFreeStatusViewModel + { + IsAdFree = !shouldShowAds, + TimeRemaining = isPremium ? int.MaxValue : 0, + IsPremium = isPremium, + ExpiryDate = expiryDate, + SessionType = status + }); + } + + [HttpPost] + [Authorize] + public async Task ExtendAdFreeTime(int minutes) + { + var userId = User.FindFirst(ClaimTypes.NameIdentifier)?.Value; + + // Método removido - sem extensão de tempo ad-free + return Json(new { success = false, message = "Feature not available" }); + } + + [HttpGet] + [Authorize] + public async Task History() + { + _adDisplayService.SetViewBagAds(ViewBag); + var userId = User.FindFirst(ClaimTypes.NameIdentifier)?.Value; + if (string.IsNullOrEmpty(userId)) + { + return RedirectToAction("Login"); + } + + var history = await _userService.GetUserQRHistoryAsync(userId, 50); + return View(history); + } + + [HttpPost] + [Authorize] + public async Task UpdatePreferences(string language) + { + var userId = User.FindFirst(ClaimTypes.NameIdentifier)?.Value; + if (string.IsNullOrEmpty(userId)) + { + return Json(new { success = false }); + } + + try + { + var user = await _userService.GetUserAsync(userId); + if (user != null) + { + user.PreferredLanguage = language; + await _userService.UpdateUserAsync(user); + return Json(new { success = true }); + } + } + catch (Exception ex) + { + _logger.LogError(ex, $"Error updating preferences for user {userId}"); + } + + return Json(new { success = false }); + } + } + + // Classe para dados do state + public class OAuthStateData + { + public string ReturnUrl { get; set; } = "/"; + public string Nonce { get; set; } = ""; + public long Timestamp { get; set; } + } } \ No newline at end of file diff --git a/Controllers/HomeController.cs b/Controllers/HomeController.cs index 53d5802..b0c3f8f 100644 --- a/Controllers/HomeController.cs +++ b/Controllers/HomeController.cs @@ -1,306 +1,306 @@ -using Microsoft.AspNetCore.Mvc; -using QRRapidoApp.Models; -using QRRapidoApp.Services; -using System.Diagnostics; -using System.Security.Claims; -using Microsoft.Extensions.Localization; - -namespace QRRapidoApp.Controllers -{ - public class HomeController : Controller - { - private readonly ILogger _logger; - private readonly AdDisplayService _adDisplayService; - private readonly IUserService _userService; - private readonly IConfiguration _config; - private readonly IStringLocalizer _localizer; - - public HomeController( - ILogger logger, - AdDisplayService adDisplayService, - IUserService userService, - IConfiguration config, - IStringLocalizer localizer - ) - { - _logger = logger; - _adDisplayService = adDisplayService; - _userService = userService; - _config = config; - _localizer = localizer; - } - - public async Task Index() - { - var userId = User?.FindFirst(ClaimTypes.NameIdentifier)?.Value; - - ViewBag.ShowAds = await _adDisplayService.ShouldShowAds(userId); - ViewBag.IsPremium = await _adDisplayService.HasValidPremiumSubscription(userId ?? ""); - ViewBag.IsAuthenticated = User.Identity?.IsAuthenticated ?? false; - ViewBag.UserName = User.Identity?.Name ?? ""; - _adDisplayService.SetViewBagAds(ViewBag); - - // SEO and Analytics data - ViewBag.Title = _config["App:TaglinePT"]; - ViewBag.Keywords = _config["SEO:KeywordsPT"]; - ViewBag.Description = _localizer["QRGenerateDescription"]; - - // User stats for logged in users - if (!string.IsNullOrEmpty(userId)) - { - ViewBag.DailyQRCount = await _userService.GetDailyQRCountAsync(userId); - ViewBag.MonthlyQRCount = await _userService.GetQRCountThisMonthAsync(userId); - } - - return View(); - } - - public IActionResult Privacy() - { - var userId = User?.FindFirst(ClaimTypes.NameIdentifier)?.Value; - - ViewBag.ShowAds = _adDisplayService.ShouldShowAds(userId).Result; - ViewBag.IsPremium = _adDisplayService.HasValidPremiumSubscription(userId ?? "").Result; - ViewBag.IsAuthenticated = User.Identity?.IsAuthenticated ?? false; - ViewBag.UserName = User.Identity?.Name ?? ""; - - ViewBag.Title = _localizer["PrivacyPolicyTitle"]; - ViewBag.Description = _localizer["PrivacyPolicyDescription"]; - _adDisplayService.SetViewBagAds(ViewBag); - - return View(); - } - - public IActionResult Terms() - { - var userId = User?.FindFirst(ClaimTypes.NameIdentifier)?.Value; - - ViewBag.ShowAds = _adDisplayService.ShouldShowAds(userId).Result; - ViewBag.IsPremium = _adDisplayService.HasValidPremiumSubscription(userId ?? "").Result; - ViewBag.IsAuthenticated = User.Identity?.IsAuthenticated ?? false; - ViewBag.UserName = User.Identity?.Name ?? ""; - - ViewBag.Title = _localizer["TermsOfUseTitle"]; - ViewBag.Description = _localizer["TermsOfUseDescription"]; - _adDisplayService.SetViewBagAds(ViewBag); - - return View(); - } - - public IActionResult About() - { - var userId = User?.FindFirst(ClaimTypes.NameIdentifier)?.Value; - - ViewBag.ShowAds = _adDisplayService.ShouldShowAds(userId).Result; - ViewBag.IsPremium = _adDisplayService.HasValidPremiumSubscription(userId ?? "").Result; - ViewBag.IsAuthenticated = User.Identity?.IsAuthenticated ?? false; - ViewBag.UserName = User.Identity?.Name ?? ""; - - ViewBag.Title = _localizer["AboutPageTitle"]; - ViewBag.Description = _localizer["AboutPageDescription"]; - _adDisplayService.SetViewBagAds(ViewBag); - - return View(); - } - - public IActionResult Contact() - { - var userId = User?.FindFirst(ClaimTypes.NameIdentifier)?.Value; - - ViewBag.ShowAds = _adDisplayService.ShouldShowAds(userId).Result; - ViewBag.IsPremium = _adDisplayService.HasValidPremiumSubscription(userId ?? "").Result; - ViewBag.IsAuthenticated = User.Identity?.IsAuthenticated ?? false; - ViewBag.UserName = User.Identity?.Name ?? ""; - - ViewBag.Title = _localizer["ContactPageTitle"]; - ViewBag.Description = _localizer["ContactPageDescription"]; - _adDisplayService.SetViewBagAds(ViewBag); - - return View(); - } - - public IActionResult FAQ() - { - var userId = User?.FindFirst(ClaimTypes.NameIdentifier)?.Value; - - ViewBag.ShowAds = _adDisplayService.ShouldShowAds(userId).Result; - ViewBag.IsPremium = _adDisplayService.HasValidPremiumSubscription(userId ?? "").Result; - ViewBag.IsAuthenticated = User.Identity?.IsAuthenticated ?? false; - ViewBag.UserName = User.Identity?.Name ?? ""; - - ViewBag.Title = _localizer["FAQPageTitle"]; - ViewBag.Description = _localizer["FAQPageDescription"]; - _adDisplayService.SetViewBagAds(ViewBag); - - return View(); - } - - public IActionResult HowToUse() - { - var userId = User?.FindFirst(ClaimTypes.NameIdentifier)?.Value; - - ViewBag.ShowAds = _adDisplayService.ShouldShowAds(userId).Result; - ViewBag.IsPremium = _adDisplayService.HasValidPremiumSubscription(userId ?? "").Result; - ViewBag.IsAuthenticated = User.Identity?.IsAuthenticated ?? false; - ViewBag.UserName = User.Identity?.Name ?? ""; - - ViewBag.Title = _localizer["HowToUsePageTitle"]; - ViewBag.Description = _localizer["HowToUsePageDescription"]; - _adDisplayService.SetViewBagAds(ViewBag); - - return View(); - } - - //[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] - //public IActionResult Error() - //{ - // return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier }); - //} - - [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] - public IActionResult Error() - { - var errorCode = Request.Query["code"].ToString(); - var errorMessage = ""; - - // Interpretar cdigos de erro especficos - if (errorCode.StartsWith("M.C506")) - { - errorMessage = "Erro de autenticao. Verifique suas credenciais e tente novamente."; - } - - ViewBag.ErrorCode = errorCode; - ViewBag.ErrorMessage = errorMessage; - - return View(new ErrorViewModel - { - RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier - }); - } - - // Dynamic QR redirect endpoint - [Route("d/{id}")] - public async Task DynamicRedirect(string id) - { - try - { - _adDisplayService.SetViewBagAds(ViewBag); - // This would lookup the dynamic QR content from cache/database - // For now, return a placeholder - return Redirect("https://qrrapido.site"); - } - catch - { - return NotFound(); - } - } - - // Health check endpoint - [Route("health")] - public IActionResult Health() - { - return Ok(new { status = "healthy", timestamp = DateTime.UtcNow }); - } - - // Sitemap endpoint for SEO - [Route("sitemap.xml")] - public IActionResult Sitemap() - { - var sitemap = $@" - - - https://qrrapido.site/ - {DateTime.UtcNow:yyyy-MM-dd} - daily - 1.0 - - - https://qrrapido.site/pt/ - {DateTime.UtcNow:yyyy-MM-dd} - daily - 0.9 - - - https://qrrapido.site/es/ - {DateTime.UtcNow:yyyy-MM-dd} - daily - 0.9 - - - https://qrrapido.site/pt-BR/About - {DateTime.UtcNow:yyyy-MM-dd} - monthly - 0.8 - - - https://qrrapido.site/es-PY/About - {DateTime.UtcNow:yyyy-MM-dd} - monthly - 0.8 - - - https://qrrapido.site/pt-BR/Contact - {DateTime.UtcNow:yyyy-MM-dd} - monthly - 0.8 - - - https://qrrapido.site/es-PY/Contact - {DateTime.UtcNow:yyyy-MM-dd} - monthly - 0.8 - - - https://qrrapido.site/pt-BR/FAQ - {DateTime.UtcNow:yyyy-MM-dd} - weekly - 0.9 - - - https://qrrapido.site/es-PY/FAQ - {DateTime.UtcNow:yyyy-MM-dd} - weekly - 0.9 - - - https://qrrapido.site/pt-BR/HowToUse - {DateTime.UtcNow:yyyy-MM-dd} - weekly - 0.8 - - - https://qrrapido.site/es-PY/HowToUse - {DateTime.UtcNow:yyyy-MM-dd} - weekly - 0.8 - - - https://qrrapido.site/Premium/Upgrade - {DateTime.UtcNow:yyyy-MM-dd} - weekly - 0.8 - - - https://qrrapido.site/privacy - {DateTime.UtcNow:yyyy-MM-dd} - monthly - 0.5 - - - https://qrrapido.site/terms - {DateTime.UtcNow:yyyy-MM-dd} - monthly - 0.5 - - "; - - return Content(sitemap, "application/xml"); - } - } - - //public class ErrorViewModel - //{ - // public string? RequestId { get; set; } - // public bool ShowRequestId => !string.IsNullOrEmpty(RequestId); - //} +using Microsoft.AspNetCore.Mvc; +using QRRapidoApp.Models; +using QRRapidoApp.Services; +using System.Diagnostics; +using System.Security.Claims; +using Microsoft.Extensions.Localization; + +namespace QRRapidoApp.Controllers +{ + public class HomeController : Controller + { + private readonly ILogger _logger; + private readonly AdDisplayService _adDisplayService; + private readonly IUserService _userService; + private readonly IConfiguration _config; + private readonly IStringLocalizer _localizer; + + public HomeController( + ILogger logger, + AdDisplayService adDisplayService, + IUserService userService, + IConfiguration config, + IStringLocalizer localizer + ) + { + _logger = logger; + _adDisplayService = adDisplayService; + _userService = userService; + _config = config; + _localizer = localizer; + } + + public async Task Index() + { + var userId = User?.FindFirst(ClaimTypes.NameIdentifier)?.Value; + + ViewBag.ShowAds = await _adDisplayService.ShouldShowAds(userId); + ViewBag.IsPremium = await _adDisplayService.HasValidPremiumSubscription(userId ?? ""); + ViewBag.IsAuthenticated = User.Identity?.IsAuthenticated ?? false; + ViewBag.UserName = User.Identity?.Name ?? ""; + _adDisplayService.SetViewBagAds(ViewBag); + + // SEO and Analytics data + ViewBag.Title = _config["App:TaglinePT"]; + ViewBag.Keywords = _config["SEO:KeywordsPT"]; + ViewBag.Description = _localizer["QRGenerateDescription"]; + + // User stats for logged in users + if (!string.IsNullOrEmpty(userId)) + { + ViewBag.DailyQRCount = await _userService.GetDailyQRCountAsync(userId); + ViewBag.MonthlyQRCount = await _userService.GetQRCountThisMonthAsync(userId); + } + + return View(); + } + + public IActionResult Privacy() + { + var userId = User?.FindFirst(ClaimTypes.NameIdentifier)?.Value; + + ViewBag.ShowAds = _adDisplayService.ShouldShowAds(userId).Result; + ViewBag.IsPremium = _adDisplayService.HasValidPremiumSubscription(userId ?? "").Result; + ViewBag.IsAuthenticated = User.Identity?.IsAuthenticated ?? false; + ViewBag.UserName = User.Identity?.Name ?? ""; + + ViewBag.Title = _localizer["PrivacyPolicyTitle"]; + ViewBag.Description = _localizer["PrivacyPolicyDescription"]; + _adDisplayService.SetViewBagAds(ViewBag); + + return View(); + } + + public IActionResult Terms() + { + var userId = User?.FindFirst(ClaimTypes.NameIdentifier)?.Value; + + ViewBag.ShowAds = _adDisplayService.ShouldShowAds(userId).Result; + ViewBag.IsPremium = _adDisplayService.HasValidPremiumSubscription(userId ?? "").Result; + ViewBag.IsAuthenticated = User.Identity?.IsAuthenticated ?? false; + ViewBag.UserName = User.Identity?.Name ?? ""; + + ViewBag.Title = _localizer["TermsOfUseTitle"]; + ViewBag.Description = _localizer["TermsOfUseDescription"]; + _adDisplayService.SetViewBagAds(ViewBag); + + return View(); + } + + public IActionResult About() + { + var userId = User?.FindFirst(ClaimTypes.NameIdentifier)?.Value; + + ViewBag.ShowAds = _adDisplayService.ShouldShowAds(userId).Result; + ViewBag.IsPremium = _adDisplayService.HasValidPremiumSubscription(userId ?? "").Result; + ViewBag.IsAuthenticated = User.Identity?.IsAuthenticated ?? false; + ViewBag.UserName = User.Identity?.Name ?? ""; + + ViewBag.Title = _localizer["AboutPageTitle"]; + ViewBag.Description = _localizer["AboutPageDescription"]; + _adDisplayService.SetViewBagAds(ViewBag); + + return View(); + } + + public IActionResult Contact() + { + var userId = User?.FindFirst(ClaimTypes.NameIdentifier)?.Value; + + ViewBag.ShowAds = _adDisplayService.ShouldShowAds(userId).Result; + ViewBag.IsPremium = _adDisplayService.HasValidPremiumSubscription(userId ?? "").Result; + ViewBag.IsAuthenticated = User.Identity?.IsAuthenticated ?? false; + ViewBag.UserName = User.Identity?.Name ?? ""; + + ViewBag.Title = _localizer["ContactPageTitle"]; + ViewBag.Description = _localizer["ContactPageDescription"]; + _adDisplayService.SetViewBagAds(ViewBag); + + return View(); + } + + public IActionResult FAQ() + { + var userId = User?.FindFirst(ClaimTypes.NameIdentifier)?.Value; + + ViewBag.ShowAds = _adDisplayService.ShouldShowAds(userId).Result; + ViewBag.IsPremium = _adDisplayService.HasValidPremiumSubscription(userId ?? "").Result; + ViewBag.IsAuthenticated = User.Identity?.IsAuthenticated ?? false; + ViewBag.UserName = User.Identity?.Name ?? ""; + + ViewBag.Title = _localizer["FAQPageTitle"]; + ViewBag.Description = _localizer["FAQPageDescription"]; + _adDisplayService.SetViewBagAds(ViewBag); + + return View(); + } + + public IActionResult HowToUse() + { + var userId = User?.FindFirst(ClaimTypes.NameIdentifier)?.Value; + + ViewBag.ShowAds = _adDisplayService.ShouldShowAds(userId).Result; + ViewBag.IsPremium = _adDisplayService.HasValidPremiumSubscription(userId ?? "").Result; + ViewBag.IsAuthenticated = User.Identity?.IsAuthenticated ?? false; + ViewBag.UserName = User.Identity?.Name ?? ""; + + ViewBag.Title = _localizer["HowToUsePageTitle"]; + ViewBag.Description = _localizer["HowToUsePageDescription"]; + _adDisplayService.SetViewBagAds(ViewBag); + + return View(); + } + + //[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] + //public IActionResult Error() + //{ + // return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier }); + //} + + [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] + public IActionResult Error() + { + var errorCode = Request.Query["code"].ToString(); + var errorMessage = ""; + + // Interpretar cdigos de erro especficos + if (errorCode.StartsWith("M.C506")) + { + errorMessage = "Erro de autenticao. Verifique suas credenciais e tente novamente."; + } + + ViewBag.ErrorCode = errorCode; + ViewBag.ErrorMessage = errorMessage; + + return View(new ErrorViewModel + { + RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier + }); + } + + // Dynamic QR redirect endpoint + [Route("d/{id}")] + public async Task DynamicRedirect(string id) + { + try + { + _adDisplayService.SetViewBagAds(ViewBag); + // This would lookup the dynamic QR content from cache/database + // For now, return a placeholder + return Redirect("https://qrrapido.site"); + } + catch + { + return NotFound(); + } + } + + // Health check endpoint + [Route("health")] + public IActionResult Health() + { + return Ok(new { status = "healthy", timestamp = DateTime.UtcNow }); + } + + // Sitemap endpoint for SEO + [Route("sitemap.xml")] + public IActionResult Sitemap() + { + var sitemap = $@" + + + https://qrrapido.site/ + {DateTime.UtcNow:yyyy-MM-dd} + daily + 1.0 + + + https://qrrapido.site/pt/ + {DateTime.UtcNow:yyyy-MM-dd} + daily + 0.9 + + + https://qrrapido.site/es/ + {DateTime.UtcNow:yyyy-MM-dd} + daily + 0.9 + + + https://qrrapido.site/pt-BR/About + {DateTime.UtcNow:yyyy-MM-dd} + monthly + 0.8 + + + https://qrrapido.site/es-PY/About + {DateTime.UtcNow:yyyy-MM-dd} + monthly + 0.8 + + + https://qrrapido.site/pt-BR/Contact + {DateTime.UtcNow:yyyy-MM-dd} + monthly + 0.8 + + + https://qrrapido.site/es-PY/Contact + {DateTime.UtcNow:yyyy-MM-dd} + monthly + 0.8 + + + https://qrrapido.site/pt-BR/FAQ + {DateTime.UtcNow:yyyy-MM-dd} + weekly + 0.9 + + + https://qrrapido.site/es-PY/FAQ + {DateTime.UtcNow:yyyy-MM-dd} + weekly + 0.9 + + + https://qrrapido.site/pt-BR/HowToUse + {DateTime.UtcNow:yyyy-MM-dd} + weekly + 0.8 + + + https://qrrapido.site/es-PY/HowToUse + {DateTime.UtcNow:yyyy-MM-dd} + weekly + 0.8 + + + https://qrrapido.site/Premium/Upgrade + {DateTime.UtcNow:yyyy-MM-dd} + weekly + 0.8 + + + https://qrrapido.site/privacy + {DateTime.UtcNow:yyyy-MM-dd} + monthly + 0.5 + + + https://qrrapido.site/terms + {DateTime.UtcNow:yyyy-MM-dd} + monthly + 0.5 + + "; + + return Content(sitemap, "application/xml"); + } + } + + //public class ErrorViewModel + //{ + // public string? RequestId { get; set; } + // public bool ShowRequestId => !string.IsNullOrEmpty(RequestId); + //} } \ No newline at end of file diff --git a/Controllers/PagamentoController.cs b/Controllers/PagamentoController.cs index bbb214d..b88935d 100644 --- a/Controllers/PagamentoController.cs +++ b/Controllers/PagamentoController.cs @@ -60,9 +60,9 @@ namespace QRRapidoApp.Controllers } var countryCode = GetUserCountryCode(); - if (countryCode != lang && languages.Contains(lang)) - { - countryCode = lang; + if (countryCode != lang && languages.Contains(lang)) + { + countryCode = lang; } var priceId = plan.PricesByCountry.ContainsKey(countryCode) @@ -136,7 +136,7 @@ namespace QRRapidoApp.Controllers private string GetUserCountryCode() { // Check current culture from URL first - var culture = HttpContext.Request.RouteValues["culture"]?.ToString() ?? + var culture = HttpContext.Request.RouteValues["culture"]?.ToString() ?? HttpContext.Features.Get()?.RequestCulture?.Culture?.Name; var countryMap = new Dictionary @@ -157,7 +157,7 @@ namespace QRRapidoApp.Controllers private string GetUserCountryCodeComplete() { // Check current culture from URL first - var culture = HttpContext.Request.RouteValues["culture"]?.ToString() ?? + var culture = HttpContext.Request.RouteValues["culture"]?.ToString() ?? HttpContext.Features.Get()?.RequestCulture?.Culture?.Name; if (languages.Contains(culture)) diff --git a/Controllers/QRController.cs b/Controllers/QRController.cs index ad32379..156d2fb 100644 --- a/Controllers/QRController.cs +++ b/Controllers/QRController.cs @@ -16,17 +16,17 @@ namespace QRRapidoApp.Controllers private readonly IUserService _userService; private readonly AdDisplayService _adService; private readonly ILogger _logger; - private readonly IStringLocalizer _localizer; - private readonly AdDisplayService _adDisplayService; - + private readonly IStringLocalizer _localizer; + private readonly AdDisplayService _adDisplayService; + public QRController(IQRCodeService qrService, IUserService userService, AdDisplayService adService, ILogger logger, IStringLocalizer localizer, AdDisplayService adDisplayService) { _qrService = qrService; _userService = userService; _adService = adService; _logger = logger; - _localizer = localizer; - _adDisplayService = adDisplayService; + _localizer = localizer; + _adDisplayService = adDisplayService; } [HttpPost("GenerateRapid")] @@ -36,7 +36,7 @@ namespace QRRapidoApp.Controllers var requestId = Guid.NewGuid().ToString("N")[..8]; var userId = User?.FindFirst(ClaimTypes.NameIdentifier)?.Value; var isAuthenticated = User?.Identity?.IsAuthenticated ?? false; - + using (_logger.BeginScope(new Dictionary { ["RequestId"] = requestId, @@ -468,7 +468,7 @@ namespace QRRapidoApp.Controllers public async Task GetHistory(int limit = 20) { try - { + { var userId = User?.FindFirst(ClaimTypes.NameIdentifier)?.Value; if (string.IsNullOrEmpty(userId)) { @@ -489,7 +489,7 @@ namespace QRRapidoApp.Controllers public async Task GetUserStats() { try - { + { var userId = User?.FindFirst(ClaimTypes.NameIdentifier)?.Value; if (string.IsNullOrEmpty(userId)) { diff --git a/Dockerfile b/Dockerfile index 79d0378..319a869 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,6 +2,10 @@ FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build WORKDIR /src +# Install Node.js for frontend build +RUN curl -fsSL https://deb.nodesource.com/setup_18.x | bash - \ + && apt-get install -y nodejs + # Copy csproj and restore as distinct layers COPY *.csproj ./ RUN dotnet restore --runtime linux-arm64 @@ -22,7 +26,7 @@ RUN dotnet publish "QRRapidoApp.csproj" -c Release -o /app/publish \ FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS final WORKDIR /app -# Install libgdiplus for System.Drawing (se necessrio para QR codes) +# Install libgdiplus for System.Drawing (se necess�rio para QR codes) RUN apt-get update && apt-get install -y libgdiplus && rm -rf /var/lib/apt/lists/* # Copy published app diff --git a/Models/ErrorViewModel.cs b/Models/ErrorViewModel.cs index b9315f7..1b788d6 100644 --- a/Models/ErrorViewModel.cs +++ b/Models/ErrorViewModel.cs @@ -1,11 +1,11 @@ -namespace QRRapidoApp.Models -{ - public class ErrorViewModel - { - public string? RequestId { get; set; } - public bool ShowRequestId => !string.IsNullOrEmpty(RequestId); - public string? ErrorCode { get; set; } - public string? ErrorMessage { get; set; } - } -} - +namespace QRRapidoApp.Models +{ + public class ErrorViewModel + { + public string? RequestId { get; set; } + public bool ShowRequestId => !string.IsNullOrEmpty(RequestId); + public string? ErrorCode { get; set; } + public string? ErrorMessage { get; set; } + } +} + diff --git a/Properties/launchSettings.json b/Properties/launchSettings.json index 4474b20..ac7db78 100644 --- a/Properties/launchSettings.json +++ b/Properties/launchSettings.json @@ -1,12 +1,12 @@ -{ - "profiles": { - "QRRapidoApp": { - "commandName": "Project", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - }, - "applicationUrl": "https://localhost:52428;http://192.168.0.85:52429;http://localhost:52429" - } - } +{ + "profiles": { + "QRRapidoApp": { + "commandName": "Project", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "applicationUrl": "https://localhost:52428;http://192.168.0.85:52429;http://localhost:52429" + } + } } \ No newline at end of file diff --git a/QRRapidoApp.sln b/QRRapidoApp.sln index efd3580..4f48f82 100644 --- a/QRRapidoApp.sln +++ b/QRRapidoApp.sln @@ -1,30 +1,30 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.13.35818.85 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "QRRapidoApp", "QRRapidoApp.csproj", "{8AF92774-40E8-830E-08B3-67F0A0B91DDE}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "RootFolder", "RootFolder", "{02EA681E-C7D8-13C7-8484-4AC65E1B71E8}" - ProjectSection(SolutionItems) = preProject - .github\workflows\deploy.yml = .github\workflows\deploy.yml - EndProjectSection -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {8AF92774-40E8-830E-08B3-67F0A0B91DDE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {8AF92774-40E8-830E-08B3-67F0A0B91DDE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {8AF92774-40E8-830E-08B3-67F0A0B91DDE}.Release|Any CPU.ActiveCfg = Release|Any CPU - {8AF92774-40E8-830E-08B3-67F0A0B91DDE}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {9E53D8E2-0957-4925-B347-404E3B14587B} - EndGlobalSection -EndGlobal + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.13.35818.85 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "QRRapidoApp", "QRRapidoApp.csproj", "{8AF92774-40E8-830E-08B3-67F0A0B91DDE}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "RootFolder", "RootFolder", "{02EA681E-C7D8-13C7-8484-4AC65E1B71E8}" + ProjectSection(SolutionItems) = preProject + .github\workflows\deploy.yml = .github\workflows\deploy.yml + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {8AF92774-40E8-830E-08B3-67F0A0B91DDE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8AF92774-40E8-830E-08B3-67F0A0B91DDE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8AF92774-40E8-830E-08B3-67F0A0B91DDE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8AF92774-40E8-830E-08B3-67F0A0B91DDE}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {9E53D8E2-0957-4925-B347-404E3B14587B} + EndGlobalSection +EndGlobal diff --git a/Resources/SharedResource.Designer.cs b/Resources/SharedResource.Designer.cs index 483c0fc..3003b19 100644 --- a/Resources/SharedResource.Designer.cs +++ b/Resources/SharedResource.Designer.cs @@ -1,720 +1,720 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Runtime Version:4.0.30319.42000 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace QRRapidoApp.Resources { - using System; - - - /// - /// A strongly-typed resource class, for looking up localized strings, etc. - /// - // This class was auto-generated by the StronglyTypedResourceBuilder - // class via a tool like ResGen or Visual Studio. - // To add or remove a member, edit your .ResX file then rerun ResGen - // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - public class SharedResource { - - private static global::System.Resources.ResourceManager resourceMan; - - private static global::System.Globalization.CultureInfo resourceCulture; - - [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - internal SharedResource() { - } - - /// - /// Returns the cached ResourceManager instance used by this class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - public static global::System.Resources.ResourceManager ResourceManager { - get { - if (object.ReferenceEquals(resourceMan, null)) { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("QRRapidoApp.Resources.SharedResource", typeof(SharedResource).Assembly); - resourceMan = temp; - } - return resourceMan; - } - } - - /// - /// Overrides the current thread's CurrentUICulture property for all - /// resource lookups using this strongly typed resource class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - public static global::System.Globalization.CultureInfo Culture { - get { - return resourceCulture; - } - set { - resourceCulture = value; - } - } - - /// - /// Looks up a localized string similar to Unlock all premium features!. - /// - public static string AdFreeOffer { - get { - return ResourceManager.GetString("AdFreeOffer", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Advanced Customization. - /// - public static string AdvancedCustomization { - get { - return ResourceManager.GetString("AdvancedCustomization", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Background Color. - /// - public static string BackgroundColor { - get { - return ResourceManager.GetString("BackgroundColor", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Back to generator. - /// - public static string BackToGenerator { - get { - return ResourceManager.GetString("BackToGenerator", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Classic. - /// - public static string Classic { - get { - return ResourceManager.GetString("Classic", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Classic. - /// - public static string ClassicStyle { - get { - return ResourceManager.GetString("ClassicStyle", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Colorful. - /// - public static string Colorful { - get { - return ResourceManager.GetString("Colorful", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Colorful. - /// - public static string ColorfulStyle { - get { - return ResourceManager.GetString("ColorfulStyle", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Content. - /// - public static string Content { - get { - return ResourceManager.GetString("Content", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Content hints. - /// - public static string ContentHints { - get { - return ResourceManager.GetString("ContentHints", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Enter your QR code content here.... - /// - public static string ContentPlaceholder { - get { - return ResourceManager.GetString("ContentPlaceholder", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Corner Style. - /// - public static string CornerStyle { - get { - return ResourceManager.GetString("CornerStyle", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Create QR Code Quickly. - /// - public static string CreateQRCodeQuickly { - get { - return ResourceManager.GetString("CreateQRCodeQuickly", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Download PDF. - /// - public static string DownloadPDF { - get { - return ResourceManager.GetString("DownloadPDF", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Download PNG. - /// - public static string DownloadPNG { - get { - return ResourceManager.GetString("DownloadPNG", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Download SVG (Vector). - /// - public static string DownloadSVG { - get { - return ResourceManager.GetString("DownloadSVG", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Dynamic QR (Premium). - /// - public static string DynamicQRPremium { - get { - return ResourceManager.GetString("DynamicQRPremium", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Dynamic QR (Premium). - /// - public static string DynamicType { - get { - return ResourceManager.GetString("DynamicType", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Email. - /// - public static string EmailType { - get { - return ResourceManager.GetString("EmailType", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Enter your QR code content here.... - /// - public static string EnterQRCodeContent { - get { - return ResourceManager.GetString("EnterQRCodeContent", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Generation error. Try again.. - /// - public static string Error { - get { - return ResourceManager.GetString("Error", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Fast generation!. - /// - public static string Fast { - get { - return ResourceManager.GetString("Fast", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Generated in. - /// - public static string GeneratedIn { - get { - return ResourceManager.GetString("GeneratedIn", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Generate QR Code. - /// - public static string GenerateQR { - get { - return ResourceManager.GetString("GenerateQR", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Generate QR Code Rapidly. - /// - public static string GenerateRapidly { - get { - return ResourceManager.GetString("GenerateRapidly", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Google. - /// - public static string Google { - get { - return ResourceManager.GetString("Google", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Login. - /// - public static string Login { - get { - return ResourceManager.GetString("Login", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Remove ads, access advanced customization and much more for only $12.90/month or $129.00/year. Login and subscribe now!. - /// - public static string LoginBenefits { - get { - return ResourceManager.GetString("LoginBenefits", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Login to save to history. - /// - public static string LoginToSave { - get { - return ResourceManager.GetString("LoginToSave", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Login with. - /// - public static string LoginWith { - get { - return ResourceManager.GetString("LoginWith", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Logo/Icon. - /// - public static string Logo { - get { - return ResourceManager.GetString("Logo", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Margin. - /// - public static string Margin { - get { - return ResourceManager.GetString("Margin", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Microsoft. - /// - public static string Microsoft { - get { - return ResourceManager.GetString("Microsoft", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Modern. - /// - public static string Modern { - get { - return ResourceManager.GetString("Modern", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Modern. - /// - public static string ModernStyle { - get { - return ResourceManager.GetString("ModernStyle", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to No Ads • History • Unlimited QR. - /// - public static string NoAdsHistoryUnlimitedQR { - get { - return ResourceManager.GetString("NoAdsHistoryUnlimitedQR", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Normal generation. - /// - public static string Normal { - get { - return ResourceManager.GetString("Normal", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Sem senha . - /// - public static string OpenNetwork { - get { - return ResourceManager.GetString("OpenNetwork", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to QR Rapido Premium. - /// - public static string PremiumTitle { - get { - return ResourceManager.GetString("PremiumTitle", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Premium User Active. - /// - public static string PremiumUserActive { - get { - return ResourceManager.GetString("PremiumUserActive", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Preview. - /// - public static string Preview { - get { - return ResourceManager.GetString("Preview", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Your QR code will appear here in seconds. - /// - public static string PreviewPlaceholder { - get { - return ResourceManager.GetString("PreviewPlaceholder", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Primary Color. - /// - public static string PrimaryColor { - get { - return ResourceManager.GetString("PrimaryColor", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Privacy Policy. - /// - public static string Privacy { - get { - return ResourceManager.GetString("Privacy", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to QR codes remaining. - /// - public static string QRCodesRemaining { - get { - return ResourceManager.GetString("QRCodesRemaining", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to QR Code Type. - /// - public static string QRCodeType { - get { - return ResourceManager.GetString("QRCodeType", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to QR Code Type. - /// - public static string QRType { - get { - return ResourceManager.GetString("QRType", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Quick Style. - /// - public static string QuickStyle { - get { - return ResourceManager.GetString("QuickStyle", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Save to History. - /// - public static string SaveToHistory { - get { - return ResourceManager.GetString("SaveToHistory", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to s. - /// - public static string Seconds { - get { - return ResourceManager.GetString("Seconds", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Select type. - /// - public static string SelectType { - get { - return ResourceManager.GetString("SelectType", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Simple Text. - /// - public static string SimpleText { - get { - return ResourceManager.GetString("SimpleText", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Size. - /// - public static string Size { - get { - return ResourceManager.GetString("Size", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to SMS. - /// - public static string SMSType { - get { - return ResourceManager.GetString("SMSType", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Premium Offer!. - /// - public static string SpecialOffer { - get { - return ResourceManager.GetString("SpecialOffer", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Short URLs generate faster. - /// - public static string SpeedTip1 { - get { - return ResourceManager.GetString("SpeedTip1", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Less text = higher speed. - /// - public static string SpeedTip2 { - get { - return ResourceManager.GetString("SpeedTip2", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Solid colors optimize the process. - /// - public static string SpeedTip3 { - get { - return ResourceManager.GetString("SpeedTip3", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Smaller sizes speed up downloads. - /// - public static string SpeedTip4 { - get { - return ResourceManager.GetString("SpeedTip4", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Tips for Faster QR. - /// - public static string SpeedTipsTitle { - get { - return ResourceManager.GetString("SpeedTipsTitle", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to QR Code saved to history!. - /// - public static string Success { - get { - return ResourceManager.GetString("Success", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Generate QR codes in seconds!. - /// - public static string Tagline { - get { - return ResourceManager.GetString("Tagline", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Plain Text. - /// - public static string TextType { - get { - return ResourceManager.GetString("TextType", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Ultra fast generation!. - /// - public static string UltraFast { - get { - return ResourceManager.GetString("UltraFast", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Ultra-fast generation guaranteed. - /// - public static string UltraFastGeneration { - get { - return ResourceManager.GetString("UltraFastGeneration", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Unlimited today. - /// - public static string UnlimitedToday { - get { - return ResourceManager.GetString("UnlimitedToday", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to URL/Link. - /// - public static string URLLink { - get { - return ResourceManager.GetString("URLLink", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to URL/Link. - /// - public static string URLType { - get { - return ResourceManager.GetString("URLType", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Business Card. - /// - public static string VCard { - get { - return ResourceManager.GetString("VCard", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Business Card. - /// - public static string VCardType { - get { - return ResourceManager.GetString("VCardType", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to WEP (muito antigo). - /// - public static string WEPLegacy { - get { - return ResourceManager.GetString("WEPLegacy", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to WiFi. - /// - public static string WiFiType { - get { - return ResourceManager.GetString("WiFiType", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Rede WPA (a mais comum). - /// - public static string WPARecommended { - get { - return ResourceManager.GetString("WPARecommended", resourceCulture); - } - } - } -} +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace QRRapidoApp.Resources { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + public class SharedResource { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal SharedResource() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + public static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("QRRapidoApp.Resources.SharedResource", typeof(SharedResource).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + public static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to Unlock all premium features!. + /// + public static string AdFreeOffer { + get { + return ResourceManager.GetString("AdFreeOffer", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Advanced Customization. + /// + public static string AdvancedCustomization { + get { + return ResourceManager.GetString("AdvancedCustomization", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Background Color. + /// + public static string BackgroundColor { + get { + return ResourceManager.GetString("BackgroundColor", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Back to generator. + /// + public static string BackToGenerator { + get { + return ResourceManager.GetString("BackToGenerator", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Classic. + /// + public static string Classic { + get { + return ResourceManager.GetString("Classic", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Classic. + /// + public static string ClassicStyle { + get { + return ResourceManager.GetString("ClassicStyle", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Colorful. + /// + public static string Colorful { + get { + return ResourceManager.GetString("Colorful", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Colorful. + /// + public static string ColorfulStyle { + get { + return ResourceManager.GetString("ColorfulStyle", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Content. + /// + public static string Content { + get { + return ResourceManager.GetString("Content", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Content hints. + /// + public static string ContentHints { + get { + return ResourceManager.GetString("ContentHints", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Enter your QR code content here.... + /// + public static string ContentPlaceholder { + get { + return ResourceManager.GetString("ContentPlaceholder", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Corner Style. + /// + public static string CornerStyle { + get { + return ResourceManager.GetString("CornerStyle", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Create QR Code Quickly. + /// + public static string CreateQRCodeQuickly { + get { + return ResourceManager.GetString("CreateQRCodeQuickly", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Download PDF. + /// + public static string DownloadPDF { + get { + return ResourceManager.GetString("DownloadPDF", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Download PNG. + /// + public static string DownloadPNG { + get { + return ResourceManager.GetString("DownloadPNG", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Download SVG (Vector). + /// + public static string DownloadSVG { + get { + return ResourceManager.GetString("DownloadSVG", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Dynamic QR (Premium). + /// + public static string DynamicQRPremium { + get { + return ResourceManager.GetString("DynamicQRPremium", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Dynamic QR (Premium). + /// + public static string DynamicType { + get { + return ResourceManager.GetString("DynamicType", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Email. + /// + public static string EmailType { + get { + return ResourceManager.GetString("EmailType", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Enter your QR code content here.... + /// + public static string EnterQRCodeContent { + get { + return ResourceManager.GetString("EnterQRCodeContent", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Generation error. Try again.. + /// + public static string Error { + get { + return ResourceManager.GetString("Error", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Fast generation!. + /// + public static string Fast { + get { + return ResourceManager.GetString("Fast", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Generated in. + /// + public static string GeneratedIn { + get { + return ResourceManager.GetString("GeneratedIn", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Generate QR Code. + /// + public static string GenerateQR { + get { + return ResourceManager.GetString("GenerateQR", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Generate QR Code Rapidly. + /// + public static string GenerateRapidly { + get { + return ResourceManager.GetString("GenerateRapidly", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Google. + /// + public static string Google { + get { + return ResourceManager.GetString("Google", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Login. + /// + public static string Login { + get { + return ResourceManager.GetString("Login", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Remove ads, access advanced customization and much more for only $12.90/month or $129.00/year. Login and subscribe now!. + /// + public static string LoginBenefits { + get { + return ResourceManager.GetString("LoginBenefits", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Login to save to history. + /// + public static string LoginToSave { + get { + return ResourceManager.GetString("LoginToSave", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Login with. + /// + public static string LoginWith { + get { + return ResourceManager.GetString("LoginWith", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Logo/Icon. + /// + public static string Logo { + get { + return ResourceManager.GetString("Logo", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Margin. + /// + public static string Margin { + get { + return ResourceManager.GetString("Margin", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Microsoft. + /// + public static string Microsoft { + get { + return ResourceManager.GetString("Microsoft", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Modern. + /// + public static string Modern { + get { + return ResourceManager.GetString("Modern", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Modern. + /// + public static string ModernStyle { + get { + return ResourceManager.GetString("ModernStyle", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to No Ads • History • Unlimited QR. + /// + public static string NoAdsHistoryUnlimitedQR { + get { + return ResourceManager.GetString("NoAdsHistoryUnlimitedQR", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Normal generation. + /// + public static string Normal { + get { + return ResourceManager.GetString("Normal", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Sem senha . + /// + public static string OpenNetwork { + get { + return ResourceManager.GetString("OpenNetwork", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to QR Rapido Premium. + /// + public static string PremiumTitle { + get { + return ResourceManager.GetString("PremiumTitle", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Premium User Active. + /// + public static string PremiumUserActive { + get { + return ResourceManager.GetString("PremiumUserActive", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Preview. + /// + public static string Preview { + get { + return ResourceManager.GetString("Preview", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Your QR code will appear here in seconds. + /// + public static string PreviewPlaceholder { + get { + return ResourceManager.GetString("PreviewPlaceholder", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Primary Color. + /// + public static string PrimaryColor { + get { + return ResourceManager.GetString("PrimaryColor", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Privacy Policy. + /// + public static string Privacy { + get { + return ResourceManager.GetString("Privacy", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to QR codes remaining. + /// + public static string QRCodesRemaining { + get { + return ResourceManager.GetString("QRCodesRemaining", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to QR Code Type. + /// + public static string QRCodeType { + get { + return ResourceManager.GetString("QRCodeType", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to QR Code Type. + /// + public static string QRType { + get { + return ResourceManager.GetString("QRType", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Quick Style. + /// + public static string QuickStyle { + get { + return ResourceManager.GetString("QuickStyle", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Save to History. + /// + public static string SaveToHistory { + get { + return ResourceManager.GetString("SaveToHistory", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to s. + /// + public static string Seconds { + get { + return ResourceManager.GetString("Seconds", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Select type. + /// + public static string SelectType { + get { + return ResourceManager.GetString("SelectType", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Simple Text. + /// + public static string SimpleText { + get { + return ResourceManager.GetString("SimpleText", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Size. + /// + public static string Size { + get { + return ResourceManager.GetString("Size", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to SMS. + /// + public static string SMSType { + get { + return ResourceManager.GetString("SMSType", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Premium Offer!. + /// + public static string SpecialOffer { + get { + return ResourceManager.GetString("SpecialOffer", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Short URLs generate faster. + /// + public static string SpeedTip1 { + get { + return ResourceManager.GetString("SpeedTip1", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Less text = higher speed. + /// + public static string SpeedTip2 { + get { + return ResourceManager.GetString("SpeedTip2", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Solid colors optimize the process. + /// + public static string SpeedTip3 { + get { + return ResourceManager.GetString("SpeedTip3", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Smaller sizes speed up downloads. + /// + public static string SpeedTip4 { + get { + return ResourceManager.GetString("SpeedTip4", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Tips for Faster QR. + /// + public static string SpeedTipsTitle { + get { + return ResourceManager.GetString("SpeedTipsTitle", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to QR Code saved to history!. + /// + public static string Success { + get { + return ResourceManager.GetString("Success", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Generate QR codes in seconds!. + /// + public static string Tagline { + get { + return ResourceManager.GetString("Tagline", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Plain Text. + /// + public static string TextType { + get { + return ResourceManager.GetString("TextType", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Ultra fast generation!. + /// + public static string UltraFast { + get { + return ResourceManager.GetString("UltraFast", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Ultra-fast generation guaranteed. + /// + public static string UltraFastGeneration { + get { + return ResourceManager.GetString("UltraFastGeneration", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Unlimited today. + /// + public static string UnlimitedToday { + get { + return ResourceManager.GetString("UnlimitedToday", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to URL/Link. + /// + public static string URLLink { + get { + return ResourceManager.GetString("URLLink", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to URL/Link. + /// + public static string URLType { + get { + return ResourceManager.GetString("URLType", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Business Card. + /// + public static string VCard { + get { + return ResourceManager.GetString("VCard", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Business Card. + /// + public static string VCardType { + get { + return ResourceManager.GetString("VCardType", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to WEP (muito antigo). + /// + public static string WEPLegacy { + get { + return ResourceManager.GetString("WEPLegacy", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to WiFi. + /// + public static string WiFiType { + get { + return ResourceManager.GetString("WiFiType", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Rede WPA (a mais comum). + /// + public static string WPARecommended { + get { + return ResourceManager.GetString("WPARecommended", resourceCulture); + } + } + } +} diff --git a/Resources/SharedResource.en.resx b/Resources/SharedResource.en.resx index 15dbffa..211f76b 100644 --- a/Resources/SharedResource.en.resx +++ b/Resources/SharedResource.en.resx @@ -1,786 +1,786 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - Generate QR codes in seconds! - - - Generate QR Code - - - QR Code Type - - - Content - - - URL/Link - - - Plain Text - - - WiFi - - - Business Card - - - SMS - - - Email - - - Dynamic QR (Premium) - - - Quick Style - - - Classic - - - Modern - - - Colorful - - - Enter your QR code content here... - - - Advanced Customization - - - Primary Color - - - Background Color - - - Size - - - Margin - - - Logo/Icon - - - Corner Style - - - Generate QR Code Rapidly - - - Preview - - - Your QR code will appear here in seconds - - - Ultra-fast generation guaranteed - - - Download PNG - - - Download SVG (Vector) - - - Download PDF - - - Save to History - - - Login to save to history - - - QR Rapido Premium - - - Tips for Faster QR - - - Short URLs generate faster - - - Less text = higher speed - - - Solid colors optimize the process - - - Smaller sizes speed up downloads - - - Login - - - Login with - - - Google - - - Microsoft - - - Unlock all premium features! - - - Premium Offer! - - - Remove ads, access detailed analytics and much more for only $12.90/month or $129.00/year. Login and subscribe now! - - - Privacy Policy - - - Back to generator - - - Generated in - - - s - - - Ultra fast generation! - - - Fast generation! - - - Normal generation - - - Generation error. Try again. - - - QR Code saved to history! - - - Create QR Code Quickly - - - Premium User Active - - - No Ads • History • Unlimited QR - - - Unlimited today - - - QR codes remaining - - - QR Code Type - - - Select type - - - URL/Link - - - Simple Text - - - Business Card - - - Dynamic QR (Premium) - - - Enter your QR code content here... - - - Content hints - - - Classic - - - Modern - - - Colorful - - - WPA netwrok (most commom) - - - WEP (muito antigo) - - - No password - - - Accelerate your productivity with the world's fastest QR generator - - - 3x faster than the competition - - - Current Status - - - You have - - - days remaining without ads. - - - Upgrade now and have premium access forever! - - - days remaining - - - The most popular plan - - - per month - - - Unlimited QR codes - - - Ultra-fast generation (0.4s) - - - No ads forever - - - Dynamic QR codes - - - Real-time analytics - - - Priority support - - - Developer API - - - Upgrade Now - - - Secure payment via Stripe - - - Cancel anytime - - - Plan Comparison - - - Feature - - - QR codes per day - - - Unlimited - - - Generation speed - - - Ads - - - No ads - - - Detailed analytics - - - Speed Demonstration - - - Competitors - - - Average time - - - 11x faster! - - - Frequently Asked Questions - - - Can I cancel anytime? - - - Yes! You can cancel your subscription at any time. There are no cancellation fees and you'll maintain premium access until the end of the already paid period. - - - What are dynamic QR codes? - - - Dynamic QR codes allow you to change the QR content after it has been created, without needing to generate a new code. Perfect for marketing campaigns and business use. - - - How does priority support work? - - - Premium users receive responses within 2 business hours via email, direct chat access and specialized technical support. - - - Payment processing error: - - - Payment processing error. Please try again. - - - Advertisement - - - ✨ Premium User - No ads! - - - Upgrade to Premium and remove ads! - - - Premium: No ads + History + Unlimited QR - - - Login with your account and get: - - - 30 days without ads - - - 50 QR codes/day - - - QR code history - - - Login with Google - - - Login with Microsoft - - - Special Offer! - - - By logging in, you automatically get - - - and can generate up to - - - for free. - - - We don't register you without your permission. - - - Privacy Policy - - - Payment successful! - - - Back to Home - - - Payment canceled. - - - View Plans - - - Unlock the Full Power of QRRapido - - - Unlimited access, no ads and exclusive features for maximum productivity. - - - Monthly Plan - - - Ideal to start exploring premium features. - - - Subscribe Now - - - Annual Plan - - - Recommended - - - /year - - - Save $ - - - Best value for frequent users. - - - Subscribe to Annual Plan - - - All plans include: - - - Redirecting... - - - An error occurred while initializing payment. Please try again. - - - Your generated QR codes are saved here for future download - - - Generate New QR Code - - - Type: - - - Created on: - - - Showing the 50 most recent QR codes. Older ones are automatically removed. - - - No QR Code found - - - When you generate QR codes while logged in, they will appear here for future download. - - - Generate First QR Code - - - Error regenerating QR Code. Please try again. - - - Small (200px) - - - Medium (300px) - - - Large (500px) - - - XL (800px) - - - Minimal - - - Large - - - Logo/Icon - - - PNG, JPG up to 2MB - - - Border Style - - - Square - - - Rounded - - - Circular - - - Leaf - - - Generate QR Code Quickly - - - Generating... - - - Availability - - - QRs generated today - - - Your QR code will appear here in seconds - - - Ultra-fast generation guaranteed - - - Download SVG (Vector) - - - Share QR Code - - - Share (System) - - - to save to history - - - Priority generation (0.4s) - - - Accelerate for $19.90/month - - - First, choose the QR Code type - - - 🌐 To generate QR for URL, enter the complete address (ex: https://google.com) - - - 👤 For business card, fill in name, phone and email in the fields below - - - 📶 For WiFi, enter network name, password and security type - - - 💬 For SMS, enter recipient number and message - - - 📧 For email, fill in recipient, subject and message (optional) - - - 📝 For free text, enter any content you want - - - Tips for Faster QR - - - Short URLs generate faster - - - Less text = higher speed - - - Solid colors optimize the process - - - Smaller sizes accelerate download - - - Why is QR Rapido faster? - - - Comparison with other popular generators - - - Optimized for speed - - - Competitor A - - - Traditional generator - - - Competitor B - - - Heavy interface - - - Competitor C - - - Many ads - - - average - - - Generate QR Code - - - Profile - - - History - - - Premium Active - - - Logout - - - Login = 30 days without ads! - - - The fastest QR generator on the web - - - Average of - - - 1.2 seconds - - - per QR code • Free • No registration required - - - The fastest QR code generator on the web. Free, secure and reliable. - - - Useful Links - - - Terms of Use - - - Support - - - Help - - - All rights reserved. - - - QR codes remaining today - - - Daily limit reached! Login for unlimited access. - - - Anonymous users: 3 QR codes per day - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Generate QR codes in seconds! + + + Generate QR Code + + + QR Code Type + + + Content + + + URL/Link + + + Plain Text + + + WiFi + + + Business Card + + + SMS + + + Email + + + Dynamic QR (Premium) + + + Quick Style + + + Classic + + + Modern + + + Colorful + + + Enter your QR code content here... + + + Advanced Customization + + + Primary Color + + + Background Color + + + Size + + + Margin + + + Logo/Icon + + + Corner Style + + + Generate QR Code Rapidly + + + Preview + + + Your QR code will appear here in seconds + + + Ultra-fast generation guaranteed + + + Download PNG + + + Download SVG (Vector) + + + Download PDF + + + Save to History + + + Login to save to history + + + QR Rapido Premium + + + Tips for Faster QR + + + Short URLs generate faster + + + Less text = higher speed + + + Solid colors optimize the process + + + Smaller sizes speed up downloads + + + Login + + + Login with + + + Google + + + Microsoft + + + Unlock all premium features! + + + Premium Offer! + + + Remove ads, access detailed analytics and much more for only $12.90/month or $129.00/year. Login and subscribe now! + + + Privacy Policy + + + Back to generator + + + Generated in + + + s + + + Ultra fast generation! + + + Fast generation! + + + Normal generation + + + Generation error. Try again. + + + QR Code saved to history! + + + Create QR Code Quickly + + + Premium User Active + + + No Ads • History • Unlimited QR + + + Unlimited today + + + QR codes remaining + + + QR Code Type + + + Select type + + + URL/Link + + + Simple Text + + + Business Card + + + Dynamic QR (Premium) + + + Enter your QR code content here... + + + Content hints + + + Classic + + + Modern + + + Colorful + + + WPA netwrok (most commom) + + + WEP (muito antigo) + + + No password + + + Accelerate your productivity with the world's fastest QR generator + + + 3x faster than the competition + + + Current Status + + + You have + + + days remaining without ads. + + + Upgrade now and have premium access forever! + + + days remaining + + + The most popular plan + + + per month + + + Unlimited QR codes + + + Ultra-fast generation (0.4s) + + + No ads forever + + + Dynamic QR codes + + + Real-time analytics + + + Priority support + + + Developer API + + + Upgrade Now + + + Secure payment via Stripe + + + Cancel anytime + + + Plan Comparison + + + Feature + + + QR codes per day + + + Unlimited + + + Generation speed + + + Ads + + + No ads + + + Detailed analytics + + + Speed Demonstration + + + Competitors + + + Average time + + + 11x faster! + + + Frequently Asked Questions + + + Can I cancel anytime? + + + Yes! You can cancel your subscription at any time. There are no cancellation fees and you'll maintain premium access until the end of the already paid period. + + + What are dynamic QR codes? + + + Dynamic QR codes allow you to change the QR content after it has been created, without needing to generate a new code. Perfect for marketing campaigns and business use. + + + How does priority support work? + + + Premium users receive responses within 2 business hours via email, direct chat access and specialized technical support. + + + Payment processing error: + + + Payment processing error. Please try again. + + + Advertisement + + + ✨ Premium User - No ads! + + + Upgrade to Premium and remove ads! + + + Premium: No ads + History + Unlimited QR + + + Login with your account and get: + + + 30 days without ads + + + 50 QR codes/day + + + QR code history + + + Login with Google + + + Login with Microsoft + + + Special Offer! + + + By logging in, you automatically get + + + and can generate up to + + + for free. + + + We don't register you without your permission. + + + Privacy Policy + + + Payment successful! + + + Back to Home + + + Payment canceled. + + + View Plans + + + Unlock the Full Power of QRRapido + + + Unlimited access, no ads and exclusive features for maximum productivity. + + + Monthly Plan + + + Ideal to start exploring premium features. + + + Subscribe Now + + + Annual Plan + + + Recommended + + + /year + + + Save $ + + + Best value for frequent users. + + + Subscribe to Annual Plan + + + All plans include: + + + Redirecting... + + + An error occurred while initializing payment. Please try again. + + + Your generated QR codes are saved here for future download + + + Generate New QR Code + + + Type: + + + Created on: + + + Showing the 50 most recent QR codes. Older ones are automatically removed. + + + No QR Code found + + + When you generate QR codes while logged in, they will appear here for future download. + + + Generate First QR Code + + + Error regenerating QR Code. Please try again. + + + Small (200px) + + + Medium (300px) + + + Large (500px) + + + XL (800px) + + + Minimal + + + Large + + + Logo/Icon + + + PNG, JPG up to 2MB + + + Border Style + + + Square + + + Rounded + + + Circular + + + Leaf + + + Generate QR Code Quickly + + + Generating... + + + Availability + + + QRs generated today + + + Your QR code will appear here in seconds + + + Ultra-fast generation guaranteed + + + Download SVG (Vector) + + + Share QR Code + + + Share (System) + + + to save to history + + + Priority generation (0.4s) + + + Accelerate for $19.90/month + + + First, choose the QR Code type + + + 🌐 To generate QR for URL, enter the complete address (ex: https://google.com) + + + 👤 For business card, fill in name, phone and email in the fields below + + + 📶 For WiFi, enter network name, password and security type + + + 💬 For SMS, enter recipient number and message + + + 📧 For email, fill in recipient, subject and message (optional) + + + 📝 For free text, enter any content you want + + + Tips for Faster QR + + + Short URLs generate faster + + + Less text = higher speed + + + Solid colors optimize the process + + + Smaller sizes accelerate download + + + Why is QR Rapido faster? + + + Comparison with other popular generators + + + Optimized for speed + + + Competitor A + + + Traditional generator + + + Competitor B + + + Heavy interface + + + Competitor C + + + Many ads + + + average + + + Generate QR Code + + + Profile + + + History + + + Premium Active + + + Logout + + + Login = 30 days without ads! + + + The fastest QR generator on the web + + + Average of + + + 1.2 seconds + + + per QR code • Free • No registration required + + + The fastest QR code generator on the web. Free, secure and reliable. + + + Useful Links + + + Terms of Use + + + Support + + + Help + + + All rights reserved. + + + QR codes remaining today + + + Daily limit reached! Login for unlimited access. + + + Anonymous users: 3 QR codes per day + \ No newline at end of file diff --git a/Resources/SharedResource.es.resx b/Resources/SharedResource.es.resx index a4302cd..c31e091 100644 --- a/Resources/SharedResource.es.resx +++ b/Resources/SharedResource.es.resx @@ -1,973 +1,973 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - ¡Japo código QR pya'épe! (Genera códigos QR en segundos) - - - Japo Código QR (Generar) - - - Mba'éichapa código QR (Tipo) - - - Mba'e oĩva (Contenido) - - - URL/Enlace - - - Texto Simple - - - WiFi ñanduti - - - Tarjeta mba'eichaguáva (de Visita) - - - SMS - - - Email - - - QR Dinamico (Premium) - - - Estilo pya'e (Rápido) - - - Ymave (Clásico) - - - Ko'ãgagua (Moderno) - - - Sa'ygua (Colorido) - - - Emoĩ ne código QR mba'e ko'ápe... (Ingrese el contenido aquí) - - - Ñemohenda porãve (Personalización Avanzada) - - - Sa'y tuichavéva (Color Principal) - - - Sa'y hugua (Color de Fondo) - - - Tuicha (Tamaño) - - - Opa'ĩ (Margen) - - - Ta'anga/Ícono - - - Estilo de Esquinas - - - Japo Código QR pya'épe (Generar Rápidamente) - - - Hecha tenondegua (Vista Previa) - - - Ne código QR ojekuaa ko'ápe segundos pype (aparecerá aquí en segundos) - - - Generacion ultra-rapida garantizada - - - Descargar PNG - - - Descargar SVG (Vectorial) - - - Descargar PDF - - - Guardar en Historial - - - Inicie sesion para guardar en el historial - - - QR Rápido Premium (Porã) - - - Ñe'ẽ pya'épe (Consejos para QR Más Rápidos) - - - URLs cortas se generan mas rapido - - - Menos texto = mayor velocidad - - - Colores solidos optimizan el proceso - - - Tamanos menores aceleran la descarga - - - Iniciar Sesion - - - Iniciar con - - - Google - - - Microsoft - - - Desbloquea todas las funciones premium! - - - Jeporu porã! (Oferta Premium!) - - - Elimina anuncios, accede a analisis detallados y mucho mas por solo $12.90/mes o $129.00/ano. Inicia sesion y suscribete ahora! - - - Politica de Privacidad - - - Volver al generador - - - Generado en - - - s - - - Generacion ultra rapida! - - - Generacion rapida! - - - Generacion normal - - - Osẽ vai - Ha'e jéy (Error - Inténtalo de nuevo) - - - Ñongatu porã! Código QR guardado en el historial! - - - Crear Codigo QR Rapidamente - - - Usuario Premium Activo - - - Sin Anuncios • Historial • QR Ilimitado - - - Ilimitado hoy - - - Codigos QR restantes - - - Tipo de Codigo QR - - - Seleccionar tipo - - - URL/Enlace - - - Texto Simple - - - Tarjeta de Visita - - - QR Dinamico (Premium) - - - Ingrese el contenido de su codigo QR aqui... - - - Sugerencias de contenido - - - Clasico - - - Moderno - - - Colorido - - - Rede WPA (a mais comum) - - - WEP (muito antigo) - - - Sin contrasenha - - - Acelera tu productividad con el generador de QR más rápido del mundo - - - 3x más rápido que la competencia - - - Estado Actual - - - Tienes - - - días restantes sin anuncios. - - - ¡Actualiza ahora y ten acceso premium para siempre! - - - días restantes - - - El plan más popular - - - por mes - - - Códigos QR ilimitados - - - Generación ultra-rápida (0.4s) - - - Sin anuncios para siempre - - - Códigos QR dinámicos - - - Analítica en tiempo real - - - Soporte prioritario - - - API para desarrolladores - - - Actualizar Ahora - - - Pago seguro via Stripe - - - Cancela cuando quieras - - - Comparación de Planes - - - Función - - - Códigos QR por día - - - Ilimitado - - - Velocidad de generación - - - Anuncios - - - Sin anuncios - - - Analítica detallada - - - Demostración de Velocidad - - - Competidores - - - Tiempo promedio - - - ¡11x más rápido! - - - Preguntas Frecuentes - - - ¿Puedo cancelar en cualquier momento? - - - ¡Sí! Puedes cancelar tu suscripción en cualquier momento. No hay tarifas de cancelación y mantendrás el acceso premium hasta el final del período ya pagado. - - - ¿Qué son los códigos QR dinámicos? - - - Los códigos QR dinámicos te permiten cambiar el contenido del QR después de que haya sido creado, sin necesidad de generar un nuevo código. Perfecto para campañas de marketing y uso empresarial. - - - ¿Cómo funciona el soporte prioritario? - - - Los usuarios premium reciben respuesta en hasta 2 horas hábiles por email, acceso al chat directo y soporte técnico especializado. - - - Error al procesar el pago: - - - Error al procesar el pago. Inténtalo de nuevo. - - - Publicidad - - - ✨ Usuario Premium - ¡Sin anuncios! - - - ¡Actualiza a Premium y elimina los anuncios! - - - Premium: Sin anuncios + Historial + QR ilimitados - - - Inicia sesión con tu cuenta y obtén: - - - 30 días sin anuncios - - - 50 códigos QR/día - - - Historial de códigos QR - - - Iniciar con Google - - - Iniciar con Microsoft - - - ¡Oferta Especial! - - - Al iniciar sesión, obtienes automáticamente - - - y puedes generar hasta - - - gratuitamente. - - - No te registramos sin tu permiso. - - - Política de Privacidad - - - ¡Jehepy porã! (Pago exitoso!) - - - Volver al Inicio - - - Pago cancelado. - - - Ver Planes - - - Desbloquea el Poder Total de QRRápido - - - Acceso sin límites, sin anuncios y con recursos exclusivos para máxima productividad. - - - Plan Mensual - - - Ideal para comenzar a explorar las funciones premium. - - - Suscribirse Ahora - - - Plan Anual - - - Recomendado - - - /año - - - Ahorra $ - - - La mejor relación calidad-precio para usuarios frecuentes. - - - Suscribirse al Plan Anual - - - Todos los planes incluyen: - - - Redirigiendo... - - - Ocurrió un error al iniciar el pago. Inténtalo de nuevo. - - - Tus códigos QR generados se guardan aquí para descarga futura - - - Generar Nuevo Código QR - - - Tipo: - - - Creado el: - - - Mostrando los 50 códigos QR más recientes. Los más antiguos se eliminan automáticamente. - - - Ningún Código QR encontrado - - - Cuando generes códigos QR estando conectado, aparecerán aquí para descarga futura. - - - Generar Primer Código QR - - - Error al regenerar el Código QR. Inténtalo de nuevo. - - - Pequeño (200px) - - - Mediano (300px) - - - Grande (500px) - - - XL (800px) - - - Mínimo - - - Grande - - - Logo/Icono - - - PNG, JPG hasta 2MB - - - Estilo de Bordes - - - Cuadrado - - - Redondeado - - - Circular - - - Hoja - - - Generar Código QR Rápidamente - - - Generando... - - - Disponibilidad - - - QRs generados hoy - - - Tu código QR aparecerá aquí en segundos - - - Generación ultra-rápida garantizada - - - Descargar SVG (Vectorial) - - - Compartir Código QR - - - Compartir (Sistema) - - - para guardar en el historial - - - Generación prioritaria (0.4s) - - - Acelerar por $19.90/mes - - - Primero, elige el tipo de código QR - - - 🌐 Para generar QR de URL, ingresa la dirección completa (ej: https://google.com) - - - 👤 Para tarjeta de visita, completa nombre, teléfono y email en los campos - - - 📶 Para WiFi, ingresa nombre de red, contraseña y tipo de seguridad - - - 💬 Para SMS, ingresa el número del destinatario y el mensaje - - - 📧 Para email, completa destinatario, asunto y mensaje (opcional) - - - 📝 Para texto libre, ingresa cualquier contenido que desees - - - Consejos para QR Más Rápidos - - - URLs cortas se generan más rápido - - - Menos texto = mayor velocidad - - - Colores sólidos optimizan el proceso - - - Tamaños menores aceleran la descarga - - - Mba'ére QR Rápido pya'eve? (¿Por qué es más rápido?) - - - Comparación con otros generadores populares - - - Optimizado para velocidad - - - Competidor A - - - Generador tradicional - - - Competidor B - - - Interfaz pesada - - - Competidor C - - - Muchos anuncios - - - promedio - - - Generar Código QR - - - Perfil - - - Historial - - - Premium hendyva (Activo) - - - Salir - - - ¡Iniciar sesión = 30 días sin anuncios! - - - El generador de QR más rápido de la web - - - Promedio de - - - 1.2 segundos - - - por código QR • Rei rehe (Gratis) • Sin registro obligatorio - - - El generador de códigos QR pya'eve (ás rápido) de la web. Rei rehe (Gratis), seguro y confiable. - - - Enlaces Útiles - - - Términos de Uso - - - Soporte - - - Ayuda - - - Todos los derechos reservados. - - - Códigos QR restantes hoy - - - ¡Límite diario alcanzado! Inicia sesión para acceso ilimitado. - - - Usuarios anónimos: 3 códigos QR por día - - - Política de Privacidad - QR Rapido - - - Conocé nuestra política de privacidad y cómo protegemos tus datos personales en QR Rapido. - - - Última actualización - - - Esta Política de Privacidad describe cómo QR Rapido ("nosotros", "nuestro" o "nuestra") recolecta, usa y protege la información que proporcionás cuando usás nuestro servicio de generación de códigos QR. - - - 1. Recolección de Datos - - - Recolectamos la siguiente información: - -• Datos de autenticación vía Google/Microsoft (nombre, email, foto de perfil) -• Contenido de los códigos QR generados (solo para usuarios registrados que eligen guardar en el historial) -• Datos de uso y estadísticas anónimas para mejorar nuestro servicio -• Información de pago procesada vía Stripe (no almacenamos datos de tarjeta) - - - 2. Uso de los Datos - - - Utilizamos tus datos para: - -• Proporcionar y mejorar nuestro servicio de generación de códigos QR -• Mantener tu historial de códigos QR (solo si elegís guardarlos) -• Procesar pagos de suscripciones premium -• Enviar comunicaciones relacionadas al servicio -• Cumplir obligaciones legales conforme a las leyes de protección de datos - - - 3. Protección de los Datos - - - Implementamos medidas de seguridad técnicas y organizacionales adecuadas para proteger tus datos personales contra acceso no autorizado, alteración, divulgación o destrucción, incluyendo: - -• Cifrado SSL/TLS en todas las comunicaciones -• Almacenamiento seguro en servidores protegidos -• Control de acceso restringido a los datos -• Monitoreo continuo de seguridad - - - 4. Compartir Datos - - - No vendemos, alquilamos ni compartimos tus datos personales con terceros, excepto: - -• Con proveedores de servicios esenciales (Google, Microsoft, Stripe) para autenticación y pagos -• Cuando sea requerido por ley u orden judicial -• Para proteger nuestros derechos legales y la seguridad de los usuarios - - - 5. Tus Derechos - - - De acuerdo con las leyes de protección de datos, tenés derecho a: - -• Acceder a tus datos personales -• Corregir datos incompletos o incorrectos -• Solicitar la eliminación de datos innecesarios -• Revocar consentimiento para tratamiento de datos -• Portabilidad de tus datos - - - 6. Cookies y Tecnologías Similares - - - Utilizamos cookies esenciales para el funcionamiento del sitio y análisis de tráfico vía Google Analytics. Podés gestionar tus preferencias de cookies en la configuración de tu navegador. - - - 7. Contacto - - - Para consultas sobre esta Política de Privacidad o ejercer tus derechos, contactanos a través del email: contato@qrrapido.site - - - Términos de Uso - QR Rapido - - - Leé los términos y condiciones de uso de QR Rapido, el generador de códigos QR más rápido de América Latina. - - - Última actualización - - - Estos Términos de Uso ("Términos") rigen el uso del servicio QR Rapido ("Servicio") proporcionado por nuestra empresa ("nosotros", "nuestro" o "nuestra"). Al usar nuestro Servicio, aceptás estos Términos. - - - 1. Aceptación de los Términos - - - Al acceder y usar QR Rapido, declarás tener al menos 18 años de edad o poseer autorización de padres/responsables, y aceptás cumplir estos Términos y todas las leyes aplicables. - - - 2. Descripción del Servicio - - - QR Rapido es un servicio online gratuito de generación de códigos QR que ofrece: - -• Generación rápida de códigos QR para diversos tipos de contenido -• Versión gratuita con limitaciones diarias -• Plan Premium con recursos avanzados e ilimitados -• Historial de códigos para usuarios registrados - - - 3. Responsabilidades del Usuario - - - Te comprometés a: - -• Usar el servicio únicamente para fines legales y éticos -• No generar códigos QR con contenido ofensivo, ilegal o que infrinja derechos de terceros -• No intentar comprometer la seguridad o disponibilidad del servicio -• Mantener la confidencialidad de tus credenciales de acceso - - - 4. Usos Prohibidos - - - Está prohibido usar el servicio para: - -• Diseminar malware, phishing o enlaces maliciosos -• Violación de derechos de autor o propiedad intelectual -• Spam, acoso o actividades fraudulentas -• Actividades que violen leyes nacionales o internacionales - - - 5. Propiedad Intelectual - - - Todos los derechos sobre el software, diseño, marca y contenido de QR Rapido pertenecen a nuestra empresa. Los códigos QR generados son de tu propiedad, pero nos concedés licencia para almacenarlos según sea necesario para proporcionar el servicio. - - - 6. Limitaciones del Servicio - - - • Usuarios gratuitos tienen límite diario de códigos QR -• Nos reservamos el derecho de modificar o interrumpir el servicio temporalmente -• No garantizamos disponibilidad 100% del tiempo -• Podemos implementar medidas técnicas para prevenir abuso - - - 7. Pagos y Suscripciones - - - • Suscripciones Premium son procesadas vía Stripe -• Pagos son recurrentes hasta cancelación -• Reembolsos siguen nuestra política de 7 días -• Precios pueden ser alterados mediante aviso previo de 30 días - - - 8. Limitación de Responsabilidad - - - El servicio se proporciona "como está". No nos responsabilizamos por: - -• Daños directos o indirectos derivados del uso del servicio -• Pérdida de datos o interrupción de negocios -• Uso inadecuado de los códigos QR generados por terceros -• Contenido accedido a través de los códigos QR - - - 9. Modificaciones de los Términos - - - Podemos actualizar estos Términos periódicamente. Cambios significativos serán comunicados por email o aviso en el sitio. El uso continuado después de los cambios constituye aceptación de los nuevos Términos. - - - 10. Ley Aplicable - - - Estos Términos se rigen por las leyes aplicables en cada jurisdicción. Las disputas serán resueltas en los tribunales competentes de cada país, sin perjuicio a tu derecho de acceso a los órganos de defensa del consumidor. - - - 11. Contacto - - - Para dudas sobre estos Términos de Uso, contactanos: contato@qrrapido.site - - - Volver al Inicio - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + ¡Japo código QR pya'épe! (Genera códigos QR en segundos) + + + Japo Código QR (Generar) + + + Mba'éichapa código QR (Tipo) + + + Mba'e oĩva (Contenido) + + + URL/Enlace + + + Texto Simple + + + WiFi ñanduti + + + Tarjeta mba'eichaguáva (de Visita) + + + SMS + + + Email + + + QR Dinamico (Premium) + + + Estilo pya'e (Rápido) + + + Ymave (Clásico) + + + Ko'ãgagua (Moderno) + + + Sa'ygua (Colorido) + + + Emoĩ ne código QR mba'e ko'ápe... (Ingrese el contenido aquí) + + + Ñemohenda porãve (Personalización Avanzada) + + + Sa'y tuichavéva (Color Principal) + + + Sa'y hugua (Color de Fondo) + + + Tuicha (Tamaño) + + + Opa'ĩ (Margen) + + + Ta'anga/Ícono + + + Estilo de Esquinas + + + Japo Código QR pya'épe (Generar Rápidamente) + + + Hecha tenondegua (Vista Previa) + + + Ne código QR ojekuaa ko'ápe segundos pype (aparecerá aquí en segundos) + + + Generacion ultra-rapida garantizada + + + Descargar PNG + + + Descargar SVG (Vectorial) + + + Descargar PDF + + + Guardar en Historial + + + Inicie sesion para guardar en el historial + + + QR Rápido Premium (Porã) + + + Ñe'ẽ pya'épe (Consejos para QR Más Rápidos) + + + URLs cortas se generan mas rapido + + + Menos texto = mayor velocidad + + + Colores solidos optimizan el proceso + + + Tamanos menores aceleran la descarga + + + Iniciar Sesion + + + Iniciar con + + + Google + + + Microsoft + + + Desbloquea todas las funciones premium! + + + Jeporu porã! (Oferta Premium!) + + + Elimina anuncios, accede a analisis detallados y mucho mas por solo $12.90/mes o $129.00/ano. Inicia sesion y suscribete ahora! + + + Politica de Privacidad + + + Volver al generador + + + Generado en + + + s + + + Generacion ultra rapida! + + + Generacion rapida! + + + Generacion normal + + + Osẽ vai - Ha'e jéy (Error - Inténtalo de nuevo) + + + Ñongatu porã! Código QR guardado en el historial! + + + Crear Codigo QR Rapidamente + + + Usuario Premium Activo + + + Sin Anuncios • Historial • QR Ilimitado + + + Ilimitado hoy + + + Codigos QR restantes + + + Tipo de Codigo QR + + + Seleccionar tipo + + + URL/Enlace + + + Texto Simple + + + Tarjeta de Visita + + + QR Dinamico (Premium) + + + Ingrese el contenido de su codigo QR aqui... + + + Sugerencias de contenido + + + Clasico + + + Moderno + + + Colorido + + + Rede WPA (a mais comum) + + + WEP (muito antigo) + + + Sin contrasenha + + + Acelera tu productividad con el generador de QR más rápido del mundo + + + 3x más rápido que la competencia + + + Estado Actual + + + Tienes + + + días restantes sin anuncios. + + + ¡Actualiza ahora y ten acceso premium para siempre! + + + días restantes + + + El plan más popular + + + por mes + + + Códigos QR ilimitados + + + Generación ultra-rápida (0.4s) + + + Sin anuncios para siempre + + + Códigos QR dinámicos + + + Analítica en tiempo real + + + Soporte prioritario + + + API para desarrolladores + + + Actualizar Ahora + + + Pago seguro via Stripe + + + Cancela cuando quieras + + + Comparación de Planes + + + Función + + + Códigos QR por día + + + Ilimitado + + + Velocidad de generación + + + Anuncios + + + Sin anuncios + + + Analítica detallada + + + Demostración de Velocidad + + + Competidores + + + Tiempo promedio + + + ¡11x más rápido! + + + Preguntas Frecuentes + + + ¿Puedo cancelar en cualquier momento? + + + ¡Sí! Puedes cancelar tu suscripción en cualquier momento. No hay tarifas de cancelación y mantendrás el acceso premium hasta el final del período ya pagado. + + + ¿Qué son los códigos QR dinámicos? + + + Los códigos QR dinámicos te permiten cambiar el contenido del QR después de que haya sido creado, sin necesidad de generar un nuevo código. Perfecto para campañas de marketing y uso empresarial. + + + ¿Cómo funciona el soporte prioritario? + + + Los usuarios premium reciben respuesta en hasta 2 horas hábiles por email, acceso al chat directo y soporte técnico especializado. + + + Error al procesar el pago: + + + Error al procesar el pago. Inténtalo de nuevo. + + + Publicidad + + + ✨ Usuario Premium - ¡Sin anuncios! + + + ¡Actualiza a Premium y elimina los anuncios! + + + Premium: Sin anuncios + Historial + QR ilimitados + + + Inicia sesión con tu cuenta y obtén: + + + 30 días sin anuncios + + + 50 códigos QR/día + + + Historial de códigos QR + + + Iniciar con Google + + + Iniciar con Microsoft + + + ¡Oferta Especial! + + + Al iniciar sesión, obtienes automáticamente + + + y puedes generar hasta + + + gratuitamente. + + + No te registramos sin tu permiso. + + + Política de Privacidad + + + ¡Jehepy porã! (Pago exitoso!) + + + Volver al Inicio + + + Pago cancelado. + + + Ver Planes + + + Desbloquea el Poder Total de QRRápido + + + Acceso sin límites, sin anuncios y con recursos exclusivos para máxima productividad. + + + Plan Mensual + + + Ideal para comenzar a explorar las funciones premium. + + + Suscribirse Ahora + + + Plan Anual + + + Recomendado + + + /año + + + Ahorra $ + + + La mejor relación calidad-precio para usuarios frecuentes. + + + Suscribirse al Plan Anual + + + Todos los planes incluyen: + + + Redirigiendo... + + + Ocurrió un error al iniciar el pago. Inténtalo de nuevo. + + + Tus códigos QR generados se guardan aquí para descarga futura + + + Generar Nuevo Código QR + + + Tipo: + + + Creado el: + + + Mostrando los 50 códigos QR más recientes. Los más antiguos se eliminan automáticamente. + + + Ningún Código QR encontrado + + + Cuando generes códigos QR estando conectado, aparecerán aquí para descarga futura. + + + Generar Primer Código QR + + + Error al regenerar el Código QR. Inténtalo de nuevo. + + + Pequeño (200px) + + + Mediano (300px) + + + Grande (500px) + + + XL (800px) + + + Mínimo + + + Grande + + + Logo/Icono + + + PNG, JPG hasta 2MB + + + Estilo de Bordes + + + Cuadrado + + + Redondeado + + + Circular + + + Hoja + + + Generar Código QR Rápidamente + + + Generando... + + + Disponibilidad + + + QRs generados hoy + + + Tu código QR aparecerá aquí en segundos + + + Generación ultra-rápida garantizada + + + Descargar SVG (Vectorial) + + + Compartir Código QR + + + Compartir (Sistema) + + + para guardar en el historial + + + Generación prioritaria (0.4s) + + + Acelerar por $19.90/mes + + + Primero, elige el tipo de código QR + + + 🌐 Para generar QR de URL, ingresa la dirección completa (ej: https://google.com) + + + 👤 Para tarjeta de visita, completa nombre, teléfono y email en los campos + + + 📶 Para WiFi, ingresa nombre de red, contraseña y tipo de seguridad + + + 💬 Para SMS, ingresa el número del destinatario y el mensaje + + + 📧 Para email, completa destinatario, asunto y mensaje (opcional) + + + 📝 Para texto libre, ingresa cualquier contenido que desees + + + Consejos para QR Más Rápidos + + + URLs cortas se generan más rápido + + + Menos texto = mayor velocidad + + + Colores sólidos optimizan el proceso + + + Tamaños menores aceleran la descarga + + + Mba'ére QR Rápido pya'eve? (¿Por qué es más rápido?) + + + Comparación con otros generadores populares + + + Optimizado para velocidad + + + Competidor A + + + Generador tradicional + + + Competidor B + + + Interfaz pesada + + + Competidor C + + + Muchos anuncios + + + promedio + + + Generar Código QR + + + Perfil + + + Historial + + + Premium hendyva (Activo) + + + Salir + + + ¡Iniciar sesión = 30 días sin anuncios! + + + El generador de QR más rápido de la web + + + Promedio de + + + 1.2 segundos + + + por código QR • Rei rehe (Gratis) • Sin registro obligatorio + + + El generador de códigos QR pya'eve (ás rápido) de la web. Rei rehe (Gratis), seguro y confiable. + + + Enlaces Útiles + + + Términos de Uso + + + Soporte + + + Ayuda + + + Todos los derechos reservados. + + + Códigos QR restantes hoy + + + ¡Límite diario alcanzado! Inicia sesión para acceso ilimitado. + + + Usuarios anónimos: 3 códigos QR por día + + + Política de Privacidad - QR Rapido + + + Conocé nuestra política de privacidad y cómo protegemos tus datos personales en QR Rapido. + + + Última actualización + + + Esta Política de Privacidad describe cómo QR Rapido ("nosotros", "nuestro" o "nuestra") recolecta, usa y protege la información que proporcionás cuando usás nuestro servicio de generación de códigos QR. + + + 1. Recolección de Datos + + + Recolectamos la siguiente información: + +• Datos de autenticación vía Google/Microsoft (nombre, email, foto de perfil) +• Contenido de los códigos QR generados (solo para usuarios registrados que eligen guardar en el historial) +• Datos de uso y estadísticas anónimas para mejorar nuestro servicio +• Información de pago procesada vía Stripe (no almacenamos datos de tarjeta) + + + 2. Uso de los Datos + + + Utilizamos tus datos para: + +• Proporcionar y mejorar nuestro servicio de generación de códigos QR +• Mantener tu historial de códigos QR (solo si elegís guardarlos) +• Procesar pagos de suscripciones premium +• Enviar comunicaciones relacionadas al servicio +• Cumplir obligaciones legales conforme a las leyes de protección de datos + + + 3. Protección de los Datos + + + Implementamos medidas de seguridad técnicas y organizacionales adecuadas para proteger tus datos personales contra acceso no autorizado, alteración, divulgación o destrucción, incluyendo: + +• Cifrado SSL/TLS en todas las comunicaciones +• Almacenamiento seguro en servidores protegidos +• Control de acceso restringido a los datos +• Monitoreo continuo de seguridad + + + 4. Compartir Datos + + + No vendemos, alquilamos ni compartimos tus datos personales con terceros, excepto: + +• Con proveedores de servicios esenciales (Google, Microsoft, Stripe) para autenticación y pagos +• Cuando sea requerido por ley u orden judicial +• Para proteger nuestros derechos legales y la seguridad de los usuarios + + + 5. Tus Derechos + + + De acuerdo con las leyes de protección de datos, tenés derecho a: + +• Acceder a tus datos personales +• Corregir datos incompletos o incorrectos +• Solicitar la eliminación de datos innecesarios +• Revocar consentimiento para tratamiento de datos +• Portabilidad de tus datos + + + 6. Cookies y Tecnologías Similares + + + Utilizamos cookies esenciales para el funcionamiento del sitio y análisis de tráfico vía Google Analytics. Podés gestionar tus preferencias de cookies en la configuración de tu navegador. + + + 7. Contacto + + + Para consultas sobre esta Política de Privacidad o ejercer tus derechos, contactanos a través del email: contato@qrrapido.site + + + Términos de Uso - QR Rapido + + + Leé los términos y condiciones de uso de QR Rapido, el generador de códigos QR más rápido de América Latina. + + + Última actualización + + + Estos Términos de Uso ("Términos") rigen el uso del servicio QR Rapido ("Servicio") proporcionado por nuestra empresa ("nosotros", "nuestro" o "nuestra"). Al usar nuestro Servicio, aceptás estos Términos. + + + 1. Aceptación de los Términos + + + Al acceder y usar QR Rapido, declarás tener al menos 18 años de edad o poseer autorización de padres/responsables, y aceptás cumplir estos Términos y todas las leyes aplicables. + + + 2. Descripción del Servicio + + + QR Rapido es un servicio online gratuito de generación de códigos QR que ofrece: + +• Generación rápida de códigos QR para diversos tipos de contenido +• Versión gratuita con limitaciones diarias +• Plan Premium con recursos avanzados e ilimitados +• Historial de códigos para usuarios registrados + + + 3. Responsabilidades del Usuario + + + Te comprometés a: + +• Usar el servicio únicamente para fines legales y éticos +• No generar códigos QR con contenido ofensivo, ilegal o que infrinja derechos de terceros +• No intentar comprometer la seguridad o disponibilidad del servicio +• Mantener la confidencialidad de tus credenciales de acceso + + + 4. Usos Prohibidos + + + Está prohibido usar el servicio para: + +• Diseminar malware, phishing o enlaces maliciosos +• Violación de derechos de autor o propiedad intelectual +• Spam, acoso o actividades fraudulentas +• Actividades que violen leyes nacionales o internacionales + + + 5. Propiedad Intelectual + + + Todos los derechos sobre el software, diseño, marca y contenido de QR Rapido pertenecen a nuestra empresa. Los códigos QR generados son de tu propiedad, pero nos concedés licencia para almacenarlos según sea necesario para proporcionar el servicio. + + + 6. Limitaciones del Servicio + + + • Usuarios gratuitos tienen límite diario de códigos QR +• Nos reservamos el derecho de modificar o interrumpir el servicio temporalmente +• No garantizamos disponibilidad 100% del tiempo +• Podemos implementar medidas técnicas para prevenir abuso + + + 7. Pagos y Suscripciones + + + • Suscripciones Premium son procesadas vía Stripe +• Pagos son recurrentes hasta cancelación +• Reembolsos siguen nuestra política de 7 días +• Precios pueden ser alterados mediante aviso previo de 30 días + + + 8. Limitación de Responsabilidad + + + El servicio se proporciona "como está". No nos responsabilizamos por: + +• Daños directos o indirectos derivados del uso del servicio +• Pérdida de datos o interrupción de negocios +• Uso inadecuado de los códigos QR generados por terceros +• Contenido accedido a través de los códigos QR + + + 9. Modificaciones de los Términos + + + Podemos actualizar estos Términos periódicamente. Cambios significativos serán comunicados por email o aviso en el sitio. El uso continuado después de los cambios constituye aceptación de los nuevos Términos. + + + 10. Ley Aplicable + + + Estos Términos se rigen por las leyes aplicables en cada jurisdicción. Las disputas serán resueltas en los tribunales competentes de cada país, sin perjuicio a tu derecho de acceso a los órganos de defensa del consumidor. + + + 11. Contacto + + + Para dudas sobre estos Términos de Uso, contactanos: contato@qrrapido.site + + + Volver al Inicio + \ No newline at end of file diff --git a/Resources/SharedResource.resx b/Resources/SharedResource.resx index 226b02f..dbb925d 100644 --- a/Resources/SharedResource.resx +++ b/Resources/SharedResource.resx @@ -1,429 +1,429 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - Generate QR codes in seconds! - - - Generate QR Code - - - QR Code Type - - - Content - - - URL/Link - - - Plain Text - - - WiFi - - - Business Card - - - SMS - - - Email - - - Dynamic QR (Premium) - - - Quick Style - - - Classic - - - Modern - - - Colorful - - - Enter your QR code content here... - - - Advanced Customization - - - Primary Color - - - Background Color - - - Size - - - Margin - - - Logo/Icon - - - Corner Style - - - Generate QR Code Rapidly - - - Preview - - - Your QR code will appear here in seconds - - - Ultra-fast generation guaranteed - - - Download PNG - - - Download SVG (Vector) - - - Download PDF - - - Save to History - - - Login to save to history - - - QR Rapido Premium - - - Tips for Faster QR - - - Short URLs generate faster - - - Less text = higher speed - - - Solid colors optimize the process - - - Smaller sizes speed up downloads - - - Login - - - Login with - - - Google - - - Microsoft - - - Unlock all premium features! - - - Premium Offer! - - - Remove ads, access advanced customization and much more for only $12.90/month or $129.00/year. Login and subscribe now! - - - Privacy Policy - - - Back to generator - - - Generated in - - - s - - - Ultra fast generation! - - - Fast generation! - - - Normal generation - - - Generation error. Try again. - - - QR Code saved to history! - - - Create QR Code Quickly - - - Premium User Active - - - No Ads • History • Unlimited QR - - - Unlimited today - - - QR codes remaining - - - QR Code Type - - - Select type - - - URL/Link - - - Simple Text - - - Business Card - - - Dynamic QR (Premium) - - - Enter your QR code content here... - - - Content hints - - - Classic - - - Modern - - - Colorful - - - Rede WPA (a mais comum) - - - WEP (muito antigo) - - - Sem senha - - - Content is required - - - Content too long. Maximum 4000 characters. - - - Custom corner styles are exclusive to Premium plan. Upgrade to use this functionality. - - - QR codes limit reached - - - Custom logo is exclusive to Premium plan. Upgrade to use this functionality. - - - Logo too large. Maximum 2MB. - - - Invalid format. Use PNG or JPG. - - - User Profile - - - QR Codes History - - - Error saving to history. - - - Feature not available - - - Error sharing. Try another method. - - - Link copied to clipboard! - - - Enter QR code content - - - Content must have at least 3 characters - - - VCard validation error: - - - QR Code generated with QR Rapido - the fastest generator in Brazil! - - - QR Rapido: Generate QR codes in seconds! Ultra-fast generator. Free, no registration required. 30 days ad-free after login. - - - Logo not provided - - - Logo too small. Minimum 32x32 pixels. - - - Invalid image format - - - Error processing logo. - - - Delete QR Code - - - Confirm Deletion - - - Are you sure you want to delete this QR code from your history? - - - Yes - - - No - - - QR Code deleted successfully! - - - Error deleting QR code. Please try again. - - - Deleting - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Generate QR codes in seconds! + + + Generate QR Code + + + QR Code Type + + + Content + + + URL/Link + + + Plain Text + + + WiFi + + + Business Card + + + SMS + + + Email + + + Dynamic QR (Premium) + + + Quick Style + + + Classic + + + Modern + + + Colorful + + + Enter your QR code content here... + + + Advanced Customization + + + Primary Color + + + Background Color + + + Size + + + Margin + + + Logo/Icon + + + Corner Style + + + Generate QR Code Rapidly + + + Preview + + + Your QR code will appear here in seconds + + + Ultra-fast generation guaranteed + + + Download PNG + + + Download SVG (Vector) + + + Download PDF + + + Save to History + + + Login to save to history + + + QR Rapido Premium + + + Tips for Faster QR + + + Short URLs generate faster + + + Less text = higher speed + + + Solid colors optimize the process + + + Smaller sizes speed up downloads + + + Login + + + Login with + + + Google + + + Microsoft + + + Unlock all premium features! + + + Premium Offer! + + + Remove ads, access advanced customization and much more for only $12.90/month or $129.00/year. Login and subscribe now! + + + Privacy Policy + + + Back to generator + + + Generated in + + + s + + + Ultra fast generation! + + + Fast generation! + + + Normal generation + + + Generation error. Try again. + + + QR Code saved to history! + + + Create QR Code Quickly + + + Premium User Active + + + No Ads • History • Unlimited QR + + + Unlimited today + + + QR codes remaining + + + QR Code Type + + + Select type + + + URL/Link + + + Simple Text + + + Business Card + + + Dynamic QR (Premium) + + + Enter your QR code content here... + + + Content hints + + + Classic + + + Modern + + + Colorful + + + Rede WPA (a mais comum) + + + WEP (muito antigo) + + + Sem senha + + + Content is required + + + Content too long. Maximum 4000 characters. + + + Custom corner styles are exclusive to Premium plan. Upgrade to use this functionality. + + + QR codes limit reached + + + Custom logo is exclusive to Premium plan. Upgrade to use this functionality. + + + Logo too large. Maximum 2MB. + + + Invalid format. Use PNG or JPG. + + + User Profile + + + QR Codes History + + + Error saving to history. + + + Feature not available + + + Error sharing. Try another method. + + + Link copied to clipboard! + + + Enter QR code content + + + Content must have at least 3 characters + + + VCard validation error: + + + QR Code generated with QR Rapido - the fastest generator in Brazil! + + + QR Rapido: Generate QR codes in seconds! Ultra-fast generator. Free, no registration required. 30 days ad-free after login. + + + Logo not provided + + + Logo too small. Minimum 32x32 pixels. + + + Invalid image format + + + Error processing logo. + + + Delete QR Code + + + Confirm Deletion + + + Are you sure you want to delete this QR code from your history? + + + Yes + + + No + + + QR Code deleted successfully! + + + Error deleting QR code. Please try again. + + + Deleting + \ No newline at end of file diff --git a/Services/AdDisplayService.cs b/Services/AdDisplayService.cs index e304bf5..c479079 100644 --- a/Services/AdDisplayService.cs +++ b/Services/AdDisplayService.cs @@ -109,10 +109,10 @@ namespace QRRapidoApp.Services } } - public void SetViewBagAds(dynamic viewBag) - { + public void SetViewBagAds(dynamic viewBag) + { viewBag.AdSenseTag = _config["AdSense:ClientId"]; - viewBag.AdSenseEnabled = _config["AdSense:Enabled"]=="True"; + viewBag.AdSenseEnabled = _config["AdSense:Enabled"]=="True"; } } } \ No newline at end of file diff --git a/Services/Monitoring/MongoDbMonitoringService.cs b/Services/Monitoring/MongoDbMonitoringService.cs index d5d215d..9065704 100644 --- a/Services/Monitoring/MongoDbMonitoringService.cs +++ b/Services/Monitoring/MongoDbMonitoringService.cs @@ -199,13 +199,13 @@ namespace QRRapidoApp.Services.Monitoring try { var command = new BsonDocument("collStats", collectionName); - var result = await context.Database!.RunCommandAsync(command); - + var result = await context.Database!.RunCommandAsync(command); + var size = GetDoubleValue(result, "size"); var totalIndexSize = GetDoubleValue(result, "totalIndexSize"); var count = result.GetValue("count", BsonValue.Create(0)).ToInt64(); - var avgObjSize = GetDoubleValue(result, "avgObjSize"); - + var avgObjSize = GetDoubleValue(result, "avgObjSize"); + collectionStats.Add(new CollectionStatistics { Name = collectionName, @@ -225,17 +225,17 @@ namespace QRRapidoApp.Services.Monitoring return collectionStats.OrderByDescending(c => c.SizeMB).ToList(); } - private static double GetDoubleValue(BsonDocument document, string fieldName) - { - var value = document.GetValue(fieldName, BsonValue.Create(0)); - return value.BsonType switch - { - BsonType.Double => value.AsDouble, - BsonType.Int32 => (double)value.AsInt32, - BsonType.Int64 => (double)value.AsInt64, - BsonType.Decimal128 => (double)value.AsDecimal128, - _ => 0.0 - }; + private static double GetDoubleValue(BsonDocument document, string fieldName) + { + var value = document.GetValue(fieldName, BsonValue.Create(0)); + return value.BsonType switch + { + BsonType.Double => value.AsDouble, + BsonType.Int32 => (double)value.AsInt32, + BsonType.Int64 => (double)value.AsInt64, + BsonType.Decimal128 => (double)value.AsDecimal128, + _ => 0.0 + }; } private bool ShouldMonitorCollection(string collectionName) diff --git a/Services/StripeService.cs b/Services/StripeService.cs index cac0a68..478c211 100644 --- a/Services/StripeService.cs +++ b/Services/StripeService.cs @@ -89,20 +89,20 @@ namespace QRRapidoApp.Services break; case "invoice.finalized": - var invoice = stripeEvent.Data.Object as Invoice; - var subscriptionLineItem = invoice.Lines?.Data - .FirstOrDefault(line => - !string.IsNullOrEmpty(line.SubscriptionId) || - line.Subscription != null - ); - - string subscriptionId = null; - - if (subscriptionLineItem != null) - { - // Tenta obter o ID da assinatura de duas formas diferentes - subscriptionId = subscriptionLineItem.SubscriptionId - ?? subscriptionLineItem.Subscription?.Id; + var invoice = stripeEvent.Data.Object as Invoice; + var subscriptionLineItem = invoice.Lines?.Data + .FirstOrDefault(line => + !string.IsNullOrEmpty(line.SubscriptionId) || + line.Subscription != null + ); + + string subscriptionId = null; + + if (subscriptionLineItem != null) + { + // Tenta obter o ID da assinatura de duas formas diferentes + subscriptionId = subscriptionLineItem.SubscriptionId + ?? subscriptionLineItem.Subscription?.Id; } if (subscriptionId != null) @@ -153,8 +153,8 @@ namespace QRRapidoApp.Services await _userService.UpdateUserStripeCustomerIdAsync(user.Id, subscription.CustomerId); } - await _userService.ActivatePremiumStatus(userId, subscription.Id, subItem.CurrentPeriodEnd); - + await _userService.ActivatePremiumStatus(userId, subscription.Id, subItem.CurrentPeriodEnd); + _logger.LogInformation($"Successfully processed premium activation/renewal for user {userId}."); } diff --git a/Services/UserService.cs b/Services/UserService.cs index ede7312..c4aea77 100644 --- a/Services/UserService.cs +++ b/Services/UserService.cs @@ -1,442 +1,442 @@ -using MongoDB.Driver; -using QRRapidoApp.Data; -using QRRapidoApp.Models; -using QRRapidoApp.Models.ViewModels; -using System.Text.Json; - -namespace QRRapidoApp.Services -{ - public class UserService : IUserService - { - private readonly MongoDbContext _context; - private readonly IConfiguration _config; - private readonly ILogger _logger; - - public UserService(MongoDbContext context, IConfiguration config, ILogger logger) - { - _context = context; - _config = config; - _logger = logger; - } - - public async Task GetUserAsync(string userId) - { - try - { - if (_context.Users == null) return null; // Development mode without MongoDB - User? userData = null; - - if (!String.IsNullOrEmpty(userId)) - { - userData = await _context.Users.Find(u => u.Id == userId).FirstOrDefaultAsync(); - } - - return userData ?? new User(); - } - catch (Exception ex) - { - _logger.LogError(ex, $"Error getting user {userId}: {ex.Message}"); - return null; - } - } - - public async Task GetUserByEmailAsync(string email) - { - try - { - if (_context.Users == null) return null; // Development mode without MongoDB - - return await _context.Users.Find(u => u.Email == email).FirstOrDefaultAsync(); - } - catch (Exception ex) - { - _logger.LogError(ex, $"Error getting user by email {email}: {ex.Message}"); - return null; - } - } - - public async Task GetUserByProviderAsync(string provider, string providerId) - { - try - { - return await _context.Users - .Find(u => u.Provider == provider && u.ProviderId == providerId) - .FirstOrDefaultAsync(); - } - catch (Exception ex) - { - _logger.LogError(ex, $"Error getting user by provider {provider}:{providerId}: {ex.Message}"); - return null; - } - } - - public async Task CreateUserAsync(string email, string name, string provider, string providerId) - { - var user = new User - { - Email = email, - Name = name, - Provider = provider, - ProviderId = providerId, - CreatedAt = DateTime.UtcNow, - LastLoginAt = DateTime.UtcNow, - PreferredLanguage = "pt-BR", - DailyQRCount = 0, - LastQRDate = DateTime.UtcNow.Date, - TotalQRGenerated = 0 - }; - - await _context.Users.InsertOneAsync(user); - _logger.LogInformation($"Created new user: {email} via {provider}"); - - return user; - } - - public async Task UpdateLastLoginAsync(string userId) - { - try - { - var update = Builders.Update - .Set(u => u.LastLoginAt, DateTime.UtcNow); - - await _context.Users.UpdateOneAsync(u => u.Id == userId, update); - } - catch (Exception ex) - { - _logger.LogError(ex, $"Error updating last login for user {userId}: {ex.Message}"); - } - } - - public async Task UpdateUserAsync(User user) - { - try - { - var result = await _context.Users.ReplaceOneAsync(u => u.Id == user.Id, user); - return result.ModifiedCount > 0; - } - catch (Exception ex) - { - _logger.LogError(ex, $"Error updating user {user.Id}: {ex.Message}"); - return false; - } - } - - public async Task GetDailyQRCountAsync(string? userId) - { - if (string.IsNullOrEmpty(userId)) - return 0; // Anonymous users tracked separately - - try - { - var user = await GetUserAsync(userId); - if (user == null) return 0; - - // Reset count if it's a new day - if (user.LastQRDate.Date < DateTime.UtcNow.Date) - { - user.DailyQRCount = 0; - user.LastQRDate = DateTime.UtcNow.Date; - await UpdateUserAsync(user); - } - - return user.DailyQRCount; - } - catch (Exception ex) - { - _logger.LogError(ex, $"Error getting daily QR count for user {userId}: {ex.Message}"); - return 0; - } - } - - public async Task IncrementDailyQRCountAsync(string userId) - { - try - { - var user = await GetUserAsync(userId); - if (user == null) return 0; - - // Reset count if it's a new day - if (user.LastQRDate.Date < DateTime.UtcNow.Date) - { - user.DailyQRCount = 1; - user.LastQRDate = DateTime.UtcNow.Date; - } - else - { - user.DailyQRCount++; - } - - user.TotalQRGenerated++; - await UpdateUserAsync(user); - - // Premium and logged users have unlimited QR codes - return int.MaxValue; - } - catch (Exception ex) - { - _logger.LogError(ex, $"Error incrementing daily QR count for user {userId}: {ex.Message}"); - return 0; - } - } - - public async Task GetRemainingQRCountAsync(string userId) - { - try - { - var user = await GetUserAsync(userId); - if (user == null) return 0; - - // Premium users have unlimited - if (user.IsPremium) return int.MaxValue; - - // Logged users (non-premium) have unlimited - return int.MaxValue; - } - catch (Exception ex) - { - _logger.LogError(ex, $"Error getting remaining QR count for user {userId}: {ex.Message}"); - return 0; - } - } - - public async Task CanGenerateQRAsync(string? userId, bool isPremium) - { - // Premium users have unlimited QR codes - if (isPremium) return true; - - // Logged users (non-premium) have unlimited QR codes - if (!string.IsNullOrEmpty(userId)) return true; - - // Anonymous users have 3 QR codes per day - var dailyCount = await GetDailyQRCountAsync(userId); - var limit = 3; - - return dailyCount < limit; - } - - public async Task SaveQRToHistoryAsync(string? userId, QRGenerationResult qrResult) - { - try - { - var qrHistory = new QRCodeHistory - { - UserId = userId, - Type = qrResult.RequestSettings?.Type ?? "unknown", - Content = qrResult.RequestSettings?.Content ?? "", - QRCodeBase64 = qrResult.QRCodeBase64, - CustomizationSettings = JsonSerializer.Serialize(qrResult.RequestSettings), - CreatedAt = DateTime.UtcNow, - Language = qrResult.RequestSettings?.Language ?? "pt-BR", - Size = qrResult.RequestSettings?.Size ?? 300, - GenerationTimeMs = qrResult.GenerationTimeMs, - FromCache = qrResult.FromCache, - IsActive = true, - LastAccessedAt = DateTime.UtcNow - }; - - await _context.QRCodeHistory.InsertOneAsync(qrHistory); - - // Update user's QR history IDs if logged in - if (!string.IsNullOrEmpty(userId)) - { - var update = Builders.Update - .Push(u => u.QRHistoryIds, qrHistory.Id); - await _context.Users.UpdateOneAsync(u => u.Id == userId, update); - } - } - catch (Exception ex) - { - _logger.LogError(ex, $"Error saving QR to history: {ex.Message}"); - } - } - - public async Task> GetUserQRHistoryAsync(string userId, int limit = 50) - { - try - { - return await _context.QRCodeHistory - .Find(q => q.UserId == userId && q.IsActive) - .SortByDescending(q => q.CreatedAt) - .Limit(limit) - .ToListAsync(); - } - catch (Exception ex) - { - _logger.LogError(ex, $"Error getting QR history for user {userId}: {ex.Message}"); - return new List(); - } - } - - public async Task GetQRDataAsync(string qrId) - { - try - { - return await _context.QRCodeHistory - .Find(q => q.Id == qrId && q.IsActive) - .FirstOrDefaultAsync(); - } - catch (Exception ex) - { - _logger.LogError(ex, $"Error getting QR data {qrId}: {ex.Message}"); - return null; - } - } - - public async Task DeleteQRFromHistoryAsync(string userId, string qrId) - { - try - { - // First verify that the QR code belongs to the user - var qrCode = await _context.QRCodeHistory - .Find(q => q.Id == qrId && q.UserId == userId && q.IsActive) - .FirstOrDefaultAsync(); - - if (qrCode == null) - { - _logger.LogWarning($"QR code not found or doesn't belong to user - QRId: {qrId}, UserId: {userId}"); - return false; - } - - // Soft delete: mark as inactive instead of permanently deleting - var update = Builders.Update.Set(q => q.IsActive, false); - var result = await _context.QRCodeHistory.UpdateOneAsync( - q => q.Id == qrId && q.UserId == userId, - update); - - return result.ModifiedCount > 0; - } - catch (Exception ex) - { - _logger.LogError(ex, $"Error deleting QR from history - QRId: {qrId}, UserId: {userId}: {ex.Message}"); - return false; - } - } - - public async Task GetQRCountThisMonthAsync(string userId) - { - try - { - var startOfMonth = new DateTime(DateTime.UtcNow.Year, DateTime.UtcNow.Month, 1); - var endOfMonth = startOfMonth.AddMonths(1); - - var count = await _context.QRCodeHistory - .CountDocumentsAsync(q => q.UserId == userId && - q.CreatedAt >= startOfMonth && - q.CreatedAt < endOfMonth); - - return (int)count; - } - catch (Exception ex) - { - _logger.LogError(ex, $"Error getting monthly QR count for user {userId}: {ex.Message}"); - return 0; - } - } - - // MÉTODO REMOVIDO: ExtendAdFreeTimeAsync - não é mais necessário - - public async Task GetUserEmailAsync(string userId) - { - var user = await GetUserAsync(userId); - return user?.Email ?? string.Empty; - } - - public async Task MarkPremiumCancelledAsync(string userId, DateTime cancelledAt) - { - try - { - if (_context.Users == null) return; // Development mode without MongoDB - - var update = Builders.Update - .Set(u => u.IsPremium, false) - .Set(u => u.PremiumCancelledAt, cancelledAt) - .Set(u => u.PremiumExpiresAt, null); - - await _context.Users.UpdateOneAsync(u => u.Id == userId, update); - - _logger.LogInformation($"Marked premium as cancelled for user {userId} at {cancelledAt}"); - } - catch (Exception ex) - { - _logger.LogError(ex, $"Error marking premium cancelled for user {userId}: {ex.Message}"); - } - } - - public async Task> GetUsersForHistoryCleanupAsync(DateTime cutoffDate) - { - try - { - if (_context.Users == null) return new List(); // Development mode without MongoDB - - return await _context.Users - .Find(u => u.PremiumCancelledAt != null && - u.PremiumCancelledAt < cutoffDate && - u.QRHistoryIds.Count > 0) - .ToListAsync(); - } - catch (Exception ex) - { - _logger.LogError(ex, $"Error getting users for history cleanup: {ex.Message}"); - return new List(); - } - } - - public async Task DeleteUserHistoryAsync(string userId) - { - try - { - if (_context.Users == null || _context.QRCodeHistory == null) return; // Development mode without MongoDB - - var user = await GetUserAsync(userId); - if (user?.QRHistoryIds?.Any() == true) - { - // Remover histórico de QR codes - await _context.QRCodeHistory.DeleteManyAsync(qr => user.QRHistoryIds.Contains(qr.Id)); - - // Limpar lista de histórico do usuário - var update = Builders.Update.Set(u => u.QRHistoryIds, new List()); - await _context.Users.UpdateOneAsync(u => u.Id == userId, update); - - _logger.LogInformation($"Deleted history for user {userId}"); - } - } - catch (Exception ex) - { - _logger.LogError(ex, $"Error deleting history for user {userId}: {ex.Message}"); - } - } - - public async Task ActivatePremiumStatus(string userId, string stripeSubscriptionId, DateTime expiryDate) - { - var update = Builders.Update - .Set(u => u.IsPremium, true) - .Set(u => u.StripeSubscriptionId, stripeSubscriptionId) - .Set(u => u.PremiumExpiresAt, expiryDate) - .Unset(u => u.PremiumCancelledAt); - - await _context.Users.UpdateOneAsync(u => u.Id == userId, update); - _logger.LogInformation($"Activated premium for user {userId}"); - } - - public async Task DeactivatePremiumStatus(string stripeSubscriptionId) - { - var update = Builders.Update - .Set(u => u.IsPremium, false) - .Set(u => u.PremiumCancelledAt, DateTime.UtcNow); - - await _context.Users.UpdateOneAsync(u => u.StripeSubscriptionId == stripeSubscriptionId, update); - _logger.LogInformation($"Deactivated premium for subscription {stripeSubscriptionId}"); - } - - public async Task GetUserByStripeCustomerIdAsync(string customerId) - { - return await _context.Users.Find(u => u.StripeCustomerId == customerId).FirstOrDefaultAsync(); - } - - public async Task UpdateUserStripeCustomerIdAsync(string userId, string stripeCustomerId) - { - var update = Builders.Update.Set(u => u.StripeCustomerId, stripeCustomerId); - await _context.Users.UpdateOneAsync(u => u.Id == userId, update); - } - } +using MongoDB.Driver; +using QRRapidoApp.Data; +using QRRapidoApp.Models; +using QRRapidoApp.Models.ViewModels; +using System.Text.Json; + +namespace QRRapidoApp.Services +{ + public class UserService : IUserService + { + private readonly MongoDbContext _context; + private readonly IConfiguration _config; + private readonly ILogger _logger; + + public UserService(MongoDbContext context, IConfiguration config, ILogger logger) + { + _context = context; + _config = config; + _logger = logger; + } + + public async Task GetUserAsync(string userId) + { + try + { + if (_context.Users == null) return null; // Development mode without MongoDB + User? userData = null; + + if (!String.IsNullOrEmpty(userId)) + { + userData = await _context.Users.Find(u => u.Id == userId).FirstOrDefaultAsync(); + } + + return userData ?? new User(); + } + catch (Exception ex) + { + _logger.LogError(ex, $"Error getting user {userId}: {ex.Message}"); + return null; + } + } + + public async Task GetUserByEmailAsync(string email) + { + try + { + if (_context.Users == null) return null; // Development mode without MongoDB + + return await _context.Users.Find(u => u.Email == email).FirstOrDefaultAsync(); + } + catch (Exception ex) + { + _logger.LogError(ex, $"Error getting user by email {email}: {ex.Message}"); + return null; + } + } + + public async Task GetUserByProviderAsync(string provider, string providerId) + { + try + { + return await _context.Users + .Find(u => u.Provider == provider && u.ProviderId == providerId) + .FirstOrDefaultAsync(); + } + catch (Exception ex) + { + _logger.LogError(ex, $"Error getting user by provider {provider}:{providerId}: {ex.Message}"); + return null; + } + } + + public async Task CreateUserAsync(string email, string name, string provider, string providerId) + { + var user = new User + { + Email = email, + Name = name, + Provider = provider, + ProviderId = providerId, + CreatedAt = DateTime.UtcNow, + LastLoginAt = DateTime.UtcNow, + PreferredLanguage = "pt-BR", + DailyQRCount = 0, + LastQRDate = DateTime.UtcNow.Date, + TotalQRGenerated = 0 + }; + + await _context.Users.InsertOneAsync(user); + _logger.LogInformation($"Created new user: {email} via {provider}"); + + return user; + } + + public async Task UpdateLastLoginAsync(string userId) + { + try + { + var update = Builders.Update + .Set(u => u.LastLoginAt, DateTime.UtcNow); + + await _context.Users.UpdateOneAsync(u => u.Id == userId, update); + } + catch (Exception ex) + { + _logger.LogError(ex, $"Error updating last login for user {userId}: {ex.Message}"); + } + } + + public async Task UpdateUserAsync(User user) + { + try + { + var result = await _context.Users.ReplaceOneAsync(u => u.Id == user.Id, user); + return result.ModifiedCount > 0; + } + catch (Exception ex) + { + _logger.LogError(ex, $"Error updating user {user.Id}: {ex.Message}"); + return false; + } + } + + public async Task GetDailyQRCountAsync(string? userId) + { + if (string.IsNullOrEmpty(userId)) + return 0; // Anonymous users tracked separately + + try + { + var user = await GetUserAsync(userId); + if (user == null) return 0; + + // Reset count if it's a new day + if (user.LastQRDate.Date < DateTime.UtcNow.Date) + { + user.DailyQRCount = 0; + user.LastQRDate = DateTime.UtcNow.Date; + await UpdateUserAsync(user); + } + + return user.DailyQRCount; + } + catch (Exception ex) + { + _logger.LogError(ex, $"Error getting daily QR count for user {userId}: {ex.Message}"); + return 0; + } + } + + public async Task IncrementDailyQRCountAsync(string userId) + { + try + { + var user = await GetUserAsync(userId); + if (user == null) return 0; + + // Reset count if it's a new day + if (user.LastQRDate.Date < DateTime.UtcNow.Date) + { + user.DailyQRCount = 1; + user.LastQRDate = DateTime.UtcNow.Date; + } + else + { + user.DailyQRCount++; + } + + user.TotalQRGenerated++; + await UpdateUserAsync(user); + + // Premium and logged users have unlimited QR codes + return int.MaxValue; + } + catch (Exception ex) + { + _logger.LogError(ex, $"Error incrementing daily QR count for user {userId}: {ex.Message}"); + return 0; + } + } + + public async Task GetRemainingQRCountAsync(string userId) + { + try + { + var user = await GetUserAsync(userId); + if (user == null) return 0; + + // Premium users have unlimited + if (user.IsPremium) return int.MaxValue; + + // Logged users (non-premium) have unlimited + return int.MaxValue; + } + catch (Exception ex) + { + _logger.LogError(ex, $"Error getting remaining QR count for user {userId}: {ex.Message}"); + return 0; + } + } + + public async Task CanGenerateQRAsync(string? userId, bool isPremium) + { + // Premium users have unlimited QR codes + if (isPremium) return true; + + // Logged users (non-premium) have unlimited QR codes + if (!string.IsNullOrEmpty(userId)) return true; + + // Anonymous users have 3 QR codes per day + var dailyCount = await GetDailyQRCountAsync(userId); + var limit = 3; + + return dailyCount < limit; + } + + public async Task SaveQRToHistoryAsync(string? userId, QRGenerationResult qrResult) + { + try + { + var qrHistory = new QRCodeHistory + { + UserId = userId, + Type = qrResult.RequestSettings?.Type ?? "unknown", + Content = qrResult.RequestSettings?.Content ?? "", + QRCodeBase64 = qrResult.QRCodeBase64, + CustomizationSettings = JsonSerializer.Serialize(qrResult.RequestSettings), + CreatedAt = DateTime.UtcNow, + Language = qrResult.RequestSettings?.Language ?? "pt-BR", + Size = qrResult.RequestSettings?.Size ?? 300, + GenerationTimeMs = qrResult.GenerationTimeMs, + FromCache = qrResult.FromCache, + IsActive = true, + LastAccessedAt = DateTime.UtcNow + }; + + await _context.QRCodeHistory.InsertOneAsync(qrHistory); + + // Update user's QR history IDs if logged in + if (!string.IsNullOrEmpty(userId)) + { + var update = Builders.Update + .Push(u => u.QRHistoryIds, qrHistory.Id); + await _context.Users.UpdateOneAsync(u => u.Id == userId, update); + } + } + catch (Exception ex) + { + _logger.LogError(ex, $"Error saving QR to history: {ex.Message}"); + } + } + + public async Task> GetUserQRHistoryAsync(string userId, int limit = 50) + { + try + { + return await _context.QRCodeHistory + .Find(q => q.UserId == userId && q.IsActive) + .SortByDescending(q => q.CreatedAt) + .Limit(limit) + .ToListAsync(); + } + catch (Exception ex) + { + _logger.LogError(ex, $"Error getting QR history for user {userId}: {ex.Message}"); + return new List(); + } + } + + public async Task GetQRDataAsync(string qrId) + { + try + { + return await _context.QRCodeHistory + .Find(q => q.Id == qrId && q.IsActive) + .FirstOrDefaultAsync(); + } + catch (Exception ex) + { + _logger.LogError(ex, $"Error getting QR data {qrId}: {ex.Message}"); + return null; + } + } + + public async Task DeleteQRFromHistoryAsync(string userId, string qrId) + { + try + { + // First verify that the QR code belongs to the user + var qrCode = await _context.QRCodeHistory + .Find(q => q.Id == qrId && q.UserId == userId && q.IsActive) + .FirstOrDefaultAsync(); + + if (qrCode == null) + { + _logger.LogWarning($"QR code not found or doesn't belong to user - QRId: {qrId}, UserId: {userId}"); + return false; + } + + // Soft delete: mark as inactive instead of permanently deleting + var update = Builders.Update.Set(q => q.IsActive, false); + var result = await _context.QRCodeHistory.UpdateOneAsync( + q => q.Id == qrId && q.UserId == userId, + update); + + return result.ModifiedCount > 0; + } + catch (Exception ex) + { + _logger.LogError(ex, $"Error deleting QR from history - QRId: {qrId}, UserId: {userId}: {ex.Message}"); + return false; + } + } + + public async Task GetQRCountThisMonthAsync(string userId) + { + try + { + var startOfMonth = new DateTime(DateTime.UtcNow.Year, DateTime.UtcNow.Month, 1); + var endOfMonth = startOfMonth.AddMonths(1); + + var count = await _context.QRCodeHistory + .CountDocumentsAsync(q => q.UserId == userId && + q.CreatedAt >= startOfMonth && + q.CreatedAt < endOfMonth); + + return (int)count; + } + catch (Exception ex) + { + _logger.LogError(ex, $"Error getting monthly QR count for user {userId}: {ex.Message}"); + return 0; + } + } + + // MÉTODO REMOVIDO: ExtendAdFreeTimeAsync - não é mais necessário + + public async Task GetUserEmailAsync(string userId) + { + var user = await GetUserAsync(userId); + return user?.Email ?? string.Empty; + } + + public async Task MarkPremiumCancelledAsync(string userId, DateTime cancelledAt) + { + try + { + if (_context.Users == null) return; // Development mode without MongoDB + + var update = Builders.Update + .Set(u => u.IsPremium, false) + .Set(u => u.PremiumCancelledAt, cancelledAt) + .Set(u => u.PremiumExpiresAt, null); + + await _context.Users.UpdateOneAsync(u => u.Id == userId, update); + + _logger.LogInformation($"Marked premium as cancelled for user {userId} at {cancelledAt}"); + } + catch (Exception ex) + { + _logger.LogError(ex, $"Error marking premium cancelled for user {userId}: {ex.Message}"); + } + } + + public async Task> GetUsersForHistoryCleanupAsync(DateTime cutoffDate) + { + try + { + if (_context.Users == null) return new List(); // Development mode without MongoDB + + return await _context.Users + .Find(u => u.PremiumCancelledAt != null && + u.PremiumCancelledAt < cutoffDate && + u.QRHistoryIds.Count > 0) + .ToListAsync(); + } + catch (Exception ex) + { + _logger.LogError(ex, $"Error getting users for history cleanup: {ex.Message}"); + return new List(); + } + } + + public async Task DeleteUserHistoryAsync(string userId) + { + try + { + if (_context.Users == null || _context.QRCodeHistory == null) return; // Development mode without MongoDB + + var user = await GetUserAsync(userId); + if (user?.QRHistoryIds?.Any() == true) + { + // Remover histórico de QR codes + await _context.QRCodeHistory.DeleteManyAsync(qr => user.QRHistoryIds.Contains(qr.Id)); + + // Limpar lista de histórico do usuário + var update = Builders.Update.Set(u => u.QRHistoryIds, new List()); + await _context.Users.UpdateOneAsync(u => u.Id == userId, update); + + _logger.LogInformation($"Deleted history for user {userId}"); + } + } + catch (Exception ex) + { + _logger.LogError(ex, $"Error deleting history for user {userId}: {ex.Message}"); + } + } + + public async Task ActivatePremiumStatus(string userId, string stripeSubscriptionId, DateTime expiryDate) + { + var update = Builders.Update + .Set(u => u.IsPremium, true) + .Set(u => u.StripeSubscriptionId, stripeSubscriptionId) + .Set(u => u.PremiumExpiresAt, expiryDate) + .Unset(u => u.PremiumCancelledAt); + + await _context.Users.UpdateOneAsync(u => u.Id == userId, update); + _logger.LogInformation($"Activated premium for user {userId}"); + } + + public async Task DeactivatePremiumStatus(string stripeSubscriptionId) + { + var update = Builders.Update + .Set(u => u.IsPremium, false) + .Set(u => u.PremiumCancelledAt, DateTime.UtcNow); + + await _context.Users.UpdateOneAsync(u => u.StripeSubscriptionId == stripeSubscriptionId, update); + _logger.LogInformation($"Deactivated premium for subscription {stripeSubscriptionId}"); + } + + public async Task GetUserByStripeCustomerIdAsync(string customerId) + { + return await _context.Users.Find(u => u.StripeCustomerId == customerId).FirstOrDefaultAsync(); + } + + public async Task UpdateUserStripeCustomerIdAsync(string userId, string stripeCustomerId) + { + var update = Builders.Update.Set(u => u.StripeCustomerId, stripeCustomerId); + await _context.Users.UpdateOneAsync(u => u.Id == userId, update); + } + } } \ No newline at end of file diff --git a/Views/Shared/Error.cshtml b/Views/Shared/Error.cshtml index c746b63..b82a660 100644 --- a/Views/Shared/Error.cshtml +++ b/Views/Shared/Error.cshtml @@ -1,43 +1,43 @@ -@using QRRapidoApp.Models -@model ErrorViewModel -@{ - ViewData["Title"] = "Erro"; - Layout = "~/Views/Shared/_Layout.cshtml"; -} - -
-
-
-
-

Erro

-

Ocorreu um erro durante o processamento da sua solicitação.

- - @if (Model?.ShowRequestId == true) - { -

- Request ID: @Model.RequestId -

- } - - @if (!string.IsNullOrEmpty(ViewBag.ErrorCode)) - { -

- Código do erro: @ViewBag.ErrorCode -

- } - - @if (!string.IsNullOrEmpty(ViewBag.ErrorMessage)) - { -

- Mensagem: @ViewBag.ErrorMessage -

- } -
- - -
-
-
+@using QRRapidoApp.Models +@model ErrorViewModel +@{ + ViewData["Title"] = "Erro"; + Layout = "~/Views/Shared/_Layout.cshtml"; +} + +
+
+
+
+

Erro

+

Ocorreu um erro durante o processamento da sua solicitação.

+ + @if (Model?.ShowRequestId == true) + { +

+ Request ID: @Model.RequestId +

+ } + + @if (!string.IsNullOrEmpty(ViewBag.ErrorCode)) + { +

+ Código do erro: @ViewBag.ErrorCode +

+ } + + @if (!string.IsNullOrEmpty(ViewBag.ErrorMessage)) + { +

+ Mensagem: @ViewBag.ErrorMessage +

+ } +
+ + +
+
+
diff --git a/wwwroot/ads.txt b/wwwroot/ads.txt index 018faee..fa49e88 100644 --- a/wwwroot/ads.txt +++ b/wwwroot/ads.txt @@ -1 +1 @@ -google.com, pub-3475956393038764, DIRECT, f08c47fec0942fa0 +google.com, pub-3475956393038764, DIRECT, f08c47fec0942fa0 diff --git a/wwwroot/css/qrrapido-theme.css b/wwwroot/css/qrrapido-theme.css index 7054ba7..541669d 100644 --- a/wwwroot/css/qrrapido-theme.css +++ b/wwwroot/css/qrrapido-theme.css @@ -1,1290 +1,1290 @@ -/* QR Rapido Custom Theme */ -:root { - --qr-primary: #007BFF; - --qr-secondary: #28A745; - --qr-accent: #FF6B35; - --qr-warning: #FFC107; - --qr-success: #28A745; - --qr-danger: #DC3545; - --qr-dark: #343A40; - --qr-light: #F8F9FA; -} - -/* Global Styles */ -body { - font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; - line-height: 1.6; -} - -.bg-gradient-primary { - background: linear-gradient(135deg, var(--qr-primary) 0%, #0056B3 100%); -} - -/* Generation Timer Styles */ -.generation-timer { - display: inline-flex; - align-items: center; - gap: 0.5rem; - padding: 0.25rem 0.75rem; - background: #f8f9fa; - border-radius: 20px; - border: 2px solid var(--qr-primary); - transition: all 0.3s ease; -} - -.generation-timer.active { - background: var(--qr-primary); - color: white; - animation: pulse 1.5s infinite; -} - -/* Speed Badge Animation */ -.speed-badge { - animation: slideInRight 0.5s ease-out; -} - -@keyframes pulse { - 0% { transform: scale(1); } - 50% { transform: scale(1.05); } - 100% { transform: scale(1); } -} - -@keyframes slideInRight { - from { - transform: translateX(20px); - opacity: 0; - } - to { - transform: translateX(0); - opacity: 1; - } -} - -/* QR Preview Placeholder - REMOVIDO: Substituído por versão com melhor contraste */ - -/* Logo and Branding */ -.navbar-brand svg { - filter: drop-shadow(2px 2px 4px rgba(0,0,0,0.1)); - transition: transform 0.3s ease; -} - -.navbar-brand:hover svg { - transform: scale(1.05); -} - -/* QR Preview Image */ -#qr-preview img { - border-radius: 8px; - box-shadow: 0 4px 8px rgba(0,0,0,0.1); - transition: transform 0.3s ease; - max-width: 100%; - height: auto; -} - -#qr-preview img:hover { - transform: scale(1.02); -} - -/* Card Hover Effects */ -.card { - transition: all 0.3s ease; - border-radius: 12px; -} - -.card:hover { - box-shadow: 0 8px 25px rgba(0,0,0,0.1); - transform: translateY(-2px); -} - -/* Button Styles */ -.btn-check:checked + .btn { - background-color: var(--qr-primary); - border-color: var(--qr-primary); - color: white; -} - -.btn-primary { - background: linear-gradient(135deg, var(--qr-primary) 0%, #0056B3 100%); - border: none; - color: white !important; - transition: all 0.3s ease; -} - -.btn-primary:hover { - background: linear-gradient(135deg, #0056B3 0%, var(--qr-primary) 100%); - color: white !important; - transform: translateY(-1px); - box-shadow: 0 4px 12px rgba(0, 123, 255, 0.3); -} - -/* Botões de login específicos - cores sólidas */ -.card-body .btn-danger { - background-color: var(--qr-danger) !important; - background-image: none !important; - border-color: var(--qr-danger) !important; - color: white !important; -} - -.card-body .btn-danger:hover { - background-color: #C82333 !important; - background-image: none !important; - border-color: #C82333 !important; - color: white !important; -} - -.card-body .btn-primary { - background-color: var(--qr-primary) !important; - background-image: none !important; - border-color: var(--qr-primary) !important; - color: white !important; -} - -.card-body .btn-primary:hover { - background-color: #0056B3 !important; - background-image: none !important; - border-color: #0056B3 !important; - color: white !important; -} - -/* Form Controls */ -.form-control:focus, -.form-select:focus { - border-color: var(--qr-primary); - box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25); -} - -.form-control-lg { - border-radius: 8px; -} - -/* Speed Statistics Cards */ -.card.border-success { - border-color: var(--qr-success) !important; -} - -.card.border-primary { - border-color: var(--qr-primary) !important; -} - -.card.border-warning { - border-color: var(--qr-warning) !important; -} - -/* Generation Stats Animation */ -.generation-stats { - animation: slideInRight 0.5s ease-out; -} - -/* Ad Container Styles - REMOVIDO: Substituído por versão com melhor contraste */ - -.ad-free-notice { - text-align: center; - border-left: 4px solid var(--qr-success); - background: linear-gradient(135deg, #d4edda 0%, #c3e6cb 100%); - border-radius: 8px; -} - -.ad-free-notice .fas { - color: var(--qr-warning); -} - -/* Loading Animations */ -.spinner-border-sm { - width: 1rem; - height: 1rem; -} - -/* Progress Bar for QR Generation */ -.progress { - height: 4px; - border-radius: 2px; - background-color: #e9ecef; -} - -.progress-bar { - background: linear-gradient(90deg, var(--qr-primary), var(--qr-accent)); -} - -/* Speed Tips */ -.list-unstyled li { - padding: 0.25rem 0; - transition: all 0.2s ease; -} - -.list-unstyled li:hover { - padding-left: 0.5rem; - color: var(--qr-primary); -} - -/* Responsive Design */ -@media (max-width: 768px) { - .generation-timer { - font-size: 0.875rem; - padding: 0.2rem 0.5rem; - } - - .speed-badge .badge { - font-size: 0.75rem; - } - - .placeholder-qr { - padding: 2rem 1rem; - } - - .card-body { - padding: 1rem; - } - - .ad-container { - margin: 10px 0; - padding: 10px; - } -} - -/* Premium Features Styling */ -.premium-feature { - background: linear-gradient(135deg, #fff3cd 0%, #ffeaa7 100%); - border-left: 4px solid var(--qr-warning); - padding: 1rem; - border-radius: 8px; - margin: 1rem 0; -} - -.premium-badge { - background: linear-gradient(135deg, var(--qr-warning) 0%, #f39c12 100%); - color: var(--qr-dark); - border-radius: 20px; - padding: 0.25rem 0.75rem; - font-size: 0.75rem; - font-weight: 600; - text-transform: uppercase; - letter-spacing: 0.5px; -} - -/* Accordion Customization */ -.accordion-button { - background: var(--qr-light); - border: none; - border-radius: 8px !important; -} - -.accordion-button:not(.collapsed) { - background: var(--qr-primary); - color: white; -} - -.accordion-button:focus { - box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25); -} - -/* Footer Styling */ -footer { - background: linear-gradient(135deg, var(--qr-dark) 0%, #2c3e50 100%); -} - -footer a { - text-decoration: none; - transition: color 0.3s ease; -} - -footer a:hover { - color: var(--qr-primary) !important; -} - -/* Language Selector */ -.dropdown-toggle::after { - margin-left: 0.5rem; -} - -.dropdown-menu { - border-radius: 8px; - box-shadow: 0 4px 12px rgba(0,0,0,0.1); - border: none; -} - -.dropdown-item { - transition: all 0.2s ease; - border-radius: 4px; - margin: 2px 4px; -} - -.dropdown-item:hover { - background: var(--qr-primary); - color: white; -} - -/* Custom Scrollbar */ -::-webkit-scrollbar { - width: 8px; -} - -::-webkit-scrollbar-track { - background: #f1f1f1; - border-radius: 4px; -} - -::-webkit-scrollbar-thumb { - background: var(--qr-primary); - border-radius: 4px; -} - -::-webkit-scrollbar-thumb:hover { - background: #0056b3; -} - -/* ================================= - THEME TOGGLE - BOTÃO ELEGANTE - ================================= */ - -.theme-toggle-container { - margin-left: 8px; -} - -#theme-toggle { - border-color: #6c757d !important; - color: #6c757d !important; - transition: all 0.3s ease; - min-width: 42px; - display: flex; - align-items: center; - justify-content: center; -} - -#theme-toggle:hover { - background-color: #007bff !important; - border-color: #007bff !important; - color: white !important; - transform: translateY(-1px); -} - -#theme-toggle:focus { - box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25); -} - -#theme-icon { - font-size: 0.875rem; - transition: transform 0.3s ease; -} - -#theme-toggle:hover #theme-icon { - transform: rotate(180deg); -} - -/* Estados do tema */ -[data-theme="light"] #theme-icon { - color: #ffc107; /* Sol amarelo */ -} - -[data-theme="dark"] #theme-icon { - color: #17a2b8; /* Lua azul */ -} - -/* Responsive - esconder texto em telas pequenas */ -@media (max-width: 768px) { - #theme-text { - display: none !important; - } - - .theme-toggle-container { - margin-left: 4px; - } -} - -/* Utility Classes */ -.text-gradient { - background: linear-gradient(135deg, var(--qr-primary) 0%, var(--qr-accent) 100%); - -webkit-background-clip: text; - -webkit-text-fill-color: transparent; - background-clip: text; -} - -.shadow-custom { - box-shadow: 0 8px 25px rgba(0, 123, 255, 0.15); -} - -.border-gradient { - border: 2px solid; - border-image: linear-gradient(135deg, var(--qr-primary) 0%, var(--qr-accent) 100%) 1; -} - -/* Print Styles */ -@media print { - .ad-container, - .btn, - .navbar, - footer { - display: none !important; - } - - #qr-preview img { - max-width: 300px; - max-height: 300px; - } -} - -/* ================================= - CORREÇÕES CRÍTICAS DE CONTRASTE E VISIBILIDADE - QR Rapido - Acessibilidade WCAG 2.1 AA - ================================= */ - -/* ================================= - DICAS PARA QR MAIS RÁPIDOS - CORREÇÃO CRÍTICA - ================================= */ - -.card.bg-light { - background-color: #ffffff !important; - border: 1px solid #dee2e6 !important; - box-shadow: 0 2px 4px rgba(0,0,0,0.08); -} - -.card.bg-light .card-header { - background-color: #f8f9fb !important; - color: #212529 !important; - border-bottom: 1px solid #dee2e6; - font-weight: 600; -} - -.card.bg-light .card-body { - background-color: #ffffff !important; - color: #212529 !important; -} - -.card.bg-light .text-muted { - color: #495057 !important; /* Mais escuro para melhor contraste */ -} - -/* ================================= - CONTENT HINTS - VISIBILIDADE GARANTIDA - ================================= */ - -#content-hints, .form-text { - color: #495057 !important; - font-weight: 500; - background: rgba(0, 123, 255, 0.05); - padding: 6px 12px; - border-radius: 4px; - border-left: 3px solid #007bff; - font-size: 0.875rem; - margin-top: 4px; - display: block !important; -} - -/* ================================= - TEXT MUTED - CONTRASTE GLOBAL - ================================= */ - -.text-muted { - color: #495057 !important; /* Mais escuro que o padrão Bootstrap */ -} - -/* ================================= - CARDS DE ESTATÍSTICAS - VISIBILIDADE - ================================= */ - -.card.border-success, .card.border-primary, .card.border-warning { - background: #ffffff; - border-width: 2px !important; - box-shadow: 0 4px 6px rgba(0,0,0,0.08); -} - -.card.border-success .card-body { - background: linear-gradient(145deg, #ffffff 0%, #f0fff4 100%); -} - -.card.border-primary .card-body { - background: linear-gradient(145deg, #ffffff 0%, #eff6ff 100%); -} - -.card.border-warning .card-body { - background: linear-gradient(145deg, #ffffff 0%, #fffbeb 100%); -} - -.card .text-success, .card .text-primary, .card .text-warning { - font-weight: 700 !important; -} - -/* ================================= - AD CONTAINERS - NUNCA INVISÍVEL - ================================= */ - -.ad-container { - background: #ffffff !important; - border: 1px solid #dee2e6 !important; - padding: 15px; - text-align: center; - border-radius: 8px; - box-shadow: 0 2px 4px rgba(0,0,0,0.05); - margin: 20px 0; - position: relative; -} - -.ad-label { - color: #495057 !important; - font-size: 0.75rem; - text-transform: uppercase; - font-weight: 600; - letter-spacing: 0.5px; - margin-bottom: 8px; -} - -/* ================================= - PLACEHOLDER QR - CONTRASTE SUPERIOR - ================================= */ - -.placeholder-qr { - border: 2px dashed #007bff !important; - background: #f8f9fb !important; - padding: 40px 20px; - text-align: center; - border-radius: 8px; - transition: all 0.3s ease; -} - -.placeholder-qr:hover { - border-color: #0056b3; - background: #e7f3ff; -} - -.placeholder-qr .text-muted { - color: #495057 !important; -} - -/* ================================= - BOTÕES - MANTENDO IDENTIDADE AZUL - ================================= */ - -.btn-primary { - background-color: #007bff !important; - border-color: #007bff !important; - font-weight: 600; -} - -.btn-primary:hover { - background-color: #0056b3 !important; - border-color: #0056b3 !important; -} - -.btn-outline-primary { - color: #007bff !important; - border-color: #007bff !important; - font-weight: 600; -} - -.btn-outline-primary:hover { - background-color: #007bff !important; - border-color: #007bff !important; - color: #ffffff !important; -} - -/* ================================= - THEME SYSTEM - BASEADO EM DATA-THEME - Tema escuro ativado via data-theme="dark" OU preferência do sistema - ================================= */ - -html[data-theme="dark"] { - /* Base cards */ - .card { - background-color: #2d3748 !important; - color: #e2e8f0 !important; - border-color: #4a5568 !important; - } - - /* Card Dicas para QR Mais Rápidos */ - .card.bg-light { - background-color: #2d3748 !important; - border-color: #4a5568 !important; - color: #e2e8f0 !important; - } - - .card.bg-light .card-header { - background-color: #4a5568 !important; - color: #e2e8f0 !important; - border-bottom-color: #718096; - } - - .card.bg-light .card-body { - background-color: #2d3748 !important; - color: #e2e8f0 !important; - } - - .card.bg-light .text-muted { - color: #cbd5e0 !important; - } - - .card.bg-light .list-unstyled li { - color: #e2e8f0 !important; - } - - /* Content Hints - Dark Mode */ - #content-hints, .form-text { - color: #e2e8f0 !important; - background: rgba(66, 153, 225, 0.15); - border-left-color: #4dabf7; - } - - /* Text Muted - Dark Mode */ - .text-muted { - color: #cbd5e0 !important; - } - - .small.text-muted, small.text-muted { - color: #a0aec0 !important; - } - - /* Cards Estatísticas - Dark Mode */ - .card.border-success, .card.border-primary, .card.border-warning { - background-color: #2d3748 !important; - border-color: inherit !important; - color: #e2e8f0 !important; - } - - .card.border-success .card-body, - .card.border-primary .card-body, - .card.border-warning .card-body { - background: #2d3748 !important; - color: #e2e8f0 !important; - } - - .card.border-success { - border-color: #48bb78 !important; - } - - .card.border-primary { - border-color: #4dabf7 !important; - } - - .card.border-warning { - border-color: #ed8936 !important; - } - - /* Ad Containers - Dark Mode */ - .ad-container { - background: #2d3748 !important; - border-color: #4a5568 !important; - color: #e2e8f0 !important; - } - - .ad-label { - color: #cbd5e0 !important; - } - - /* Placeholder QR - Dark Mode */ - .placeholder-qr { - background: #4a5568 !important; - border-color: #4dabf7 !important; - color: #e2e8f0 !important; - } - - .placeholder-qr:hover { - background: #2d3748; - border-color: #63b3ed; - } - - .placeholder-qr .text-muted { - color: #cbd5e0 !important; - } - - /* Form Controls - Dark Mode Completo */ - .form-control, .form-select { - background-color: #4a5568 !important; - border-color: #718096 !important; - color: #e2e8f0 !important; - } - - .form-control:focus, .form-select:focus { - background-color: #4a5568 !important; - border-color: #4dabf7 !important; - box-shadow: 0 0 0 0.2rem rgba(77, 171, 247, 0.25) !important; - color: #e2e8f0 !important; - } - - .form-control::placeholder { - color: #a0aec0 !important; - opacity: 1; - } - - .form-label { - color: #e2e8f0 !important; - font-weight: 600; - } - - /* Botões - Dark Mode */ - .btn-primary { - background-color: #4dabf7 !important; - border-color: #4dabf7 !important; - color: white !important; - } - - .btn-primary:hover { - background-color: #3182ce !important; - border-color: #3182ce !important; - color: white !important; - } - - /* Botões de login específicos - tema escuro */ - .card-body .btn-danger { - background-color: #f56565 !important; - background-image: none !important; - border-color: #f56565 !important; - color: white !important; - } - - .card-body .btn-danger:hover { - background-color: #e53e3e !important; - background-image: none !important; - border-color: #e53e3e !important; - color: white !important; - } - - .card-body .btn-primary { - background-color: #4dabf7 !important; - background-image: none !important; - border-color: #4dabf7 !important; - color: white !important; - } - - .card-body .btn-primary:hover { - background-color: #3182ce !important; - background-image: none !important; - border-color: #3182ce !important; - color: white !important; - } - - /* Body e elementos base */ - body { - background-color: #1a202c !important; - color: #e2e8f0 !important; - } - - /* Navbar dark mode */ - .navbar-light { - background-color: #2d3748 !important; - border-bottom-color: #4a5568 !important; - } - - .navbar-light .navbar-brand { - color: #e2e8f0 !important; - } - - /* Dropdown menus escuro */ - .btn-outline-secondary { - background-color: #4a5568 !important; - border-color: #718096 !important; - color: #e2e8f0 !important; - } - - .btn-outline-secondary:hover { - background-color: #4dabf7 !important; - border-color: #4dabf7 !important; - color: #ffffff !important; - } - - /* Links e textos diversos */ - a { - color: #4dabf7 !important; - } - - a:hover { - color: #63b3ed !important; - } - - /* Premium features */ - .premium-feature { - background: linear-gradient(135deg, #4a5568 0%, #2d3748 100%); - border-left-color: #ed8936; - color: #e2e8f0; - } - - /* Accordion */ - .accordion-button { - background: #4a5568 !important; - color: #e2e8f0 !important; - } - - .accordion-button:not(.collapsed) { - background: #4dabf7 !important; - color: #ffffff !important; - } - - /* Dropdown menus */ - .dropdown-menu { - background-color: #2d3748 !important; - border-color: #4a5568 !important; - } - - .dropdown-item { - color: #e2e8f0 !important; - } - - .dropdown-item:hover { - background-color: #4dabf7 !important; - color: #ffffff !important; - } - - /* Correções adicionais para elementos específicos */ - .small { - color: #a0aec0 !important; - } - - .badge { - background-color: #4a5568 !important; - color: #e2e8f0 !important; - } - - .badge.bg-success { - background-color: #48bb78 !important; - color: #ffffff !important; - } - - .badge.bg-primary { - background-color: #4dabf7 !important; - color: #ffffff !important; - } - - .badge.bg-warning { - background-color: #ed8936 !important; - color: #ffffff !important; - } - - /* Alerts */ - .alert { - background-color: #4a5568 !important; - border-color: #718096 !important; - color: #e2e8f0 !important; - } - - .alert-success { - background-color: #2f855a !important; - border-color: #48bb78 !important; - color: #ffffff !important; - } - - .alert-info { - background-color: #2b6cb0 !important; - border-color: #4dabf7 !important; - color: #ffffff !important; - } - - .alert-warning { - background-color: #c05621 !important; - border-color: #ed8936 !important; - color: #ffffff !important; - } - - /* Tables */ - .table { - background-color: #2d3748 !important; - color: #e2e8f0 !important; - } - - .table th, .table td { - border-color: #4a5568 !important; - color: #e2e8f0 !important; - } - - .table-hover tbody tr:hover { - background-color: #4a5568 !important; - } - - /* List groups */ - .list-group-item { - background-color: #2d3748 !important; - border-color: #4a5568 !important; - color: #e2e8f0 !important; - } - - /* Progress bars */ - .progress { - background-color: #4a5568 !important; - } - - /* Footer */ - footer { - background: linear-gradient(135deg, #1a202c 0%, #2d3748 100%) !important; - color: #e2e8f0 !important; - } - - footer a { - color: #4dabf7 !important; - } - - footer a:hover { - color: #63b3ed !important; - } - - /* Hero section */ - .bg-gradient-primary { - background: linear-gradient(135deg, #4dabf7 0%, #3182ce 100%) !important; - } - - /* Generation timer e speed badge */ - .generation-timer { - background: #4a5568 !important; - border-color: #4dabf7 !important; - color: #e2e8f0 !important; - } - - .generation-timer.active { - background: #4dabf7 !important; - color: #ffffff !important; - } -} - -/* ================================= - TEMA CLARO EXPLÍCITO - Forçar tema claro mesmo se o sistema estiver escuro - ================================= */ - -html[data-theme="light"] { - /* Body e elementos base */ - body { - background-color: #ffffff !important; - color: #212529 !important; - } - - /* Cards */ - .card { - background-color: #ffffff !important; - color: #212529 !important; - border-color: #dee2e6 !important; - } - - .card.bg-light { - background-color: #ffffff !important; - border-color: #dee2e6 !important; - color: #212529 !important; - } - - .card.bg-light .card-header { - background-color: #f8f9fb !important; - color: #212529 !important; - border-bottom-color: #dee2e6; - } - - .card.bg-light .card-body { - background-color: #ffffff !important; - color: #212529 !important; - } - - .card.bg-light .text-muted { - color: #495057 !important; - } - - /* Text muted */ - .text-muted { - color: #495057 !important; - } - - .small.text-muted, small.text-muted { - color: #6c757d !important; - } - - /* Content hints */ - #content-hints, .form-text { - color: #495057 !important; - background: rgba(0, 123, 255, 0.05); - border-left-color: #007bff; - } - - /* Form controls */ - .form-control, .form-select { - background-color: #ffffff !important; - border-color: #ced4da !important; - color: #495057 !important; - } - - .form-control:focus, .form-select:focus { - border-color: #007bff !important; - box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25) !important; - color: #495057 !important; - } - - .form-control::placeholder { - color: #6c757d !important; - opacity: 1; - } - - .form-label { - color: #212529 !important; - font-weight: 600; - } - - /* Cards de estatísticas */ - .card.border-success, .card.border-primary, .card.border-warning { - background-color: #ffffff !important; - color: #212529 !important; - } - - .card.border-success .card-body, - .card.border-primary .card-body, - .card.border-warning .card-body { - background: linear-gradient(145deg, #ffffff 0%, #f8f9fa 100%) !important; - color: #212529 !important; - } - - /* Ad containers */ - .ad-container { - background: #ffffff !important; - border-color: #dee2e6 !important; - color: #212529 !important; - } - - .ad-label { - color: #495057 !important; - } - - /* Placeholder QR */ - .placeholder-qr { - background: #f8f9fb !important; - border-color: #007bff !important; - color: #212529 !important; - } - - .placeholder-qr:hover { - background: #e7f3ff !important; - border-color: #0056b3 !important; - } - - .placeholder-qr .text-muted { - color: #495057 !important; - } - - /* Navbar */ - .navbar-light { - background-color: #ffffff !important; - border-bottom-color: #dee2e6 !important; - } - - .navbar-light .navbar-brand { - color: #212529 !important; - } - - /* Hero section */ - .bg-gradient-primary { - background: linear-gradient(135deg, #007BFF 0%, #0056B3 100%) !important; - } - - /* Links */ - a { - color: #007bff !important; - } - - a:hover { - color: #0056b3 !important; - } - - /* Botões */ - .btn-primary { - background-color: #007bff !important; - border-color: #007bff !important; - color: white !important; - } - - .btn-primary:hover { - background-color: #0056b3 !important; - border-color: #0056b3 !important; - color: white !important; - } - - /* Botões de login específicos - tema claro */ - .card-body .btn-danger { - background-color: #dc3545 !important; - background-image: none !important; - border-color: #dc3545 !important; - color: white !important; - } - - .card-body .btn-danger:hover { - background-color: #c82333 !important; - background-image: none !important; - border-color: #c82333 !important; - color: white !important; - } - - .card-body .btn-primary { - background-color: #007bff !important; - background-image: none !important; - border-color: #007bff !important; - color: white !important; - } - - .card-body .btn-primary:hover { - background-color: #0056b3 !important; - background-image: none !important; - border-color: #0056b3 !important; - color: white !important; - } - - /* Generation timer */ - .generation-timer { - background: #f8f9fa !important; - border-color: #007bff !important; - color: #212529 !important; - } - - .generation-timer.active { - background: #007bff !important; - color: #ffffff !important; - } - - /* Footer */ - footer { - background: linear-gradient(135deg, #343A40 0%, #2c3e50 100%) !important; - color: #ffffff !important; - } - - footer a { - color: #007bff !important; - } - - footer a:hover { - color: #0056b3 !important; - } -} - -/* ============================================ */ -/* SISTEMA DE FLUXO ASCENDENTE PROGRESSIVO */ -/* ============================================ */ - -/* Classe para destacar campo inicial */ -.qr-field-highlight { - border: 2px solid #3B82F6 !important; - box-shadow: 0 0 5px rgba(59, 130, 246, 0.3) !important; - transition: all 0.3s ease !important; -} - -/* Feedback visual para campos obrigatórios */ -.required-field:invalid, -.form-control:invalid { - border-color: #dc3545 !important; - box-shadow: 0 0 0 0.25rem rgba(220, 53, 69, 0.25) !important; -} - -.required-field:valid, -.form-control:valid { - border-color: #28a745 !important; - box-shadow: 0 0 0 0.25rem rgba(40, 167, 69, 0.25) !important; -} - -/* Estados de campos desabilitados */ -.disabled-section { - opacity: 0.6; - pointer-events: none; - transition: all 0.3s ease; -} - -/* Transições suaves para seções */ -.qr-section { - transition: all 0.3s ease; -} - -/* Tooltip para campos bloqueados */ -.field-blocked-hint { - color: #f59e0b; - font-size: 0.875rem; - margin-top: 0.25rem; - display: flex; - align-items: center; - gap: 0.25rem; - opacity: 0; - animation: fadeIn 0.3s ease forwards; -} - -.field-blocked-hint.show { - opacity: 1; -} - -@keyframes fadeIn { - from { opacity: 0; transform: translateY(-5px); } - to { opacity: 1; transform: translateY(0); } -} - -/* Tema escuro - ajustes para sistema de fluxo */ -html[data-theme="dark"] .qr-field-highlight { - border-color: #60A5FA !important; - box-shadow: 0 0 5px rgba(96, 165, 250, 0.3) !important; -} - -html[data-theme="dark"] .required-field:invalid, -html[data-theme="dark"] .form-control:invalid { - border-color: #ef4444 !important; - box-shadow: 0 0 0 0.25rem rgba(239, 68, 68, 0.25) !important; -} - -html[data-theme="dark"] .required-field:valid, -html[data-theme="dark"] .form-control:valid { - border-color: #22c55e !important; - box-shadow: 0 0 0 0.25rem rgba(34, 197, 94, 0.25) !important; -} - -/* ================================= - UX IMPROVEMENTS - BUTTON READY STATES - ================================= */ - -.btn-pulse { - animation: buttonPulse 1.5s ease-in-out infinite; - box-shadow: 0 0 10px rgba(0, 123, 255, 0.4) !important; -} - -.btn-ready { - background: linear-gradient(45deg, #007bff, #00d4ff) !important; - border-color: #007bff !important; - position: relative; - overflow: hidden; - transform: scale(1.02); - transition: all 0.3s ease; -} - -.btn-ready::before { - content: ''; - position: absolute; - top: 0; - left: -100%; - width: 100%; - height: 100%; - background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent); - transition: left 0.6s ease; -} - -.btn-ready:hover::before { - left: 100%; -} - -@keyframes buttonPulse { - 0% { - transform: scale(1.02); - box-shadow: 0 0 10px rgba(0, 123, 255, 0.4); - } - 50% { - transform: scale(1.05); - box-shadow: 0 0 20px rgba(0, 123, 255, 0.6); - } - 100% { - transform: scale(1.02); - box-shadow: 0 0 10px rgba(0, 123, 255, 0.4); - } -} - -/* Dark mode adjustments for button ready states */ -html[data-theme="dark"] .btn-pulse { - box-shadow: 0 0 10px rgba(96, 165, 250, 0.4) !important; -} - -html[data-theme="dark"] .btn-ready { - background: linear-gradient(45deg, #3b82f6, #60a5fa) !important; -} - -html[data-theme="dark"] @keyframes buttonPulse { - 0% { - transform: scale(1.02); - box-shadow: 0 0 10px rgba(96, 165, 250, 0.4); - } - 50% { - transform: scale(1.05); - box-shadow: 0 0 20px rgba(96, 165, 250, 0.6); - } - 100% { - transform: scale(1.02); - box-shadow: 0 0 10px rgba(96, 165, 250, 0.4); - } +/* QR Rapido Custom Theme */ +:root { + --qr-primary: #007BFF; + --qr-secondary: #28A745; + --qr-accent: #FF6B35; + --qr-warning: #FFC107; + --qr-success: #28A745; + --qr-danger: #DC3545; + --qr-dark: #343A40; + --qr-light: #F8F9FA; +} + +/* Global Styles */ +body { + font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; + line-height: 1.6; +} + +.bg-gradient-primary { + background: linear-gradient(135deg, var(--qr-primary) 0%, #0056B3 100%); +} + +/* Generation Timer Styles */ +.generation-timer { + display: inline-flex; + align-items: center; + gap: 0.5rem; + padding: 0.25rem 0.75rem; + background: #f8f9fa; + border-radius: 20px; + border: 2px solid var(--qr-primary); + transition: all 0.3s ease; +} + +.generation-timer.active { + background: var(--qr-primary); + color: white; + animation: pulse 1.5s infinite; +} + +/* Speed Badge Animation */ +.speed-badge { + animation: slideInRight 0.5s ease-out; +} + +@keyframes pulse { + 0% { transform: scale(1); } + 50% { transform: scale(1.05); } + 100% { transform: scale(1); } +} + +@keyframes slideInRight { + from { + transform: translateX(20px); + opacity: 0; + } + to { + transform: translateX(0); + opacity: 1; + } +} + +/* QR Preview Placeholder - REMOVIDO: Substituído por versão com melhor contraste */ + +/* Logo and Branding */ +.navbar-brand svg { + filter: drop-shadow(2px 2px 4px rgba(0,0,0,0.1)); + transition: transform 0.3s ease; +} + +.navbar-brand:hover svg { + transform: scale(1.05); +} + +/* QR Preview Image */ +#qr-preview img { + border-radius: 8px; + box-shadow: 0 4px 8px rgba(0,0,0,0.1); + transition: transform 0.3s ease; + max-width: 100%; + height: auto; +} + +#qr-preview img:hover { + transform: scale(1.02); +} + +/* Card Hover Effects */ +.card { + transition: all 0.3s ease; + border-radius: 12px; +} + +.card:hover { + box-shadow: 0 8px 25px rgba(0,0,0,0.1); + transform: translateY(-2px); +} + +/* Button Styles */ +.btn-check:checked + .btn { + background-color: var(--qr-primary); + border-color: var(--qr-primary); + color: white; +} + +.btn-primary { + background: linear-gradient(135deg, var(--qr-primary) 0%, #0056B3 100%); + border: none; + color: white !important; + transition: all 0.3s ease; +} + +.btn-primary:hover { + background: linear-gradient(135deg, #0056B3 0%, var(--qr-primary) 100%); + color: white !important; + transform: translateY(-1px); + box-shadow: 0 4px 12px rgba(0, 123, 255, 0.3); +} + +/* Botões de login específicos - cores sólidas */ +.card-body .btn-danger { + background-color: var(--qr-danger) !important; + background-image: none !important; + border-color: var(--qr-danger) !important; + color: white !important; +} + +.card-body .btn-danger:hover { + background-color: #C82333 !important; + background-image: none !important; + border-color: #C82333 !important; + color: white !important; +} + +.card-body .btn-primary { + background-color: var(--qr-primary) !important; + background-image: none !important; + border-color: var(--qr-primary) !important; + color: white !important; +} + +.card-body .btn-primary:hover { + background-color: #0056B3 !important; + background-image: none !important; + border-color: #0056B3 !important; + color: white !important; +} + +/* Form Controls */ +.form-control:focus, +.form-select:focus { + border-color: var(--qr-primary); + box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25); +} + +.form-control-lg { + border-radius: 8px; +} + +/* Speed Statistics Cards */ +.card.border-success { + border-color: var(--qr-success) !important; +} + +.card.border-primary { + border-color: var(--qr-primary) !important; +} + +.card.border-warning { + border-color: var(--qr-warning) !important; +} + +/* Generation Stats Animation */ +.generation-stats { + animation: slideInRight 0.5s ease-out; +} + +/* Ad Container Styles - REMOVIDO: Substituído por versão com melhor contraste */ + +.ad-free-notice { + text-align: center; + border-left: 4px solid var(--qr-success); + background: linear-gradient(135deg, #d4edda 0%, #c3e6cb 100%); + border-radius: 8px; +} + +.ad-free-notice .fas { + color: var(--qr-warning); +} + +/* Loading Animations */ +.spinner-border-sm { + width: 1rem; + height: 1rem; +} + +/* Progress Bar for QR Generation */ +.progress { + height: 4px; + border-radius: 2px; + background-color: #e9ecef; +} + +.progress-bar { + background: linear-gradient(90deg, var(--qr-primary), var(--qr-accent)); +} + +/* Speed Tips */ +.list-unstyled li { + padding: 0.25rem 0; + transition: all 0.2s ease; +} + +.list-unstyled li:hover { + padding-left: 0.5rem; + color: var(--qr-primary); +} + +/* Responsive Design */ +@media (max-width: 768px) { + .generation-timer { + font-size: 0.875rem; + padding: 0.2rem 0.5rem; + } + + .speed-badge .badge { + font-size: 0.75rem; + } + + .placeholder-qr { + padding: 2rem 1rem; + } + + .card-body { + padding: 1rem; + } + + .ad-container { + margin: 10px 0; + padding: 10px; + } +} + +/* Premium Features Styling */ +.premium-feature { + background: linear-gradient(135deg, #fff3cd 0%, #ffeaa7 100%); + border-left: 4px solid var(--qr-warning); + padding: 1rem; + border-radius: 8px; + margin: 1rem 0; +} + +.premium-badge { + background: linear-gradient(135deg, var(--qr-warning) 0%, #f39c12 100%); + color: var(--qr-dark); + border-radius: 20px; + padding: 0.25rem 0.75rem; + font-size: 0.75rem; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.5px; +} + +/* Accordion Customization */ +.accordion-button { + background: var(--qr-light); + border: none; + border-radius: 8px !important; +} + +.accordion-button:not(.collapsed) { + background: var(--qr-primary); + color: white; +} + +.accordion-button:focus { + box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25); +} + +/* Footer Styling */ +footer { + background: linear-gradient(135deg, var(--qr-dark) 0%, #2c3e50 100%); +} + +footer a { + text-decoration: none; + transition: color 0.3s ease; +} + +footer a:hover { + color: var(--qr-primary) !important; +} + +/* Language Selector */ +.dropdown-toggle::after { + margin-left: 0.5rem; +} + +.dropdown-menu { + border-radius: 8px; + box-shadow: 0 4px 12px rgba(0,0,0,0.1); + border: none; +} + +.dropdown-item { + transition: all 0.2s ease; + border-radius: 4px; + margin: 2px 4px; +} + +.dropdown-item:hover { + background: var(--qr-primary); + color: white; +} + +/* Custom Scrollbar */ +::-webkit-scrollbar { + width: 8px; +} + +::-webkit-scrollbar-track { + background: #f1f1f1; + border-radius: 4px; +} + +::-webkit-scrollbar-thumb { + background: var(--qr-primary); + border-radius: 4px; +} + +::-webkit-scrollbar-thumb:hover { + background: #0056b3; +} + +/* ================================= + THEME TOGGLE - BOTÃO ELEGANTE + ================================= */ + +.theme-toggle-container { + margin-left: 8px; +} + +#theme-toggle { + border-color: #6c757d !important; + color: #6c757d !important; + transition: all 0.3s ease; + min-width: 42px; + display: flex; + align-items: center; + justify-content: center; +} + +#theme-toggle:hover { + background-color: #007bff !important; + border-color: #007bff !important; + color: white !important; + transform: translateY(-1px); +} + +#theme-toggle:focus { + box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25); +} + +#theme-icon { + font-size: 0.875rem; + transition: transform 0.3s ease; +} + +#theme-toggle:hover #theme-icon { + transform: rotate(180deg); +} + +/* Estados do tema */ +[data-theme="light"] #theme-icon { + color: #ffc107; /* Sol amarelo */ +} + +[data-theme="dark"] #theme-icon { + color: #17a2b8; /* Lua azul */ +} + +/* Responsive - esconder texto em telas pequenas */ +@media (max-width: 768px) { + #theme-text { + display: none !important; + } + + .theme-toggle-container { + margin-left: 4px; + } +} + +/* Utility Classes */ +.text-gradient { + background: linear-gradient(135deg, var(--qr-primary) 0%, var(--qr-accent) 100%); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; +} + +.shadow-custom { + box-shadow: 0 8px 25px rgba(0, 123, 255, 0.15); +} + +.border-gradient { + border: 2px solid; + border-image: linear-gradient(135deg, var(--qr-primary) 0%, var(--qr-accent) 100%) 1; +} + +/* Print Styles */ +@media print { + .ad-container, + .btn, + .navbar, + footer { + display: none !important; + } + + #qr-preview img { + max-width: 300px; + max-height: 300px; + } +} + +/* ================================= + CORREÇÕES CRÍTICAS DE CONTRASTE E VISIBILIDADE + QR Rapido - Acessibilidade WCAG 2.1 AA + ================================= */ + +/* ================================= + DICAS PARA QR MAIS RÁPIDOS - CORREÇÃO CRÍTICA + ================================= */ + +.card.bg-light { + background-color: #ffffff !important; + border: 1px solid #dee2e6 !important; + box-shadow: 0 2px 4px rgba(0,0,0,0.08); +} + +.card.bg-light .card-header { + background-color: #f8f9fb !important; + color: #212529 !important; + border-bottom: 1px solid #dee2e6; + font-weight: 600; +} + +.card.bg-light .card-body { + background-color: #ffffff !important; + color: #212529 !important; +} + +.card.bg-light .text-muted { + color: #495057 !important; /* Mais escuro para melhor contraste */ +} + +/* ================================= + CONTENT HINTS - VISIBILIDADE GARANTIDA + ================================= */ + +#content-hints, .form-text { + color: #495057 !important; + font-weight: 500; + background: rgba(0, 123, 255, 0.05); + padding: 6px 12px; + border-radius: 4px; + border-left: 3px solid #007bff; + font-size: 0.875rem; + margin-top: 4px; + display: block !important; +} + +/* ================================= + TEXT MUTED - CONTRASTE GLOBAL + ================================= */ + +.text-muted { + color: #495057 !important; /* Mais escuro que o padrão Bootstrap */ +} + +/* ================================= + CARDS DE ESTATÍSTICAS - VISIBILIDADE + ================================= */ + +.card.border-success, .card.border-primary, .card.border-warning { + background: #ffffff; + border-width: 2px !important; + box-shadow: 0 4px 6px rgba(0,0,0,0.08); +} + +.card.border-success .card-body { + background: linear-gradient(145deg, #ffffff 0%, #f0fff4 100%); +} + +.card.border-primary .card-body { + background: linear-gradient(145deg, #ffffff 0%, #eff6ff 100%); +} + +.card.border-warning .card-body { + background: linear-gradient(145deg, #ffffff 0%, #fffbeb 100%); +} + +.card .text-success, .card .text-primary, .card .text-warning { + font-weight: 700 !important; +} + +/* ================================= + AD CONTAINERS - NUNCA INVISÍVEL + ================================= */ + +.ad-container { + background: #ffffff !important; + border: 1px solid #dee2e6 !important; + padding: 15px; + text-align: center; + border-radius: 8px; + box-shadow: 0 2px 4px rgba(0,0,0,0.05); + margin: 20px 0; + position: relative; +} + +.ad-label { + color: #495057 !important; + font-size: 0.75rem; + text-transform: uppercase; + font-weight: 600; + letter-spacing: 0.5px; + margin-bottom: 8px; +} + +/* ================================= + PLACEHOLDER QR - CONTRASTE SUPERIOR + ================================= */ + +.placeholder-qr { + border: 2px dashed #007bff !important; + background: #f8f9fb !important; + padding: 40px 20px; + text-align: center; + border-radius: 8px; + transition: all 0.3s ease; +} + +.placeholder-qr:hover { + border-color: #0056b3; + background: #e7f3ff; +} + +.placeholder-qr .text-muted { + color: #495057 !important; +} + +/* ================================= + BOTÕES - MANTENDO IDENTIDADE AZUL + ================================= */ + +.btn-primary { + background-color: #007bff !important; + border-color: #007bff !important; + font-weight: 600; +} + +.btn-primary:hover { + background-color: #0056b3 !important; + border-color: #0056b3 !important; +} + +.btn-outline-primary { + color: #007bff !important; + border-color: #007bff !important; + font-weight: 600; +} + +.btn-outline-primary:hover { + background-color: #007bff !important; + border-color: #007bff !important; + color: #ffffff !important; +} + +/* ================================= + THEME SYSTEM - BASEADO EM DATA-THEME + Tema escuro ativado via data-theme="dark" OU preferência do sistema + ================================= */ + +html[data-theme="dark"] { + /* Base cards */ + .card { + background-color: #2d3748 !important; + color: #e2e8f0 !important; + border-color: #4a5568 !important; + } + + /* Card Dicas para QR Mais Rápidos */ + .card.bg-light { + background-color: #2d3748 !important; + border-color: #4a5568 !important; + color: #e2e8f0 !important; + } + + .card.bg-light .card-header { + background-color: #4a5568 !important; + color: #e2e8f0 !important; + border-bottom-color: #718096; + } + + .card.bg-light .card-body { + background-color: #2d3748 !important; + color: #e2e8f0 !important; + } + + .card.bg-light .text-muted { + color: #cbd5e0 !important; + } + + .card.bg-light .list-unstyled li { + color: #e2e8f0 !important; + } + + /* Content Hints - Dark Mode */ + #content-hints, .form-text { + color: #e2e8f0 !important; + background: rgba(66, 153, 225, 0.15); + border-left-color: #4dabf7; + } + + /* Text Muted - Dark Mode */ + .text-muted { + color: #cbd5e0 !important; + } + + .small.text-muted, small.text-muted { + color: #a0aec0 !important; + } + + /* Cards Estatísticas - Dark Mode */ + .card.border-success, .card.border-primary, .card.border-warning { + background-color: #2d3748 !important; + border-color: inherit !important; + color: #e2e8f0 !important; + } + + .card.border-success .card-body, + .card.border-primary .card-body, + .card.border-warning .card-body { + background: #2d3748 !important; + color: #e2e8f0 !important; + } + + .card.border-success { + border-color: #48bb78 !important; + } + + .card.border-primary { + border-color: #4dabf7 !important; + } + + .card.border-warning { + border-color: #ed8936 !important; + } + + /* Ad Containers - Dark Mode */ + .ad-container { + background: #2d3748 !important; + border-color: #4a5568 !important; + color: #e2e8f0 !important; + } + + .ad-label { + color: #cbd5e0 !important; + } + + /* Placeholder QR - Dark Mode */ + .placeholder-qr { + background: #4a5568 !important; + border-color: #4dabf7 !important; + color: #e2e8f0 !important; + } + + .placeholder-qr:hover { + background: #2d3748; + border-color: #63b3ed; + } + + .placeholder-qr .text-muted { + color: #cbd5e0 !important; + } + + /* Form Controls - Dark Mode Completo */ + .form-control, .form-select { + background-color: #4a5568 !important; + border-color: #718096 !important; + color: #e2e8f0 !important; + } + + .form-control:focus, .form-select:focus { + background-color: #4a5568 !important; + border-color: #4dabf7 !important; + box-shadow: 0 0 0 0.2rem rgba(77, 171, 247, 0.25) !important; + color: #e2e8f0 !important; + } + + .form-control::placeholder { + color: #a0aec0 !important; + opacity: 1; + } + + .form-label { + color: #e2e8f0 !important; + font-weight: 600; + } + + /* Botões - Dark Mode */ + .btn-primary { + background-color: #4dabf7 !important; + border-color: #4dabf7 !important; + color: white !important; + } + + .btn-primary:hover { + background-color: #3182ce !important; + border-color: #3182ce !important; + color: white !important; + } + + /* Botões de login específicos - tema escuro */ + .card-body .btn-danger { + background-color: #f56565 !important; + background-image: none !important; + border-color: #f56565 !important; + color: white !important; + } + + .card-body .btn-danger:hover { + background-color: #e53e3e !important; + background-image: none !important; + border-color: #e53e3e !important; + color: white !important; + } + + .card-body .btn-primary { + background-color: #4dabf7 !important; + background-image: none !important; + border-color: #4dabf7 !important; + color: white !important; + } + + .card-body .btn-primary:hover { + background-color: #3182ce !important; + background-image: none !important; + border-color: #3182ce !important; + color: white !important; + } + + /* Body e elementos base */ + body { + background-color: #1a202c !important; + color: #e2e8f0 !important; + } + + /* Navbar dark mode */ + .navbar-light { + background-color: #2d3748 !important; + border-bottom-color: #4a5568 !important; + } + + .navbar-light .navbar-brand { + color: #e2e8f0 !important; + } + + /* Dropdown menus escuro */ + .btn-outline-secondary { + background-color: #4a5568 !important; + border-color: #718096 !important; + color: #e2e8f0 !important; + } + + .btn-outline-secondary:hover { + background-color: #4dabf7 !important; + border-color: #4dabf7 !important; + color: #ffffff !important; + } + + /* Links e textos diversos */ + a { + color: #4dabf7 !important; + } + + a:hover { + color: #63b3ed !important; + } + + /* Premium features */ + .premium-feature { + background: linear-gradient(135deg, #4a5568 0%, #2d3748 100%); + border-left-color: #ed8936; + color: #e2e8f0; + } + + /* Accordion */ + .accordion-button { + background: #4a5568 !important; + color: #e2e8f0 !important; + } + + .accordion-button:not(.collapsed) { + background: #4dabf7 !important; + color: #ffffff !important; + } + + /* Dropdown menus */ + .dropdown-menu { + background-color: #2d3748 !important; + border-color: #4a5568 !important; + } + + .dropdown-item { + color: #e2e8f0 !important; + } + + .dropdown-item:hover { + background-color: #4dabf7 !important; + color: #ffffff !important; + } + + /* Correções adicionais para elementos específicos */ + .small { + color: #a0aec0 !important; + } + + .badge { + background-color: #4a5568 !important; + color: #e2e8f0 !important; + } + + .badge.bg-success { + background-color: #48bb78 !important; + color: #ffffff !important; + } + + .badge.bg-primary { + background-color: #4dabf7 !important; + color: #ffffff !important; + } + + .badge.bg-warning { + background-color: #ed8936 !important; + color: #ffffff !important; + } + + /* Alerts */ + .alert { + background-color: #4a5568 !important; + border-color: #718096 !important; + color: #e2e8f0 !important; + } + + .alert-success { + background-color: #2f855a !important; + border-color: #48bb78 !important; + color: #ffffff !important; + } + + .alert-info { + background-color: #2b6cb0 !important; + border-color: #4dabf7 !important; + color: #ffffff !important; + } + + .alert-warning { + background-color: #c05621 !important; + border-color: #ed8936 !important; + color: #ffffff !important; + } + + /* Tables */ + .table { + background-color: #2d3748 !important; + color: #e2e8f0 !important; + } + + .table th, .table td { + border-color: #4a5568 !important; + color: #e2e8f0 !important; + } + + .table-hover tbody tr:hover { + background-color: #4a5568 !important; + } + + /* List groups */ + .list-group-item { + background-color: #2d3748 !important; + border-color: #4a5568 !important; + color: #e2e8f0 !important; + } + + /* Progress bars */ + .progress { + background-color: #4a5568 !important; + } + + /* Footer */ + footer { + background: linear-gradient(135deg, #1a202c 0%, #2d3748 100%) !important; + color: #e2e8f0 !important; + } + + footer a { + color: #4dabf7 !important; + } + + footer a:hover { + color: #63b3ed !important; + } + + /* Hero section */ + .bg-gradient-primary { + background: linear-gradient(135deg, #4dabf7 0%, #3182ce 100%) !important; + } + + /* Generation timer e speed badge */ + .generation-timer { + background: #4a5568 !important; + border-color: #4dabf7 !important; + color: #e2e8f0 !important; + } + + .generation-timer.active { + background: #4dabf7 !important; + color: #ffffff !important; + } +} + +/* ================================= + TEMA CLARO EXPLÍCITO + Forçar tema claro mesmo se o sistema estiver escuro + ================================= */ + +html[data-theme="light"] { + /* Body e elementos base */ + body { + background-color: #ffffff !important; + color: #212529 !important; + } + + /* Cards */ + .card { + background-color: #ffffff !important; + color: #212529 !important; + border-color: #dee2e6 !important; + } + + .card.bg-light { + background-color: #ffffff !important; + border-color: #dee2e6 !important; + color: #212529 !important; + } + + .card.bg-light .card-header { + background-color: #f8f9fb !important; + color: #212529 !important; + border-bottom-color: #dee2e6; + } + + .card.bg-light .card-body { + background-color: #ffffff !important; + color: #212529 !important; + } + + .card.bg-light .text-muted { + color: #495057 !important; + } + + /* Text muted */ + .text-muted { + color: #495057 !important; + } + + .small.text-muted, small.text-muted { + color: #6c757d !important; + } + + /* Content hints */ + #content-hints, .form-text { + color: #495057 !important; + background: rgba(0, 123, 255, 0.05); + border-left-color: #007bff; + } + + /* Form controls */ + .form-control, .form-select { + background-color: #ffffff !important; + border-color: #ced4da !important; + color: #495057 !important; + } + + .form-control:focus, .form-select:focus { + border-color: #007bff !important; + box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25) !important; + color: #495057 !important; + } + + .form-control::placeholder { + color: #6c757d !important; + opacity: 1; + } + + .form-label { + color: #212529 !important; + font-weight: 600; + } + + /* Cards de estatísticas */ + .card.border-success, .card.border-primary, .card.border-warning { + background-color: #ffffff !important; + color: #212529 !important; + } + + .card.border-success .card-body, + .card.border-primary .card-body, + .card.border-warning .card-body { + background: linear-gradient(145deg, #ffffff 0%, #f8f9fa 100%) !important; + color: #212529 !important; + } + + /* Ad containers */ + .ad-container { + background: #ffffff !important; + border-color: #dee2e6 !important; + color: #212529 !important; + } + + .ad-label { + color: #495057 !important; + } + + /* Placeholder QR */ + .placeholder-qr { + background: #f8f9fb !important; + border-color: #007bff !important; + color: #212529 !important; + } + + .placeholder-qr:hover { + background: #e7f3ff !important; + border-color: #0056b3 !important; + } + + .placeholder-qr .text-muted { + color: #495057 !important; + } + + /* Navbar */ + .navbar-light { + background-color: #ffffff !important; + border-bottom-color: #dee2e6 !important; + } + + .navbar-light .navbar-brand { + color: #212529 !important; + } + + /* Hero section */ + .bg-gradient-primary { + background: linear-gradient(135deg, #007BFF 0%, #0056B3 100%) !important; + } + + /* Links */ + a { + color: #007bff !important; + } + + a:hover { + color: #0056b3 !important; + } + + /* Botões */ + .btn-primary { + background-color: #007bff !important; + border-color: #007bff !important; + color: white !important; + } + + .btn-primary:hover { + background-color: #0056b3 !important; + border-color: #0056b3 !important; + color: white !important; + } + + /* Botões de login específicos - tema claro */ + .card-body .btn-danger { + background-color: #dc3545 !important; + background-image: none !important; + border-color: #dc3545 !important; + color: white !important; + } + + .card-body .btn-danger:hover { + background-color: #c82333 !important; + background-image: none !important; + border-color: #c82333 !important; + color: white !important; + } + + .card-body .btn-primary { + background-color: #007bff !important; + background-image: none !important; + border-color: #007bff !important; + color: white !important; + } + + .card-body .btn-primary:hover { + background-color: #0056b3 !important; + background-image: none !important; + border-color: #0056b3 !important; + color: white !important; + } + + /* Generation timer */ + .generation-timer { + background: #f8f9fa !important; + border-color: #007bff !important; + color: #212529 !important; + } + + .generation-timer.active { + background: #007bff !important; + color: #ffffff !important; + } + + /* Footer */ + footer { + background: linear-gradient(135deg, #343A40 0%, #2c3e50 100%) !important; + color: #ffffff !important; + } + + footer a { + color: #007bff !important; + } + + footer a:hover { + color: #0056b3 !important; + } +} + +/* ============================================ */ +/* SISTEMA DE FLUXO ASCENDENTE PROGRESSIVO */ +/* ============================================ */ + +/* Classe para destacar campo inicial */ +.qr-field-highlight { + border: 2px solid #3B82F6 !important; + box-shadow: 0 0 5px rgba(59, 130, 246, 0.3) !important; + transition: all 0.3s ease !important; +} + +/* Feedback visual para campos obrigatórios */ +.required-field:invalid, +.form-control:invalid { + border-color: #dc3545 !important; + box-shadow: 0 0 0 0.25rem rgba(220, 53, 69, 0.25) !important; +} + +.required-field:valid, +.form-control:valid { + border-color: #28a745 !important; + box-shadow: 0 0 0 0.25rem rgba(40, 167, 69, 0.25) !important; +} + +/* Estados de campos desabilitados */ +.disabled-section { + opacity: 0.6; + pointer-events: none; + transition: all 0.3s ease; +} + +/* Transições suaves para seções */ +.qr-section { + transition: all 0.3s ease; +} + +/* Tooltip para campos bloqueados */ +.field-blocked-hint { + color: #f59e0b; + font-size: 0.875rem; + margin-top: 0.25rem; + display: flex; + align-items: center; + gap: 0.25rem; + opacity: 0; + animation: fadeIn 0.3s ease forwards; +} + +.field-blocked-hint.show { + opacity: 1; +} + +@keyframes fadeIn { + from { opacity: 0; transform: translateY(-5px); } + to { opacity: 1; transform: translateY(0); } +} + +/* Tema escuro - ajustes para sistema de fluxo */ +html[data-theme="dark"] .qr-field-highlight { + border-color: #60A5FA !important; + box-shadow: 0 0 5px rgba(96, 165, 250, 0.3) !important; +} + +html[data-theme="dark"] .required-field:invalid, +html[data-theme="dark"] .form-control:invalid { + border-color: #ef4444 !important; + box-shadow: 0 0 0 0.25rem rgba(239, 68, 68, 0.25) !important; +} + +html[data-theme="dark"] .required-field:valid, +html[data-theme="dark"] .form-control:valid { + border-color: #22c55e !important; + box-shadow: 0 0 0 0.25rem rgba(34, 197, 94, 0.25) !important; +} + +/* ================================= + UX IMPROVEMENTS - BUTTON READY STATES + ================================= */ + +.btn-pulse { + animation: buttonPulse 1.5s ease-in-out infinite; + box-shadow: 0 0 10px rgba(0, 123, 255, 0.4) !important; +} + +.btn-ready { + background: linear-gradient(45deg, #007bff, #00d4ff) !important; + border-color: #007bff !important; + position: relative; + overflow: hidden; + transform: scale(1.02); + transition: all 0.3s ease; +} + +.btn-ready::before { + content: ''; + position: absolute; + top: 0; + left: -100%; + width: 100%; + height: 100%; + background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent); + transition: left 0.6s ease; +} + +.btn-ready:hover::before { + left: 100%; +} + +@keyframes buttonPulse { + 0% { + transform: scale(1.02); + box-shadow: 0 0 10px rgba(0, 123, 255, 0.4); + } + 50% { + transform: scale(1.05); + box-shadow: 0 0 20px rgba(0, 123, 255, 0.6); + } + 100% { + transform: scale(1.02); + box-shadow: 0 0 10px rgba(0, 123, 255, 0.4); + } +} + +/* Dark mode adjustments for button ready states */ +html[data-theme="dark"] .btn-pulse { + box-shadow: 0 0 10px rgba(96, 165, 250, 0.4) !important; +} + +html[data-theme="dark"] .btn-ready { + background: linear-gradient(45deg, #3b82f6, #60a5fa) !important; +} + +html[data-theme="dark"] @keyframes buttonPulse { + 0% { + transform: scale(1.02); + box-shadow: 0 0 10px rgba(96, 165, 250, 0.4); + } + 50% { + transform: scale(1.05); + box-shadow: 0 0 20px rgba(96, 165, 250, 0.6); + } + 100% { + transform: scale(1.02); + box-shadow: 0 0 10px rgba(96, 165, 250, 0.4); + } } \ No newline at end of file