From 16001eb15b82719ad30c7be69c25cead343ddfb0 Mon Sep 17 00:00:00 2001 From: Ricardo Carneiro <71648276+ricarneiro@users.noreply.github.com> Date: Sun, 1 Jun 2025 21:39:47 -0300 Subject: [PATCH] =?UTF-8?q?feat:=20nova=20vers=C3=A3o=20simplificada?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Controllers/BaseController.cs | 25 +- .../Controllers/ConverterController.cs | 2 +- .../Controllers/EnController.cs | 34 +- .../Controllers/EsController.cs | 32 +- .../Controllers/HomeController.cs | 527 +----------------- .../TextConversionApiController.cs | 181 ++++++ .../Models/SiteConfiguration.cs | 39 +- .../OnlyOneAccessTemplate.csproj | 2 + OnlyOneAccessTemplate/Program.cs | 35 +- .../Services/ConversionService.cs | 18 +- OnlyOneAccessTemplate/Services/Interfaces.cs | 1 - OnlyOneAccessTemplate/Services/SeoService.cs | 24 +- .../Services/SiteConfigurationService .cs | 210 ++----- .../Services/TextConversionApiService.cs | 207 +++++++ .../Services/UpperLowerConversorService.cs | 85 --- OnlyOneAccessTemplate/Views/Home/Index.cshtml | 58 +- .../Views/Shared/_Header.cshtml | 6 +- OnlyOneAccessTemplate/appsettings.json | 4 + OnlyOneAccessTemplate/wwwroot/css/site.css | 81 ++- 19 files changed, 672 insertions(+), 899 deletions(-) create mode 100644 OnlyOneAccessTemplate/Controllers/TextConversionApiController.cs create mode 100644 OnlyOneAccessTemplate/Services/TextConversionApiService.cs delete mode 100644 OnlyOneAccessTemplate/Services/UpperLowerConversorService.cs diff --git a/OnlyOneAccessTemplate/Controllers/BaseController.cs b/OnlyOneAccessTemplate/Controllers/BaseController.cs index 0e21146..0a43cb2 100644 --- a/OnlyOneAccessTemplate/Controllers/BaseController.cs +++ b/OnlyOneAccessTemplate/Controllers/BaseController.cs @@ -6,7 +6,7 @@ using OnlyOneAccessTemplate.Models; namespace OnlyOneAccessTemplate.Controllers { - public abstract class BaseController : Controller + public class BaseController : Controller { protected readonly ISiteConfigurationService _siteConfig; protected readonly ILanguageService _languageService; @@ -65,23 +65,23 @@ namespace OnlyOneAccessTemplate.Controllers // Basic SEO ViewBag.Language = language; ViewBag.Direction = _languageService.GetLanguageDirection(language); - ViewBag.SiteName = config.Seo.SiteName; - ViewBag.SiteDescription = config.Seo.DefaultDescription; + ViewBag.SiteName = config.SiteTitle; + ViewBag.SiteDescription = config.SiteDescription; ViewBag.CanonicalUrl = currentUrl; - ViewBag.Author = config.Seo.Author; - ViewBag.GTMId = config.Seo.GoogleTagManagerId; + ViewBag.Author = "Convert-it Online Team"; + ViewBag.GTMId = _configuration.GetValue("SEO:GoogleTagManagerId"); // Default SEO values - ViewBag.Title = ViewBag.Title ?? config.Seo.DefaultTitle; - ViewBag.Description = ViewBag.Description ?? config.Seo.DefaultDescription; - ViewBag.Keywords = ViewBag.Keywords ?? config.Seo.DefaultKeywords; + ViewBag.Title = ViewBag.Title ?? config.SiteTitle; + ViewBag.Description = ViewBag.Description ?? config.SiteDescription; + ViewBag.Keywords = ViewBag.Keywords ?? config.SiteKeywords; // Open Graph ViewBag.OgTitle = ViewBag.Title; ViewBag.OgDescription = ViewBag.Description; - ViewBag.OgImage = config.Seo.OgImage; + ViewBag.OgImage = config.OgImage; ViewBag.OgLocale = GetOgLocale(language); - ViewBag.TwitterHandle = config.Seo.TwitterHandle; + ViewBag.TwitterHandle = "@convertit_online"; // Language alternatives SetupHreflangUrls(currentUrl, language); @@ -119,8 +119,9 @@ namespace OnlyOneAccessTemplate.Controllers ViewBag.LogoUrl = "/img/logo.png"; ViewBag.BodyClass = $"lang-{language}"; - // Conversion config - ViewBag.ConversionConfig = config.Conversion; + // AdSense config + ViewBag.AdSenseClientId = config.AdSenseClientId; + ViewBag.AdSenseSlotId = config.AdSenseSlotId; } // Nova função para configurar textos específicos do conversor diff --git a/OnlyOneAccessTemplate/Controllers/ConverterController.cs b/OnlyOneAccessTemplate/Controllers/ConverterController.cs index 75e25e7..5540c89 100644 --- a/OnlyOneAccessTemplate/Controllers/ConverterController.cs +++ b/OnlyOneAccessTemplate/Controllers/ConverterController.cs @@ -34,7 +34,7 @@ namespace OnlyOneAccessTemplate.Controllers ["text-case"] = typeof(TextCaseConverterService), ["csv-json"] = typeof(CsvToJsonConverterService), ["image-ocr"] = typeof(ImageToTextConverterService), - ["sentence-converter"] = typeof(UpperLowerConversorService) + ["sentence-converter"] = typeof(ApiBasedSentenceConverterService) // Adicionar novos conversores aqui }; } diff --git a/OnlyOneAccessTemplate/Controllers/EnController.cs b/OnlyOneAccessTemplate/Controllers/EnController.cs index 148043f..014401a 100644 --- a/OnlyOneAccessTemplate/Controllers/EnController.cs +++ b/OnlyOneAccessTemplate/Controllers/EnController.cs @@ -1,20 +1,19 @@ -using global::OnlyOneAccessTemplate.Services; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Caching.Memory; +using OnlyOneAccessTemplate.Models; using OnlyOneAccessTemplate.Services; namespace OnlyOneAccessTemplate.Controllers { - [Route("en")] - public class EnController : HomeController + public class EnController : BaseController { public EnController( ISiteConfigurationService siteConfig, ILanguageService languageService, ISeoService seoService, IMemoryCache cache, - IConfiguration configuration) + IConfiguration configuration) : base(siteConfig, languageService, seoService, cache, configuration) { } @@ -22,22 +21,25 @@ namespace OnlyOneAccessTemplate.Controllers protected override string GetCurrentLanguage() => "en"; [Route("")] - [Route("home")] - public new async Task Index() + [Route("index")] + public IActionResult Index() { - return await base.Index(); + var siteConfig = _siteConfig.GetConfiguration("en"); + ViewBag.PageTitle = siteConfig.SiteTitle; + ViewBag.PageDescription = siteConfig.SiteDescription; + + return View("~/Views/Home/Index.cshtml", siteConfig); } - [Route("about")] - public new async Task About() + [Route("convert-pdf-to-word")] + public IActionResult ConvertPdfToWord() { - return await base.About(); - } - - [Route("contact")] - public new async Task Contact() - { - return await base.Contact(); + var siteConfig = _siteConfig.GetConfiguration("en"); + + SetPageSeo("Convert PDF to Word Online Free", + "Convert your PDF files to Word quickly and free..."); + + return View("~/Views/Home/Index.cshtml", siteConfig); } } } diff --git a/OnlyOneAccessTemplate/Controllers/EsController.cs b/OnlyOneAccessTemplate/Controllers/EsController.cs index cad69ca..ea5b948 100644 --- a/OnlyOneAccessTemplate/Controllers/EsController.cs +++ b/OnlyOneAccessTemplate/Controllers/EsController.cs @@ -1,20 +1,19 @@ -using global::OnlyOneAccessTemplate.Services; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Caching.Memory; +using OnlyOneAccessTemplate.Models; using OnlyOneAccessTemplate.Services; namespace OnlyOneAccessTemplate.Controllers { - [Route("es")] - public class EsController : HomeController + public class EsController : BaseController { public EsController( ISiteConfigurationService siteConfig, ILanguageService languageService, ISeoService seoService, IMemoryCache cache, - IConfiguration configuration) + IConfiguration configuration) : base(siteConfig, languageService, seoService, cache, configuration) { } @@ -23,21 +22,24 @@ namespace OnlyOneAccessTemplate.Controllers [Route("")] [Route("inicio")] - public new async Task Index() + public IActionResult Index() { - return await base.Index(); + var siteConfig = _siteConfig.GetConfiguration("es"); + ViewBag.PageTitle = siteConfig.SiteTitle; + ViewBag.PageDescription = siteConfig.SiteDescription; + + return View("~/Views/Home/Index.cshtml", siteConfig); } - [Route("acerca")] - public new async Task About() + [Route("convertir-pdf-a-word-en-linea")] + public IActionResult ConvertirPdfAWordEnLinea() { - return await base.About(); - } - - [Route("contacto")] - public new async Task Contact() - { - return await base.Contact(); + var siteConfig = _siteConfig.GetConfiguration("es"); + + SetPageSeo("Convertir PDF a Word en Línea Gratis", + "Convierte tus archivos PDF a Word rápidamente y gratis..."); + + return View("~/Views/Home/Index.cshtml", siteConfig); } } } diff --git a/OnlyOneAccessTemplate/Controllers/HomeController.cs b/OnlyOneAccessTemplate/Controllers/HomeController.cs index 364318e..c6415fa 100644 --- a/OnlyOneAccessTemplate/Controllers/HomeController.cs +++ b/OnlyOneAccessTemplate/Controllers/HomeController.cs @@ -1,12 +1,10 @@ -using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Caching.Memory; using OnlyOneAccessTemplate.Services; using OnlyOneAccessTemplate.Models; - namespace OnlyOneAccessTemplate.Controllers { - public class HomeController : BaseController { public HomeController( @@ -19,92 +17,18 @@ namespace OnlyOneAccessTemplate.Controllers { } - public async Task Index() + [Route("")] + [Route("pt")] + public IActionResult Index() { var language = GetCurrentLanguage(); - - // Get page content from database - var pageContent = await _siteConfig.GetPageContentAsync(language, "index"); - - // If no content exists, create default content - if (pageContent == null) - { - await CreateDefaultContentAsync(language); - pageContent = await _siteConfig.GetPageContentAsync(language, "index"); - } - - // Set current page for navigation + var siteConfig = _siteConfig.GetConfiguration(language); + + ViewBag.PageTitle = siteConfig.SiteTitle; + ViewBag.PageDescription = siteConfig.SiteDescription; ViewBag.CurrentPage = "home"; - return View(pageContent); - } - - public async Task About() - { - var language = GetCurrentLanguage(); - var pageContent = await _siteConfig.GetPageContentAsync(language, "about"); - - if (pageContent == null) - { - await CreateDefaultContentAsync(language); - pageContent = await _siteConfig.GetPageContentAsync(language, "about"); - } - - ViewBag.CurrentPage = "about"; - return View(pageContent); - } - - public async Task Contact() - { - var language = GetCurrentLanguage(); - var pageContent = await _siteConfig.GetPageContentAsync(language, "contact"); - - ViewBag.CurrentPage = "contact"; - return View(pageContent); - } - - [HttpGet("setup-data")] - public async Task SetupDefaultData() - { - try - { - var languages = new[] { "pt", "en", "es" }; - - foreach (var language in languages) - { - await CreateDefaultContentAsync(language); - } - - return Json(new { success = true, message = "Dados padrão criados com sucesso!" }); - } - catch (Exception ex) - { - return Json(new { success = false, message = ex.Message }); - } - } - - [HttpGet("privacy")] - public IActionResult Privacy() - { - ViewBag.CurrentPage = "privacy"; - SetPageSeo( - "Política de Privacidade", - "Nossa política de privacidade e proteção de dados", - "privacidade, proteção de dados, lgpd" - ); - return View(); - } - - [HttpGet("terms")] - public IActionResult Terms() - { - ViewBag.CurrentPage = "terms"; - SetPageSeo( - "Termos de Uso", - "Termos e condições de uso do nosso serviço", - "termos, condições, uso" - ); - return View(); + return View(siteConfig); } [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] @@ -113,438 +37,5 @@ namespace OnlyOneAccessTemplate.Controllers return View(); } - private async Task CreateDefaultContentAsync(string language) - { - try - { - // Check if configuration already exists - var configExists = await _siteConfig.ConfigurationExistsAsync(language); - if (configExists) return; - - // Create default configuration with sample content - var siteConfig = new SiteConfiguration - { - Language = language, - Seo = CreateDefaultSeoConfig(language), - Pages = CreateDefaultPages(language), - Conversion = CreateDefaultConversionConfig(language), - CreatedAt = DateTime.UtcNow, - UpdatedAt = DateTime.UtcNow - }; - - await _siteConfig.UpdateConfigurationAsync(siteConfig); - } - catch (Exception ex) - { - // Log error but don't break the page - Console.WriteLine($"Error creating default content: {ex.Message}"); - } - } - - private SeoConfig CreateDefaultSeoConfig(string language) - { - return language switch - { - "en" => new SeoConfig - { - SiteName = "OnlyOne Access Template", - DefaultTitle = "Transform Your Business - Conversion Template", - DefaultDescription = "Professional conversion solutions to boost your business results with proven methodology", - DefaultKeywords = "conversion, business, marketing, results, template", - TwitterCard = "summary_large_image", - GoogleTagManagerId = "GTM-XXXXXXX", - GoogleAnalyticsId = "GA-XXXXXXXX", - Author = "OnlyOne Access Team", - OgImage = "/img/og-image-en.jpg" - }, - "es" => new SeoConfig - { - SiteName = "OnlyOne Access Template", - DefaultTitle = "Transforma Tu Negocio - Template de Conversión", - DefaultDescription = "Soluciones profesionales de conversión para potenciar los resultados de tu negocio", - DefaultKeywords = "conversión, negocio, marketing, resultados, plantilla", - TwitterCard = "summary_large_image", - GoogleTagManagerId = "GTM-XXXXXXX", - GoogleAnalyticsId = "GA-XXXXXXXX", - Author = "Equipo OnlyOne Access", - OgImage = "/img/og-image-es.jpg" - }, - _ => new SeoConfig - { - SiteName = "OnlyOne Access Template", - DefaultTitle = "Transforme Seu Negócio - Template de Conversão", - DefaultDescription = "Soluções profissionais de conversão para alavancar os resultados do seu negócio", - DefaultKeywords = "conversão, negócio, marketing, resultados, template", - TwitterCard = "summary_large_image", - GoogleTagManagerId = "GTM-XXXXXXX", - GoogleAnalyticsId = "GA-XXXXXXXX", - Author = "Equipe OnlyOne Access", - OgImage = "/img/og-image-pt.jpg" - } - }; - } - - private Dictionary CreateDefaultPages(string language) - { - return language switch - { - "en" => new Dictionary - { - ["index"] = new PageContent - { - Title = "Transform Your Business Today", - Description = "Increase your sales by up to 300% with our proven methodology and expert support", - Keywords = "business transformation, conversion, sales growth, methodology", - H1 = "Transform Your Business Today", - MetaTitle = "Business Transformation | Conversion Solutions", - Blocks = CreateEnglishBlocks(), - IsActive = true, - PublishDate = DateTime.UtcNow - }, - ["about"] = new PageContent - { - Title = "About Us - Our Mission", - Description = "Learn about our mission to help businesses achieve better conversion rates", - H1 = "About Our Company", - MetaTitle = "About Us | Conversion Experts" - } - }, - "es" => new Dictionary - { - ["index"] = new PageContent - { - Title = "Transforma Tu Negocio Hoy", - Description = "Aumenta tus ventas hasta un 300% con nuestra metodología probada y soporte especializado", - Keywords = "transformación empresarial, conversión, crecimiento de ventas, metodología", - H1 = "Transforma Tu Negocio Hoy", - MetaTitle = "Transformación Empresarial | Soluciones de Conversión", - Blocks = CreateSpanishBlocks(), - IsActive = true, - PublishDate = DateTime.UtcNow - }, - ["about"] = new PageContent - { - Title = "Acerca de Nosotros - Nuestra Misión", - Description = "Conoce nuestra misión de ayudar a las empresas a lograr mejores tasas de conversión", - H1 = "Acerca de Nuestra Empresa", - MetaTitle = "Acerca de Nosotros | Expertos en Conversión" - } - }, - _ => new Dictionary - { - ["index"] = new PageContent - { - Title = "Transforme Seu Negócio Hoje", - Description = "Aumente suas vendas em até 300% com nossa metodologia comprovada e suporte especializado", - Keywords = "transformação empresarial, conversão, crescimento de vendas, metodologia", - H1 = "Transforme Seu Negócio Hoje", - MetaTitle = "Transformação Empresarial | Soluções de Conversão", - Blocks = CreatePortugueseBlocks(), - IsActive = true, - PublishDate = DateTime.UtcNow - }, - ["about"] = new PageContent - { - Title = "Sobre Nós - Nossa Missão", - Description = "Conheça nossa missão de ajudar empresas a alcançar melhores taxas de conversão", - H1 = "Sobre Nossa Empresa", - MetaTitle = "Sobre Nós | Especialistas em Conversão" - } - } - }; - } - - private List CreatePortugueseBlocks() - { - return new List - { - new ContentBlock - { - Type = "hero", - Title = "Transforme Seu Negócio com Nossa Solução Inovadora", - Content = "Aumente suas vendas em até 300% com nossa metodologia comprovada, suporte especializado e resultados garantidos em 30 dias.", - ImageUrl = "/img/hero-pt.jpg", - ButtonText = "Começar Transformação", - ButtonUrl = "#conversion-form", - Order = 1, - Properties = new Dictionary - { - { "badge", "🚀 Mais de 1000 empresas transformadas" }, - { "features", new[] { "✅ Metodologia comprovada", "✅ Suporte 24/7", "✅ Resultados em 30 dias" } }, - { "social_proof", "Mais de 500 avaliações 5 estrelas" } - } - }, - new ContentBlock - { - Type = "features", - Title = "Por Que Escolher Nossa Solução?", - Content = "Descubra os benefícios que já transformaram mais de 1000 empresas em todo o Brasil", - Order = 2, - Properties = new Dictionary - { - { "feature_list", new[] - { - new Dictionary - { - { "icon", "fas fa-rocket" }, - { "title", "Resultados Rápidos" }, - { "description", "Veja os primeiros resultados em até 30 dias com nossa metodologia testada" } - }, - new Dictionary - { - { "icon", "fas fa-shield-alt" }, - { "title", "Garantia Total" }, - { "description", "100% de garantia ou seu dinheiro de volta. Assumimos o risco por você" } - }, - new Dictionary - { - { "icon", "fas fa-users" }, - { "title", "Suporte Especializado" }, - { "description", "Equipe dedicada 24/7 para garantir seu sucesso em cada etapa" } - }, - new Dictionary - { - { "icon", "fas fa-chart-line" }, - { "title", "Crescimento Sustentável" }, - { "description", "Estratégias de longo prazo para crescimento consistente e duradouro" } - }, - new Dictionary - { - { "icon", "fas fa-cog" }, - { "title", "Automatização" }, - { "description", "Processos automatizados que trabalham para você 24 horas por dia" } - }, - new Dictionary - { - { "icon", "fas fa-trophy" }, - { "title", "Casos de Sucesso" }, - { "description", "Mais de 1000 empresas já alcançaram resultados extraordinários" } - } - } - } - } - }, - new ContentBlock - { - Type = "testimonials", - Title = "O Que Nossos Clientes Dizem", - Content = "Mais de 1000 empresas já transformaram seus resultados. Veja alguns depoimentos:", - Order = 3, - Properties = new Dictionary - { - { "testimonials", new[] - { - new Dictionary - { - { "quote", "Aumentamos nossas vendas em 250% em apenas 3 meses. A metodologia realmente funciona e o suporte é excepcional!" }, - { "name", "Maria Silva" }, - { "position", "CEO, TechStart Brasil" }, - { "avatar", "/img/testimonial-1.jpg" } - }, - new Dictionary - { - { "quote", "O ROI foi incrível. Recuperamos o investimento em 30 dias e continuamos crescendo desde então." }, - { "name", "João Santos" }, - { "position", "Diretor Comercial, InovaCorp" }, - { "avatar", "/img/testimonial-2.jpg" } - }, - new Dictionary - { - { "quote", "Finalmente encontramos uma solução que entrega o que promete. Resultados desde o primeiro mês!" }, - { "name", "Ana Costa" }, - { "position", "Fundadora, GrowBiz" }, - { "avatar", "/img/testimonial-3.jpg" } - } - } - } - } - }, - new ContentBlock - { - Type = "cta", - Title = "Pronto para Transformar Seu Negócio?", - Content = "Junte-se a mais de 1000 empresas que já alcançaram resultados extraordinários com nossa metodologia comprovada.", - ButtonText = "Começar Minha Transformação", - ButtonUrl = "#conversion-form", - Order = 4, - Properties = new Dictionary - { - { "urgency_badge", "⚡ Últimas vagas da semana" }, - { "guarantee", "🛡️ Garantia de 30 dias ou seu dinheiro de volta" }, - { "secondary_button_text", "Assistir Demonstração" }, - { "secondary_button_url", "#demo-video" } - } - } - }; - } - - private List CreateEnglishBlocks() - { - return new List - { - new ContentBlock - { - Type = "hero", - Title = "Transform Your Business with Our Innovative Solution", - Content = "Increase your sales by up to 300% with our proven methodology, expert support and guaranteed results in 30 days.", - ImageUrl = "/img/hero-en.jpg", - ButtonText = "Start Transformation", - ButtonUrl = "#conversion-form", - Order = 1, - Properties = new Dictionary - { - { "badge", "🚀 Over 1000 companies transformed" }, - { "features", new[] { "✅ Proven methodology", "✅ 24/7 Support", "✅ Results in 30 days" } }, - { "social_proof", "Over 500 five-star reviews" } - } - }, - new ContentBlock - { - Type = "features", - Title = "Why Choose Our Solution?", - Content = "Discover the benefits that have already transformed over 1000 companies worldwide", - Order = 2, - Properties = new Dictionary - { - { "feature_list", new[] - { - new Dictionary - { - { "icon", "fas fa-rocket" }, - { "title", "Fast Results" }, - { "description", "See first results within 30 days with our tested methodology" } - }, - new Dictionary - { - { "icon", "fas fa-shield-alt" }, - { "title", "Full Guarantee" }, - { "description", "100% guarantee or your money back. We take the risk for you" } - }, - new Dictionary - { - { "icon", "fas fa-users" }, - { "title", "Expert Support" }, - { "description", "24/7 dedicated team to ensure your success at every step" } - } - } - } - } - }, - new ContentBlock - { - Type = "testimonials", - Title = "What Our Clients Say", - Content = "Over 1000 companies have already transformed their results. See some testimonials:", - Order = 3, - Properties = new Dictionary - { - { "testimonials", new[] - { - new Dictionary - { - { "quote", "We increased our sales by 250% in just 3 months. The methodology really works!" }, - { "name", "Mary Johnson" }, - { "position", "CEO, TechStart USA" }, - { "avatar", "/img/testimonial-en-1.jpg" } - } - } - } - } - }, - new ContentBlock - { - Type = "cta", - Title = "Ready to Transform Your Business?", - Content = "Join over 1000 companies that have already achieved extraordinary results.", - ButtonText = "Start My Transformation", - ButtonUrl = "#conversion-form", - Order = 4 - } - }; - } - - private List CreateSpanishBlocks() - { - return new List - { - new ContentBlock - { - Type = "hero", - Title = "Transforma Tu Negocio con Nuestra Solución Innovadora", - Content = "Aumenta tus ventas hasta un 300% con nuestra metodología probada, soporte especializado y resultados garantizados en 30 días.", - ImageUrl = "/img/hero-es.jpg", - ButtonText = "Comenzar Transformación", - ButtonUrl = "#conversion-form", - Order = 1, - Properties = new Dictionary - { - { "badge", "🚀 Más de 1000 empresas transformadas" }, - { "features", new[] { "✅ Metodología probada", "✅ Soporte 24/7", "✅ Resultados en 30 días" } }, - { "social_proof", "Más de 500 reseñas de 5 estrellas" } - } - }, - new ContentBlock - { - Type = "features", - Title = "¿Por Qué Elegir Nuestra Solución?", - Content = "Descubre los beneficios que ya han transformado más de 1000 empresas en todo el mundo", - Order = 2 - }, - new ContentBlock - { - Type = "testimonials", - Title = "Lo Que Dicen Nuestros Clientes", - Content = "Más de 1000 empresas ya han transformado sus resultados. Ve algunos testimonios:", - Order = 3 - }, - new ContentBlock - { - Type = "cta", - Title = "¿Listo para Transformar Tu Negocio?", - Content = "Únete a más de 1000 empresas que ya han logrado resultados extraordinarios.", - ButtonText = "Comenzar Mi Transformación", - ButtonUrl = "#conversion-form", - Order = 4 - } - }; - } - - private ConversionConfig CreateDefaultConversionConfig(string language) - { - return new ConversionConfig - { - FormAction = "/conversion/submit", - ThankYouPage = language switch - { - "en" => "/en/thank-you", - "es" => "/es/gracias", - _ => "/obrigado" - }, - FormFields = language switch - { - "en" => new List - { - new() { Name = "name", Type = "text", Label = "Full Name", Placeholder = "Enter your full name", Required = true, Order = 1 }, - new() { Name = "email", Type = "email", Label = "Email Address", Placeholder = "Enter your email", Required = true, Order = 2 }, - new() { Name = "phone", Type = "tel", Label = "Phone Number", Placeholder = "Enter your phone number", Required = false, Order = 3 }, - new() { Name = "company", Type = "text", Label = "Company Name", Placeholder = "Enter your company name", Required = false, Order = 4 } - }, - "es" => new List - { - new() { Name = "name", Type = "text", Label = "Nombre Completo", Placeholder = "Ingresa tu nombre completo", Required = true, Order = 1 }, - new() { Name = "email", Type = "email", Label = "Correo Electrónico", Placeholder = "Ingresa tu correo", Required = true, Order = 2 }, - new() { Name = "phone", Type = "tel", Label = "Número de Teléfono", Placeholder = "Ingresa tu teléfono", Required = false, Order = 3 }, - new() { Name = "company", Type = "text", Label = "Nombre de la Empresa", Placeholder = "Ingresa el nombre de tu empresa", Required = false, Order = 4 } - }, - _ => new List - { - new() { Name = "name", Type = "text", Label = "Nome Completo", Placeholder = "Digite seu nome completo", Required = true, Order = 1 }, - new() { Name = "email", Type = "email", Label = "E-mail", Placeholder = "Digite seu e-mail", Required = true, Order = 2 }, - new() { Name = "phone", Type = "tel", Label = "Telefone", Placeholder = "Digite seu telefone", Required = false, Order = 3 }, - new() { Name = "company", Type = "text", Label = "Nome da Empresa", Placeholder = "Digite o nome da sua empresa", Required = false, Order = 4 } - } - } - }; - } } } \ No newline at end of file diff --git a/OnlyOneAccessTemplate/Controllers/TextConversionApiController.cs b/OnlyOneAccessTemplate/Controllers/TextConversionApiController.cs new file mode 100644 index 0000000..3cb63e2 --- /dev/null +++ b/OnlyOneAccessTemplate/Controllers/TextConversionApiController.cs @@ -0,0 +1,181 @@ +using Microsoft.AspNetCore.Mvc; +using System.Text.RegularExpressions; +using System.Text; +using Polly; +using Polly.Extensions.Http; + +namespace OnlyOneAccessTemplate.Controllers +{ + [ApiController] + [Route("api/text-conversion")] + public class TextConversionApiController : ControllerBase + { + private readonly ILogger _logger; + private readonly IAsyncPolicy _retryPolicy; + + public TextConversionApiController(ILogger logger) + { + _logger = logger; + + // Configurar política de retry com Polly + _retryPolicy = Policy + .Handle() + .WaitAndRetryAsync( + retryCount: 3, + sleepDurationProvider: retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)), + onRetry: (outcome, timespan, retryCount, context) => + { + _logger.LogWarning("Tentativa {RetryCount} de conversão de texto após {Delay}ms. Erro: {Error}", + retryCount, timespan.TotalMilliseconds, outcome.Message); + }) + .AsAsyncPolicy(); + } + + [HttpPost("sentence-case")] + public async Task ConvertToSentenceCase([FromBody] TextConversionRequest request) + { + try + { + if (string.IsNullOrEmpty(request.Text)) + { + return BadRequest(new { success = false, message = "Texto não pode estar vazio" }); + } + + // Aplicar política de retry + var result = await _retryPolicy.ExecuteAsync(async () => + { + return await Task.FromResult(ConvertToSentenceCase(request.Text)); + }); + + _logger.LogInformation("Conversão de texto realizada com sucesso para {Length} caracteres", request.Text.Length); + + return Ok(new TextConversionResponse + { + Success = true, + OriginalText = request.Text, + ConvertedText = result, + ProcessedAt = DateTime.UtcNow + }); + } + catch (Exception ex) + { + _logger.LogError(ex, "Erro ao converter texto para sentence case"); + return StatusCode(500, new { success = false, message = "Erro interno do servidor" }); + } + } + + [HttpPost("upper-case")] + public async Task ConvertToUpperCase([FromBody] TextConversionRequest request) + { + try + { + if (string.IsNullOrEmpty(request.Text)) + { + return BadRequest(new { success = false, message = "Texto não pode estar vazio" }); + } + + var result = await _retryPolicy.ExecuteAsync(async () => + { + return await Task.FromResult(request.Text.ToUpper()); + }); + + return Ok(new TextConversionResponse + { + Success = true, + OriginalText = request.Text, + ConvertedText = result, + ProcessedAt = DateTime.UtcNow + }); + } + catch (Exception ex) + { + _logger.LogError(ex, "Erro ao converter texto para maiúsculas"); + return StatusCode(500, new { success = false, message = "Erro interno do servidor" }); + } + } + + [HttpPost("lower-case")] + public async Task ConvertToLowerCase([FromBody] TextConversionRequest request) + { + try + { + if (string.IsNullOrEmpty(request.Text)) + { + return BadRequest(new { success = false, message = "Texto não pode estar vazio" }); + } + + var result = await _retryPolicy.ExecuteAsync(async () => + { + return await Task.FromResult(request.Text.ToLower()); + }); + + return Ok(new TextConversionResponse + { + Success = true, + OriginalText = request.Text, + ConvertedText = result, + ProcessedAt = DateTime.UtcNow + }); + } + catch (Exception ex) + { + _logger.LogError(ex, "Erro ao converter texto para minúsculas"); + return StatusCode(500, new { success = false, message = "Erro interno do servidor" }); + } + } + + [HttpGet("health")] + public IActionResult HealthCheck() + { + return Ok(new { status = "healthy", timestamp = DateTime.UtcNow }); + } + + private string ConvertToSentenceCase(string text) + { + if (string.IsNullOrEmpty(text)) + return text; + + // Converte todo o texto para minúsculas primeiro + string lowerText = text.ToLower(); + + // StringBuilder para construir o resultado + StringBuilder result = new StringBuilder(lowerText); + + // Capitaliza a primeira letra do texto se for uma letra + if (result.Length > 0 && char.IsLetter(result[0])) + { + result[0] = char.ToUpper(result[0]); + } + + // Regex para encontrar início de frases/parágrafos + // Procura por pontos, exclamações, interrogações ou quebras de linha + // seguidos por espaços e uma letra + string pattern = @"([.!?\n]\s*)([a-z])"; + + string resultText = result.ToString(); + resultText = Regex.Replace(resultText, pattern, match => + { + return match.Groups[1].Value + char.ToUpper(match.Groups[2].Value[0]); + }); + + return resultText; + } + } + + public class TextConversionRequest + { + public string Text { get; set; } = string.Empty; + public string? Language { get; set; } + public Dictionary? Options { get; set; } + } + + public class TextConversionResponse + { + public bool Success { get; set; } + public string OriginalText { get; set; } = string.Empty; + public string ConvertedText { get; set; } = string.Empty; + public DateTime ProcessedAt { get; set; } + public string? ErrorMessage { get; set; } + public Dictionary? Metadata { get; set; } + } +} \ No newline at end of file diff --git a/OnlyOneAccessTemplate/Models/SiteConfiguration.cs b/OnlyOneAccessTemplate/Models/SiteConfiguration.cs index 65a9be0..0815a8a 100644 --- a/OnlyOneAccessTemplate/Models/SiteConfiguration.cs +++ b/OnlyOneAccessTemplate/Models/SiteConfiguration.cs @@ -11,12 +11,41 @@ namespace OnlyOneAccessTemplate.Models public string Id { get; set; } = string.Empty; [Required] - public string Language { get; set; } = "pt"; - - public SeoConfig Seo { get; set; } = new(); - public Dictionary Pages { get; set; } = new(); - public ConversionConfig Conversion { get; set; } = new(); + public string Language { get; set; } = "pt"; // "pt", "en", "es" + + public string Currency { get; set; } = "BRL"; // "BRL", "USD", "EUR" + public string CountryCode { get; set; } = "BR"; // "BR", "US", "ES" + + // SEO Configuration + public string SiteTitle { get; set; } = string.Empty; + public string SiteDescription { get; set; } = string.Empty; + public string SiteKeywords { get; set; } = string.Empty; + public string OgImage { get; set; } = string.Empty; + + // AdSense Configuration + public string AdSenseClientId { get; set; } = string.Empty; + public string AdSenseSlotId { get; set; } = string.Empty; + + // Home Page Content + public HomePageContent HomePage { get; set; } = new(); + public DateTime CreatedAt { get; set; } = DateTime.UtcNow; public DateTime UpdatedAt { get; set; } = DateTime.UtcNow; } + + public class HomePageContent + { + public string MainTitle { get; set; } = string.Empty; + public string MainDescription { get; set; } = string.Empty; + public string CallToAction { get; set; } = string.Empty; + public List FeaturedConverters { get; set; } = new(); + } + + public class ConverterCard + { + public string Title { get; set; } = string.Empty; + public string Description { get; set; } = string.Empty; + public string Icon { get; set; } = string.Empty; + public string Url { get; set; } = string.Empty; + } } diff --git a/OnlyOneAccessTemplate/OnlyOneAccessTemplate.csproj b/OnlyOneAccessTemplate/OnlyOneAccessTemplate.csproj index 71e9333..199a6a1 100644 --- a/OnlyOneAccessTemplate/OnlyOneAccessTemplate.csproj +++ b/OnlyOneAccessTemplate/OnlyOneAccessTemplate.csproj @@ -17,6 +17,8 @@ + + diff --git a/OnlyOneAccessTemplate/Program.cs b/OnlyOneAccessTemplate/Program.cs index bd271c7..cdf7a04 100644 --- a/OnlyOneAccessTemplate/Program.cs +++ b/OnlyOneAccessTemplate/Program.cs @@ -35,14 +35,14 @@ builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); -// Converter Services - Registrar todos os conversores disponveis +// Converter Services - Registrar todos os conversores dispon�veis builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); -builder.Services.AddScoped(); -// Adicione aqui novos conversores conforme necessrio: -// builder.Services.AddScoped(); +// Substituindo UpperLowerConversorService por API com Polly +builder.Services.AddHttpClient(); +builder.Services.AddScoped(); // HttpClient for external calls builder.Services.AddHttpClient(); @@ -96,22 +96,15 @@ app.MapControllerRoute( pattern: "converter/config/{converterType}", defaults: new { controller = "Converter", action = "GetConverterConfig" }); +// Rotas específicas por idioma app.MapControllerRoute( - name: "default-pt", + name: "multilingual", + pattern: "{language:regex(en|es)}/{controller=Home}/{action=Index}/{id?}"); + +app.MapControllerRoute( + name: "default", pattern: "{controller=Home}/{action=Index}/{id?}"); -app.MapControllerRoute( - name: "pt-explicit", - pattern: "pt/{controller=Home}/{action=Index}/{id?}"); - -app.MapControllerRoute( - name: "english", - pattern: "en/{controller=En}/{action=Index}/{id?}"); - -app.MapControllerRoute( - name: "spanish", - pattern: "es/{controller=Es}/{action=Index}/{id?}"); - // SEO Routes app.MapGet("/sitemap.xml", async (ISiteConfigurationService siteConfig) => { @@ -137,21 +130,21 @@ if (app.Environment.IsDevelopment()) try { - // Verificar se j existem configuraes + // Verificar se j� existem configura��es var languages = new[] { "pt", "en", "es" }; foreach (var lang in languages) { var configExists = await siteConfigService.ConfigurationExistsAsync(lang); if (!configExists) { - logger.LogInformation("Criando configurao padro para idioma: {Language}", lang); - _ = await siteConfigService.GetConfigurationAsync(lang); // Isso criar a configurao padro + logger.LogInformation("Criando configura��o padr�o para idioma: {Language}", lang); + _ = await siteConfigService.GetConfigurationAsync(lang); // Isso criar� a configura��o padr�o } } } catch (Exception ex) { - logger.LogError(ex, "Erro ao inicializar dados padro"); + logger.LogError(ex, "Erro ao inicializar dados padr�o"); } } diff --git a/OnlyOneAccessTemplate/Services/ConversionService.cs b/OnlyOneAccessTemplate/Services/ConversionService.cs index 95f91b0..beb9343 100644 --- a/OnlyOneAccessTemplate/Services/ConversionService.cs +++ b/OnlyOneAccessTemplate/Services/ConversionService.cs @@ -65,16 +65,16 @@ namespace OnlyOneAccessTemplate.Services }); // Enviar para webhook se configurado - if (!string.IsNullOrEmpty(configuration.Conversion.WebhookUrl)) - { - _ = Task.Run(() => SendWebhookAsync(configuration.Conversion.WebhookUrl, conversionRecord)); - } + //if (!string.IsNullOrEmpty(configuration..Conversion.WebhookUrl)) + //{ + // _ = Task.Run(() => SendWebhookAsync(configuration.Conversion.WebhookUrl, conversionRecord)); + //} - // Disparar pixel de conversão se configurado - if (!string.IsNullOrEmpty(configuration.Conversion.ConversionPixel)) - { - _ = Task.Run(() => FireConversionPixelAsync(configuration.Conversion.ConversionPixel, conversionRecord)); - } + //// Disparar pixel de conversão se configurado + //if (!string.IsNullOrEmpty(configuration.Conversion.ConversionPixel)) + //{ + // _ = Task.Run(() => FireConversionPixelAsync(configuration.Conversion.ConversionPixel, conversionRecord)); + //} _logger.LogInformation("Conversão processada com sucesso: {ConversionId}", conversionRecord.Id); return true; diff --git a/OnlyOneAccessTemplate/Services/Interfaces.cs b/OnlyOneAccessTemplate/Services/Interfaces.cs index 29e138d..287ef9f 100644 --- a/OnlyOneAccessTemplate/Services/Interfaces.cs +++ b/OnlyOneAccessTemplate/Services/Interfaces.cs @@ -6,7 +6,6 @@ namespace OnlyOneAccessTemplate.Services { Task GetConfigurationAsync(string language); SiteConfiguration GetConfiguration(string language); // Versão síncrona para o BaseController - Task GetPageContentAsync(string language, string pageName); Task GenerateSitemapAsync(); Task GenerateRobotsAsync(); Task UpdateConfigurationAsync(SiteConfiguration config); diff --git a/OnlyOneAccessTemplate/Services/SeoService.cs b/OnlyOneAccessTemplate/Services/SeoService.cs index 8d473bb..14c2187 100644 --- a/OnlyOneAccessTemplate/Services/SeoService.cs +++ b/OnlyOneAccessTemplate/Services/SeoService.cs @@ -42,19 +42,19 @@ namespace OnlyOneAccessTemplate.Services try { var configuration = await _siteConfig.GetConfigurationAsync(language); - content = content ?? await _siteConfig.GetPageContentAsync(language, pageName); + //content = content ?? await _siteConfig.ge.GetPageContentAsync(language, pageName); var baseUrl = _configuration.GetValue("SEO:DefaultDomain") ?? "https://localhost"; var canonicalUrl = BuildCanonicalUrl(baseUrl, language, pageName); var hreflangUrls = _languageService.GetHreflangUrls(canonicalUrl, language); var metadata = new SeoMetadata( - Title: content?.MetaTitle ?? content?.Title ?? configuration.Seo.DefaultTitle, - Description: content?.Description ?? configuration.Seo.DefaultDescription, - Keywords: content?.Keywords ?? configuration.Seo.DefaultKeywords, - OgTitle: content?.Title ?? configuration.Seo.DefaultTitle, - OgDescription: content?.Description ?? configuration.Seo.DefaultDescription, - OgImage: content?.OgImage ?? configuration.Seo.OgImage, + Title: content?.MetaTitle ?? content?.Title ?? configuration.SiteTitle, + Description: content?.Description ?? configuration.SiteDescription, + Keywords: content?.Keywords ?? configuration.SiteKeywords, + OgTitle: content?.Title ?? configuration.SiteTitle, + OgDescription: content?.Description ?? configuration.SiteDescription, + OgImage: content?.OgImage ?? configuration.OgImage, CanonicalUrl: canonicalUrl, HreflangUrls: hreflangUrls, StructuredData: await GenerateStructuredDataAsync(language, "WebPage", new { content, configuration }) @@ -138,14 +138,14 @@ namespace OnlyOneAccessTemplate.Services { Context = "https://schema.org", Type = "WebPage", - Name = config.Seo.SiteName, - Description = config.Seo.DefaultDescription, + Name = config.SiteTitle, + Description = config.SiteDescription, Url = baseUrl, InLanguage = language, IsPartOf = new { Type = "WebSite", - Name = config.Seo.SiteName, + Name = config.SiteTitle, Url = baseUrl }, DatePublished = DateTime.UtcNow.ToString("yyyy-MM-dd"), @@ -239,7 +239,7 @@ namespace OnlyOneAccessTemplate.Services { Context = "https://schema.org", Type = "Organization", - Name = config.Seo.SiteName, + Name = config.SiteTitle, Url = baseUrl, Logo = new { @@ -312,7 +312,7 @@ namespace OnlyOneAccessTemplate.Services Brand = new { Type = "Brand", - Name = config.Seo.SiteName + Name = config.SiteTitle }, Offers = new { diff --git a/OnlyOneAccessTemplate/Services/SiteConfigurationService .cs b/OnlyOneAccessTemplate/Services/SiteConfigurationService .cs index ac86ad6..4fafed7 100644 --- a/OnlyOneAccessTemplate/Services/SiteConfigurationService .cs +++ b/OnlyOneAccessTemplate/Services/SiteConfigurationService .cs @@ -85,17 +85,6 @@ namespace OnlyOneAccessTemplate.Services } } - public async Task GetPageContentAsync(string language, string pageName) - { - var configuration = await GetConfigurationAsync(language); - - if (configuration.Pages.TryGetValue(pageName.ToLowerInvariant(), out var pageContent)) - { - return pageContent; - } - - return null; - } public async Task GenerateSitemapAsync() { @@ -215,15 +204,7 @@ namespace OnlyOneAccessTemplate.Services private async Task CreateDefaultConfigurationAsync(string language) { - var defaultConfig = new SiteConfiguration - { - Language = language, - Seo = CreateDefaultSeoConfig(language), - Pages = CreateDefaultPages(language), - Conversion = CreateDefaultConversionConfig(language), - CreatedAt = DateTime.UtcNow, - UpdatedAt = DateTime.UtcNow - }; + var defaultConfig = CreateDefaultConfiguration(language); try { @@ -240,17 +221,54 @@ namespace OnlyOneAccessTemplate.Services private SiteConfiguration CreateDefaultConfiguration(string language) { - var defaultConfig = new SiteConfiguration + return language switch { - Language = language, - Seo = CreateDefaultSeoConfig(language), - Pages = CreateDefaultPages(language), - Conversion = CreateDefaultConversionConfig(language), - CreatedAt = DateTime.UtcNow, - UpdatedAt = DateTime.UtcNow + "en" => new SiteConfiguration + { + Language = "en", + Currency = "USD", + CountryCode = "US", + SiteTitle = "Convert-it Online - Free Converters", + SiteDescription = "Convert your files online for free. PDF to Word, images, text and much more.", + SiteKeywords = "convert, converter, pdf, word, text, online, free", + HomePage = new HomePageContent + { + MainTitle = "Free Online Converters", + MainDescription = "Convert your files quickly and securely", + CallToAction = "Choose your converter" + } + }, + "es" => new SiteConfiguration + { + Language = "es", + Currency = "EUR", + CountryCode = "ES", + SiteTitle = "Convert-it Online - Convertidores Gratuitos", + SiteDescription = "Convierte tus archivos en línea gratis. PDF a Word, imágenes, texto y mucho más.", + SiteKeywords = "convertir, convertidor, pdf, word, texto, en línea, gratis", + HomePage = new HomePageContent + { + MainTitle = "Convertidores en Línea Gratuitos", + MainDescription = "Convierte tus archivos rápidamente y con seguridad", + CallToAction = "Elige tu convertidor" + } + }, + _ => new SiteConfiguration + { + Language = "pt", + Currency = "BRL", + CountryCode = "BR", + SiteTitle = "Convert-it Online - Conversores Gratuitos", + SiteDescription = "Converta seus arquivos online gratuitamente. PDF para Word, imagens, textos e muito mais.", + SiteKeywords = "converter, conversor, pdf, word, texto, online, grátis", + HomePage = new HomePageContent + { + MainTitle = "Conversores Online Gratuitos", + MainDescription = "Converta seus arquivos rapidamente e com segurança", + CallToAction = "Escolha seu conversor" + } + } }; - - return defaultConfig; } public async Task ConfigurationExistsAsync(string language) @@ -288,140 +306,6 @@ namespace OnlyOneAccessTemplate.Services } } - private SeoConfig CreateDefaultSeoConfig(string language) - { - var defaultSiteName = _config.GetValue("SEO:DefaultSiteName") ?? "Site de Conversão"; - - return language switch - { - "en" => new SeoConfig - { - SiteName = defaultSiteName, - DefaultTitle = "Conversion Site - Transform Your Business", - DefaultDescription = "Professional conversion solutions to boost your business results", - DefaultKeywords = "conversion, business, marketing, results", - TwitterCard = "summary_large_image", - GoogleTagManagerId = _config.GetValue("SEO:GoogleTagManagerId") ?? "", - GoogleAnalyticsId = _config.GetValue("SEO:GoogleAnalyticsId") ?? "", - Author = "Conversion Site Team" - }, - "es" => new SeoConfig - { - SiteName = defaultSiteName, - DefaultTitle = "Sitio de Conversión - Transforma Tu Negocio", - DefaultDescription = "Soluciones profesionales de conversión para potenciar los resultados de tu negocio", - DefaultKeywords = "conversión, negocio, marketing, resultados", - TwitterCard = "summary_large_image", - GoogleTagManagerId = _config.GetValue("SEO:GoogleTagManagerId") ?? "", - GoogleAnalyticsId = _config.GetValue("SEO:GoogleAnalyticsId") ?? "", - Author = "Equipo Sitio de Conversión" - }, - _ => new SeoConfig - { - SiteName = defaultSiteName, - DefaultTitle = "Site de Conversão - Transforme Seu Negócio", - DefaultDescription = "Soluções profissionais de conversão para alavancar os resultados do seu negócio", - DefaultKeywords = "conversão, negócio, marketing, resultados", - TwitterCard = "summary_large_image", - GoogleTagManagerId = _config.GetValue("SEO:GoogleTagManagerId") ?? "", - GoogleAnalyticsId = _config.GetValue("SEO:GoogleAnalyticsId") ?? "", - Author = "Equipe Site de Conversão" - } - }; - } - - private Dictionary CreateDefaultPages(string language) - { - return language switch - { - "en" => new Dictionary - { - ["index"] = new PageContent - { - Title = "Home - Transform Your Business", - Description = "Professional conversion solutions to boost your business results", - H1 = "Transform Your Business Today", - MetaTitle = "Business Transformation | Conversion Solutions" - }, - ["about"] = new PageContent - { - Title = "About Us - Our Mission", - Description = "Learn about our mission to help businesses achieve better conversion rates", - H1 = "About Our Company", - MetaTitle = "About Us | Conversion Experts" - } - }, - "es" => new Dictionary - { - ["index"] = new PageContent - { - Title = "Inicio - Transforma Tu Negocio", - Description = "Soluciones profesionales de conversión para potenciar los resultados de tu negocio", - H1 = "Transforma Tu Negocio Hoy", - MetaTitle = "Transformación Empresarial | Soluciones de Conversión" - }, - ["about"] = new PageContent - { - Title = "Acerca de Nosotros - Nuestra Misión", - Description = "Conoce nuestra misión de ayudar a las empresas a lograr mejores tasas de conversión", - H1 = "Acerca de Nuestra Empresa", - MetaTitle = "Acerca de Nosotros | Expertos en Conversión" - } - }, - _ => new Dictionary - { - ["index"] = new PageContent - { - Title = "Início - Transforme Seu Negócio", - Description = "Soluções profissionais de conversão para alavancar os resultados do seu negócio", - H1 = "Transforme Seu Negócio Hoje", - MetaTitle = "Transformação Empresarial | Soluções de Conversão" - }, - ["about"] = new PageContent - { - Title = "Sobre Nós - Nossa Missão", - Description = "Conheça nossa missão de ajudar empresas a alcançar melhores taxas de conversão", - H1 = "Sobre Nossa Empresa", - MetaTitle = "Sobre Nós | Especialistas em Conversão" - } - } - }; - } - - private ConversionConfig CreateDefaultConversionConfig(string language) - { - return new ConversionConfig - { - FormAction = "/conversion/submit", - ThankYouPage = language switch - { - "en" => "/thank-you", - "es" => "/gracias", - _ => "/obrigado" - }, - FormFields = language switch - { - "en" => new List - { - new() { Name = "name", Type = "text", Label = "Full Name", Placeholder = "Enter your full name", Required = true, Order = 1 }, - new() { Name = "email", Type = "email", Label = "Email", Placeholder = "Enter your email", Required = true, Order = 2 }, - new() { Name = "phone", Type = "tel", Label = "Phone", Placeholder = "Enter your phone number", Required = false, Order = 3 } - }, - "es" => new List - { - new() { Name = "name", Type = "text", Label = "Nombre Completo", Placeholder = "Ingresa tu nombre completo", Required = true, Order = 1 }, - new() { Name = "email", Type = "email", Label = "Correo Electrónico", Placeholder = "Ingresa tu correo", Required = true, Order = 2 }, - new() { Name = "phone", Type = "tel", Label = "Teléfono", Placeholder = "Ingresa tu número de teléfono", Required = false, Order = 3 } - }, - _ => new List - { - new() { Name = "name", Type = "text", Label = "Nome Completo", Placeholder = "Digite seu nome completo", Required = true, Order = 1 }, - new() { Name = "email", Type = "email", Label = "E-mail", Placeholder = "Digite seu e-mail", Required = true, Order = 2 }, - new() { Name = "phone", Type = "tel", Label = "Telefone", Placeholder = "Digite seu telefone", Required = false, Order = 3 } - } - } - }; - } private string BuildUrl(string baseUrl, string language, string page) { diff --git a/OnlyOneAccessTemplate/Services/TextConversionApiService.cs b/OnlyOneAccessTemplate/Services/TextConversionApiService.cs new file mode 100644 index 0000000..c2d7e9e --- /dev/null +++ b/OnlyOneAccessTemplate/Services/TextConversionApiService.cs @@ -0,0 +1,207 @@ +using OnlyOneAccessTemplate.Models; +using OnlyOneAccessTemplate.Services.OnlyOneAccessTemplate.Services; +using Polly; +using Polly.Extensions.Http; +using System.Text; +using System.Text.Json; + +namespace OnlyOneAccessTemplate.Services +{ + public interface ITextConversionApiService + { + Task ConvertToSentenceCaseAsync(string text); + Task ConvertToUpperCaseAsync(string text); + Task ConvertToLowerCaseAsync(string text); + Task IsHealthyAsync(); + } + + public class TextConversionApiService : ITextConversionApiService + { + private readonly HttpClient _httpClient; + private readonly ILogger _logger; + private readonly IAsyncPolicy _retryPolicy; + private readonly string _baseUrl; + + public TextConversionApiService( + HttpClient httpClient, + ILogger logger, + IConfiguration configuration) + { + _httpClient = httpClient; + _logger = logger; + _baseUrl = configuration.GetValue("TextConversionApi:BaseUrl") ?? "https://localhost:7071"; + + // Configurar política de retry com Polly para HttpClient + _retryPolicy = HttpPolicyExtensions + .HandleTransientHttpError() + .OrResult(msg => !msg.IsSuccessStatusCode) + .WaitAndRetryAsync( + retryCount: 3, + sleepDurationProvider: retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)), + onRetry: (outcome, timespan, retryCount, context) => + { + _logger.LogWarning("Tentativa {RetryCount} de chamada à API após {Delay}ms. Status: {StatusCode}", + retryCount, timespan.TotalMilliseconds, outcome.Result?.StatusCode); + }); + + // Configurar timeout + _httpClient.Timeout = TimeSpan.FromSeconds(30); + } + + public async Task ConvertToSentenceCaseAsync(string text) + { + try + { + var request = new { Text = text }; + var response = await CallApiAsync("/api/text-conversion/sentence-case", request); + return response.ConvertedText; + } + catch (Exception ex) + { + _logger.LogError(ex, "Erro ao converter texto para sentence case via API"); + throw; + } + } + + public async Task ConvertToUpperCaseAsync(string text) + { + try + { + var request = new { Text = text }; + var response = await CallApiAsync("/api/text-conversion/upper-case", request); + return response.ConvertedText; + } + catch (Exception ex) + { + _logger.LogError(ex, "Erro ao converter texto para maiúsculas via API"); + throw; + } + } + + public async Task ConvertToLowerCaseAsync(string text) + { + try + { + var request = new { Text = text }; + var response = await CallApiAsync("/api/text-conversion/lower-case", request); + return response.ConvertedText; + } + catch (Exception ex) + { + _logger.LogError(ex, "Erro ao converter texto para minúsculas via API"); + throw; + } + } + + public async Task IsHealthyAsync() + { + try + { + var response = await _retryPolicy.ExecuteAsync(async () => + { + return await _httpClient.GetAsync($"{_baseUrl}/api/text-conversion/health"); + }); + + return response.IsSuccessStatusCode; + } + catch (Exception ex) + { + _logger.LogError(ex, "Erro ao verificar saúde da API de conversão de texto"); + return false; + } + } + + private async Task CallApiAsync(string endpoint, object request) + { + var json = JsonSerializer.Serialize(request); + var content = new StringContent(json, Encoding.UTF8, "application/json"); + + var response = await _retryPolicy.ExecuteAsync(async () => + { + return await _httpClient.PostAsync($"{_baseUrl}{endpoint}", content); + }); + + if (!response.IsSuccessStatusCode) + { + var errorContent = await response.Content.ReadAsStringAsync(); + throw new HttpRequestException($"API call failed with status {response.StatusCode}: {errorContent}"); + } + + var responseContent = await response.Content.ReadAsStringAsync(); + var result = JsonSerializer.Deserialize(responseContent, new JsonSerializerOptions + { + PropertyNameCaseInsensitive = true + }); + + if (result == null || !result.Success) + { + throw new InvalidOperationException($"API returned unsuccessful response: {result?.ErrorMessage}"); + } + + return result; + } + } + + public class TextConversionApiResponse + { + public bool Success { get; set; } + public string OriginalText { get; set; } = string.Empty; + public string ConvertedText { get; set; } = string.Empty; + public DateTime ProcessedAt { get; set; } + public string? ErrorMessage { get; set; } + public Dictionary? Metadata { get; set; } + } + + // Novo serviço que substitui o UpperLowerConversorService usando a API + public class ApiBasedSentenceConverterService : BaseConverterService + { + private readonly ITextConversionApiService _apiService; + + public override string ConverterType => "text-case-sentence"; + public override string ConverterName => "Maiúsculas para minúsculas"; + + public ApiBasedSentenceConverterService( + ILogger logger, + IConfiguration configuration, + ITextConversionApiService apiService) + : base(logger, configuration) + { + _apiService = apiService; + } + + public override async Task ConvertAsync(ConversionRequest request) + { + try + { + var resultado = await _apiService.ConvertToSentenceCaseAsync(request.TextInput); + return new ConversionResult(true, OutputText: resultado); + } + catch (Exception ex) + { + _logger.LogError(ex, "Erro na conversão via API"); + return new ConversionResult(false, ErrorMessage: "Erro ao processar via API"); + } + } + + public override ConverterConfiguration GetConfiguration(string language) + { + var texts = GetLocalizedTexts(language); + + texts["ConverterTitle"] = language switch + { + "en" => "Sentence Case Convert", + "es" => "Convertir a Mayúscula Inicial", + _ => "Converter para primeira maiúscula" + }; + + return new ConverterConfiguration + { + ConverterType = "text", + OutputType = "text", + HasAdvancedOptions = false, + AllowShare = true, + LocalizedTexts = texts + }; + } + } +} \ No newline at end of file diff --git a/OnlyOneAccessTemplate/Services/UpperLowerConversorService.cs b/OnlyOneAccessTemplate/Services/UpperLowerConversorService.cs deleted file mode 100644 index 4648769..0000000 --- a/OnlyOneAccessTemplate/Services/UpperLowerConversorService.cs +++ /dev/null @@ -1,85 +0,0 @@ -using OnlyOneAccessTemplate.Services.OnlyOneAccessTemplate.Services; -using System.Text.RegularExpressions; -using System.Text; - -namespace OnlyOneAccessTemplate.Services -{ - public class UpperLowerConversorService : BaseConverterService - { - public override string ConverterType => "text-case-sentence"; - - public override string ConverterName => "Maiúsculas para minúsculas"; - - public UpperLowerConversorService(ILogger logger, IConfiguration configuration) - : base(logger, configuration) { } - - public override async Task ConvertAsync(ConversionRequest request) - { - try - { - // Sua lógica de conversão aqui - var resultado = ConvertToSentenceCase(request.TextInput); - - return new ConversionResult(true, OutputText: resultado); - } - catch (Exception ex) - { - _logger.LogError(ex, "Erro na conversão"); - return new ConversionResult(false, ErrorMessage: "Erro ao processar"); - } - } - - private string ConvertToSentenceCase(string text) - { - if (string.IsNullOrEmpty(text)) - return text; - - // Converte todo o texto para minúsculas primeiro - string lowerText = text.ToLower(); - - // StringBuilder para construir o resultado - StringBuilder result = new StringBuilder(lowerText); - - // Capitaliza a primeira letra do texto se for uma letra - if (result.Length > 0 && char.IsLetter(result[0])) - { - result[0] = char.ToUpper(result[0]); - } - - // Regex para encontrar início de frases/parágrafos - // Procura por pontos, exclamações, interrogações ou quebras de linha - // seguidos por espaços e uma letra - string pattern = @"([.!?\n]\s*)([a-z])"; - - string resultText = result.ToString(); - resultText = Regex.Replace(resultText, pattern, match => - { - return match.Groups[1].Value + char.ToUpper(match.Groups[2].Value[0]); - }); - - return resultText; - } - - public override ConverterConfiguration GetConfiguration(string language) - { - var texts = GetLocalizedTexts(language); - - // Personalize os textos para seu conversor - texts["ConverterTitle"] = language switch - { - "en" => "Sentence Case Convert", - "es" => "TÍTULO DE TU CONVERTIDOR", - _ => "Converter para primeira maiúscula" - }; - - return new ConverterConfiguration - { - ConverterType = "text", // ou "file" ou "url" - OutputType = "text", // ou "download" ou "preview" - HasAdvancedOptions = false, - AllowShare = true, - LocalizedTexts = texts - }; - } - } -} diff --git a/OnlyOneAccessTemplate/Views/Home/Index.cshtml b/OnlyOneAccessTemplate/Views/Home/Index.cshtml index acb0bb4..868c7e0 100644 --- a/OnlyOneAccessTemplate/Views/Home/Index.cshtml +++ b/OnlyOneAccessTemplate/Views/Home/Index.cshtml @@ -1,6 +1,6 @@ -@model OnlyOneAccessTemplate.Models.PageContent +@model OnlyOneAccessTemplate.Models.SiteConfiguration @{ - ViewData["Title"] = ViewBag.Title ?? "Conversor Online"; + ViewData["Title"] = ViewBag.Title ?? Model.SiteTitle; Layout = "~/Views/Shared/_Layout.cshtml"; } @@ -69,10 +69,10 @@
-

+

@(ViewBag.ConverterTitle ?? "CONVERSOR ONLINE")

-

+

@(ViewBag.ConverterDescription ?? "Converta seus arquivos de forma rápida e segura")

@@ -97,23 +97,23 @@
- 1 -
@(ViewBag.Step1Title ?? "Upload")
- @(ViewBag.Step1Description ?? "Selecione seu arquivo") + 1 +
@(ViewBag.Step1Title ?? "Digite")
+ @(ViewBag.Step1Description ?? "Digite seu texto")
- 2 -
@(ViewBag.Step2Title ?? "Processar")
- @(ViewBag.Step2Description ?? "Aguarde o processamento") + 2 +
@(ViewBag.Step2Title ?? "Converter")
+ @(ViewBag.Step2Description ?? "Clique para converter")
- 3 -
@(ViewBag.Step3Title ?? "Download")
- @(ViewBag.Step3Description ?? "Baixe o resultado") + 3 +
@(ViewBag.Step3Title ?? "Copiar")
+ @(ViewBag.Step3Description ?? "Copie o resultado")
@@ -128,13 +128,13 @@
- @(ViewBag.SecurityText ?? "Seus dados estão seguros") + @(ViewBag.SecurityText ?? "Seus dados estão seguros")
- - @(ViewBag.FileInfoText ?? "Processamento rápido e seguro") + + @(ViewBag.FileInfoText ?? "Processamento rápido e seguro")
@@ -175,8 +175,8 @@
-

@(ViewBag.BenefitsTitle ?? "Por Que Usar Nossa Ferramenta?")

-

@(ViewBag.BenefitsSubtitle ?? "Descubra os benefícios de nossa solução")

+

@(ViewBag.BenefitsTitle ?? "Por Que Usar Nossa Ferramenta?")

+

@(ViewBag.BenefitsSubtitle ?? "Descubra os benefícios de nossa solução")

@@ -185,36 +185,36 @@
- +
-
@(ViewBag.Feature1Title ?? "Rápido e Fácil")
+
@(ViewBag.Feature1Title ?? "Rápido e Fácil")
@(ViewBag.Feature1Description ?? "Conversão instantânea")
- +
-
@(ViewBag.Feature2Title ?? "Seguro")
+
@(ViewBag.Feature2Title ?? "Seguro")
@(ViewBag.Feature2Description ?? "Dados protegidos")
- +
-
@(ViewBag.Feature3Title ?? "Confiável")
+
@(ViewBag.Feature3Title ?? "Confiável")
@(ViewBag.Feature3Description ?? "Resultados precisos")
- +
-
Rápido
+
Rápido
Conversão em segundos
@@ -239,9 +239,9 @@
-

@(ViewBag.FinalCtaTitle ?? "Pronto para Converter?")

-

@(ViewBag.FinalCtaSubtitle ?? "Use nossa ferramenta gratuita agora mesmo")

- +

@(ViewBag.FinalCtaTitle ?? "Pronto para Converter?")

+

@(ViewBag.FinalCtaSubtitle ?? "Use nossa ferramenta gratuita agora mesmo")

+
@(ViewBag.FinalCtaButtonText ?? "Começar Conversão")
diff --git a/OnlyOneAccessTemplate/Views/Shared/_Header.cshtml b/OnlyOneAccessTemplate/Views/Shared/_Header.cshtml index 46f8f5b..062f828 100644 --- a/OnlyOneAccessTemplate/Views/Shared/_Header.cshtml +++ b/OnlyOneAccessTemplate/Views/Shared/_Header.cshtml @@ -2,7 +2,7 @@
? - @ViewBag.SiteName + @ViewBag.SiteName
- + @ViewBag.CtaButtonText
diff --git a/OnlyOneAccessTemplate/appsettings.json b/OnlyOneAccessTemplate/appsettings.json index 2776582..0771632 100644 --- a/OnlyOneAccessTemplate/appsettings.json +++ b/OnlyOneAccessTemplate/appsettings.json @@ -25,6 +25,10 @@ "DatabaseName": "text_case_converter_db" }, + "TextConversionApi": { + "BaseUrl": "https://localhost:7071" + }, + "SEO": { "DefaultDomain": "https://maiusculasminusculas.com", "DefaultSiteName": "Maiúsculas para 1a. minúsculas online", diff --git a/OnlyOneAccessTemplate/wwwroot/css/site.css b/OnlyOneAccessTemplate/wwwroot/css/site.css index 630d393..ad2a1c3 100644 --- a/OnlyOneAccessTemplate/wwwroot/css/site.css +++ b/OnlyOneAccessTemplate/wwwroot/css/site.css @@ -1,9 +1,12 @@ /* Custom CSS for Conversion Template */ :root { - --primary-color: #667eea; - --secondary-color: #764ba2; + --primary-color: #28a745; + --secondary-color: #20c997; --success-color: #28a745; + --accent-color: #34ce57; + --light-green: #e8f5e8; + --dark-green: #1e7e34; --warning-color: #ffc107; --danger-color: #dc3545; --info-color: #17a2b8; @@ -110,6 +113,10 @@ h1, h2, h3, h4, h5, h6 { background: linear-gradient(135deg, var(--primary-color) 0%, var(--secondary-color) 100%); } +.bg-gradient-green { + background: linear-gradient(135deg, var(--primary-color) 0%, var(--accent-color) 100%); +} + .text-gradient { background: linear-gradient(135deg, var(--primary-color), var(--secondary-color)); -webkit-background-clip: text; @@ -117,18 +124,24 @@ h1, h2, h3, h4, h5, h6 { background-clip: text; } +.text-green-light { + color: var(--accent-color) !important; +} + .shadow-custom { - box-shadow: 0 10px 25px rgba(102, 126, 234, 0.15); + box-shadow: 0 10px 25px rgba(40, 167, 69, 0.15); } /* Header Styles */ .navbar { transition: var(--transition); backdrop-filter: blur(10px); + background-color: #ffffff !important; + border-bottom: 1px solid var(--light-green); } .navbar.scrolled { - background: rgba(255, 255, 255, 0.95) !important; + background: rgba(255, 255, 255, 0.98) !important; box-shadow: var(--box-shadow); } @@ -141,6 +154,7 @@ h1, h2, h3, h4, h5, h6 { font-weight: 500; transition: var(--transition); position: relative; + color: #666 !important; } .nav-link:hover, @@ -165,6 +179,54 @@ h1, h2, h3, h4, h5, h6 { width: 100%; } +/* Melhorar dropdown */ +.dropdown-menu { + border: 1px solid var(--light-green); + box-shadow: 0 4px 6px rgba(40, 167, 69, 0.1); +} + +.dropdown-item { + color: #666; + transition: var(--transition); +} + + .dropdown-item:hover { + background-color: var(--light-green); + color: var(--primary-color); + } + + .dropdown-item.active { + background-color: var(--primary-color); + color: white; + } + +/* Melhorar botão do idioma */ +.btn-outline-success { + border-color: var(--primary-color); + color: var(--primary-color); +} + + .btn-outline-success:hover { + background-color: var(--primary-color); + border-color: var(--primary-color); + color: white; + } + +/* Melhorar navbar toggler */ +.navbar-toggler { + border-color: var(--primary-color); + padding: 4px 8px; +} + + .navbar-toggler:focus { + box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.25); + border-color: var(--primary-color); + } + +.navbar-toggler-icon { + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%2840, 167, 69, 1%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e"); +} + /* Hero Section */ .hero-section { min-height: 100vh; @@ -192,7 +254,7 @@ h1, h2, h3, h4, h5, h6 { } .hero-features .fas { - color: #28a745; + color: var(--accent-color); } .scroll-indicator { @@ -230,7 +292,7 @@ h1, h2, h3, h4, h5, h6 { } .conversion-section { - background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%); + background: linear-gradient(135deg, var(--light-green) 0%, #f8f9fa 100%); } .form-control, @@ -276,14 +338,15 @@ h1, h2, h3, h4, h5, h6 { } .btn-primary { - background: linear-gradient(135deg, var(--primary-color), var(--secondary-color)); + background: linear-gradient(135deg, var(--primary-color), var(--accent-color)); border: none; - box-shadow: 0 5px 15px rgba(102, 126, 234, 0.3); + box-shadow: 0 5px 15px rgba(40, 167, 69, 0.3); } .btn-primary:hover { transform: translateY(-2px); - box-shadow: 0 8px 25px rgba(102, 126, 234, 0.4); + box-shadow: 0 8px 25px rgba(40, 167, 69, 0.4); + background: linear-gradient(135deg, var(--dark-green), var(--primary-color)); } .btn-lg {