feat: nova versão simplificada
This commit is contained in:
parent
c7ada0119d
commit
16001eb15b
@ -6,7 +6,7 @@ using OnlyOneAccessTemplate.Models;
|
|||||||
|
|
||||||
namespace OnlyOneAccessTemplate.Controllers
|
namespace OnlyOneAccessTemplate.Controllers
|
||||||
{
|
{
|
||||||
public abstract class BaseController : Controller
|
public class BaseController : Controller
|
||||||
{
|
{
|
||||||
protected readonly ISiteConfigurationService _siteConfig;
|
protected readonly ISiteConfigurationService _siteConfig;
|
||||||
protected readonly ILanguageService _languageService;
|
protected readonly ILanguageService _languageService;
|
||||||
@ -65,23 +65,23 @@ namespace OnlyOneAccessTemplate.Controllers
|
|||||||
// Basic SEO
|
// Basic SEO
|
||||||
ViewBag.Language = language;
|
ViewBag.Language = language;
|
||||||
ViewBag.Direction = _languageService.GetLanguageDirection(language);
|
ViewBag.Direction = _languageService.GetLanguageDirection(language);
|
||||||
ViewBag.SiteName = config.Seo.SiteName;
|
ViewBag.SiteName = config.SiteTitle;
|
||||||
ViewBag.SiteDescription = config.Seo.DefaultDescription;
|
ViewBag.SiteDescription = config.SiteDescription;
|
||||||
ViewBag.CanonicalUrl = currentUrl;
|
ViewBag.CanonicalUrl = currentUrl;
|
||||||
ViewBag.Author = config.Seo.Author;
|
ViewBag.Author = "Convert-it Online Team";
|
||||||
ViewBag.GTMId = config.Seo.GoogleTagManagerId;
|
ViewBag.GTMId = _configuration.GetValue<string>("SEO:GoogleTagManagerId");
|
||||||
|
|
||||||
// Default SEO values
|
// Default SEO values
|
||||||
ViewBag.Title = ViewBag.Title ?? config.Seo.DefaultTitle;
|
ViewBag.Title = ViewBag.Title ?? config.SiteTitle;
|
||||||
ViewBag.Description = ViewBag.Description ?? config.Seo.DefaultDescription;
|
ViewBag.Description = ViewBag.Description ?? config.SiteDescription;
|
||||||
ViewBag.Keywords = ViewBag.Keywords ?? config.Seo.DefaultKeywords;
|
ViewBag.Keywords = ViewBag.Keywords ?? config.SiteKeywords;
|
||||||
|
|
||||||
// Open Graph
|
// Open Graph
|
||||||
ViewBag.OgTitle = ViewBag.Title;
|
ViewBag.OgTitle = ViewBag.Title;
|
||||||
ViewBag.OgDescription = ViewBag.Description;
|
ViewBag.OgDescription = ViewBag.Description;
|
||||||
ViewBag.OgImage = config.Seo.OgImage;
|
ViewBag.OgImage = config.OgImage;
|
||||||
ViewBag.OgLocale = GetOgLocale(language);
|
ViewBag.OgLocale = GetOgLocale(language);
|
||||||
ViewBag.TwitterHandle = config.Seo.TwitterHandle;
|
ViewBag.TwitterHandle = "@convertit_online";
|
||||||
|
|
||||||
// Language alternatives
|
// Language alternatives
|
||||||
SetupHreflangUrls(currentUrl, language);
|
SetupHreflangUrls(currentUrl, language);
|
||||||
@ -119,8 +119,9 @@ namespace OnlyOneAccessTemplate.Controllers
|
|||||||
ViewBag.LogoUrl = "/img/logo.png";
|
ViewBag.LogoUrl = "/img/logo.png";
|
||||||
ViewBag.BodyClass = $"lang-{language}";
|
ViewBag.BodyClass = $"lang-{language}";
|
||||||
|
|
||||||
// Conversion config
|
// AdSense config
|
||||||
ViewBag.ConversionConfig = config.Conversion;
|
ViewBag.AdSenseClientId = config.AdSenseClientId;
|
||||||
|
ViewBag.AdSenseSlotId = config.AdSenseSlotId;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Nova função para configurar textos específicos do conversor
|
// Nova função para configurar textos específicos do conversor
|
||||||
|
|||||||
@ -34,7 +34,7 @@ namespace OnlyOneAccessTemplate.Controllers
|
|||||||
["text-case"] = typeof(TextCaseConverterService),
|
["text-case"] = typeof(TextCaseConverterService),
|
||||||
["csv-json"] = typeof(CsvToJsonConverterService),
|
["csv-json"] = typeof(CsvToJsonConverterService),
|
||||||
["image-ocr"] = typeof(ImageToTextConverterService),
|
["image-ocr"] = typeof(ImageToTextConverterService),
|
||||||
["sentence-converter"] = typeof(UpperLowerConversorService)
|
["sentence-converter"] = typeof(ApiBasedSentenceConverterService)
|
||||||
// Adicionar novos conversores aqui
|
// Adicionar novos conversores aqui
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,13 +1,12 @@
|
|||||||
using global::OnlyOneAccessTemplate.Services;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.Extensions.Caching.Memory;
|
using Microsoft.Extensions.Caching.Memory;
|
||||||
|
using OnlyOneAccessTemplate.Models;
|
||||||
using OnlyOneAccessTemplate.Services;
|
using OnlyOneAccessTemplate.Services;
|
||||||
|
|
||||||
namespace OnlyOneAccessTemplate.Controllers
|
namespace OnlyOneAccessTemplate.Controllers
|
||||||
{
|
{
|
||||||
|
|
||||||
[Route("en")]
|
[Route("en")]
|
||||||
public class EnController : HomeController
|
public class EnController : BaseController
|
||||||
{
|
{
|
||||||
public EnController(
|
public EnController(
|
||||||
ISiteConfigurationService siteConfig,
|
ISiteConfigurationService siteConfig,
|
||||||
@ -22,22 +21,25 @@ namespace OnlyOneAccessTemplate.Controllers
|
|||||||
protected override string GetCurrentLanguage() => "en";
|
protected override string GetCurrentLanguage() => "en";
|
||||||
|
|
||||||
[Route("")]
|
[Route("")]
|
||||||
[Route("home")]
|
[Route("index")]
|
||||||
public new async Task<IActionResult> 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")]
|
[Route("convert-pdf-to-word")]
|
||||||
public new async Task<IActionResult> About()
|
public IActionResult ConvertPdfToWord()
|
||||||
{
|
{
|
||||||
return await base.About();
|
var siteConfig = _siteConfig.GetConfiguration("en");
|
||||||
}
|
|
||||||
|
|
||||||
[Route("contact")]
|
SetPageSeo("Convert PDF to Word Online Free",
|
||||||
public new async Task<IActionResult> Contact()
|
"Convert your PDF files to Word quickly and free...");
|
||||||
{
|
|
||||||
return await base.Contact();
|
return View("~/Views/Home/Index.cshtml", siteConfig);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,13 +1,12 @@
|
|||||||
using global::OnlyOneAccessTemplate.Services;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.Extensions.Caching.Memory;
|
using Microsoft.Extensions.Caching.Memory;
|
||||||
|
using OnlyOneAccessTemplate.Models;
|
||||||
using OnlyOneAccessTemplate.Services;
|
using OnlyOneAccessTemplate.Services;
|
||||||
|
|
||||||
namespace OnlyOneAccessTemplate.Controllers
|
namespace OnlyOneAccessTemplate.Controllers
|
||||||
{
|
{
|
||||||
|
|
||||||
[Route("es")]
|
[Route("es")]
|
||||||
public class EsController : HomeController
|
public class EsController : BaseController
|
||||||
{
|
{
|
||||||
public EsController(
|
public EsController(
|
||||||
ISiteConfigurationService siteConfig,
|
ISiteConfigurationService siteConfig,
|
||||||
@ -23,21 +22,24 @@ namespace OnlyOneAccessTemplate.Controllers
|
|||||||
|
|
||||||
[Route("")]
|
[Route("")]
|
||||||
[Route("inicio")]
|
[Route("inicio")]
|
||||||
public new async Task<IActionResult> 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")]
|
[Route("convertir-pdf-a-word-en-linea")]
|
||||||
public new async Task<IActionResult> About()
|
public IActionResult ConvertirPdfAWordEnLinea()
|
||||||
{
|
{
|
||||||
return await base.About();
|
var siteConfig = _siteConfig.GetConfiguration("es");
|
||||||
}
|
|
||||||
|
|
||||||
[Route("contacto")]
|
SetPageSeo("Convertir PDF a Word en Línea Gratis",
|
||||||
public new async Task<IActionResult> Contact()
|
"Convierte tus archivos PDF a Word rápidamente y gratis...");
|
||||||
{
|
|
||||||
return await base.Contact();
|
return View("~/Views/Home/Index.cshtml", siteConfig);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,12 +1,10 @@
|
|||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.Extensions.Caching.Memory;
|
using Microsoft.Extensions.Caching.Memory;
|
||||||
using OnlyOneAccessTemplate.Services;
|
using OnlyOneAccessTemplate.Services;
|
||||||
using OnlyOneAccessTemplate.Models;
|
using OnlyOneAccessTemplate.Models;
|
||||||
|
|
||||||
|
|
||||||
namespace OnlyOneAccessTemplate.Controllers
|
namespace OnlyOneAccessTemplate.Controllers
|
||||||
{
|
{
|
||||||
|
|
||||||
public class HomeController : BaseController
|
public class HomeController : BaseController
|
||||||
{
|
{
|
||||||
public HomeController(
|
public HomeController(
|
||||||
@ -19,92 +17,18 @@ namespace OnlyOneAccessTemplate.Controllers
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<IActionResult> Index()
|
[Route("")]
|
||||||
|
[Route("pt")]
|
||||||
|
public IActionResult Index()
|
||||||
{
|
{
|
||||||
var language = GetCurrentLanguage();
|
var language = GetCurrentLanguage();
|
||||||
|
var siteConfig = _siteConfig.GetConfiguration(language);
|
||||||
|
|
||||||
// Get page content from database
|
ViewBag.PageTitle = siteConfig.SiteTitle;
|
||||||
var pageContent = await _siteConfig.GetPageContentAsync(language, "index");
|
ViewBag.PageDescription = siteConfig.SiteDescription;
|
||||||
|
|
||||||
// If no content exists, create default content
|
|
||||||
if (pageContent == null)
|
|
||||||
{
|
|
||||||
await CreateDefaultContentAsync(language);
|
|
||||||
pageContent = await _siteConfig.GetPageContentAsync(language, "index");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set current page for navigation
|
|
||||||
ViewBag.CurrentPage = "home";
|
ViewBag.CurrentPage = "home";
|
||||||
|
|
||||||
return View(pageContent);
|
return View(siteConfig);
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<IActionResult> 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<IActionResult> Contact()
|
|
||||||
{
|
|
||||||
var language = GetCurrentLanguage();
|
|
||||||
var pageContent = await _siteConfig.GetPageContentAsync(language, "contact");
|
|
||||||
|
|
||||||
ViewBag.CurrentPage = "contact";
|
|
||||||
return View(pageContent);
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet("setup-data")]
|
|
||||||
public async Task<IActionResult> 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();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
|
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
|
||||||
@ -113,438 +37,5 @@ namespace OnlyOneAccessTemplate.Controllers
|
|||||||
return View();
|
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<string, PageContent> CreateDefaultPages(string language)
|
|
||||||
{
|
|
||||||
return language switch
|
|
||||||
{
|
|
||||||
"en" => new Dictionary<string, PageContent>
|
|
||||||
{
|
|
||||||
["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<string, PageContent>
|
|
||||||
{
|
|
||||||
["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<string, PageContent>
|
|
||||||
{
|
|
||||||
["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<ContentBlock> CreatePortugueseBlocks()
|
|
||||||
{
|
|
||||||
return new List<ContentBlock>
|
|
||||||
{
|
|
||||||
new ContentBlock
|
|
||||||
{
|
|
||||||
Type = "hero",
|
|
||||||
Title = "Transforme Seu Negócio com Nossa <span class='text-gradient'>Solução Inovadora</span>",
|
|
||||||
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<string, object>
|
|
||||||
{
|
|
||||||
{ "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<string, object>
|
|
||||||
{
|
|
||||||
{ "feature_list", new[]
|
|
||||||
{
|
|
||||||
new Dictionary<string, object>
|
|
||||||
{
|
|
||||||
{ "icon", "fas fa-rocket" },
|
|
||||||
{ "title", "Resultados Rápidos" },
|
|
||||||
{ "description", "Veja os primeiros resultados em até 30 dias com nossa metodologia testada" }
|
|
||||||
},
|
|
||||||
new Dictionary<string, object>
|
|
||||||
{
|
|
||||||
{ "icon", "fas fa-shield-alt" },
|
|
||||||
{ "title", "Garantia Total" },
|
|
||||||
{ "description", "100% de garantia ou seu dinheiro de volta. Assumimos o risco por você" }
|
|
||||||
},
|
|
||||||
new Dictionary<string, object>
|
|
||||||
{
|
|
||||||
{ "icon", "fas fa-users" },
|
|
||||||
{ "title", "Suporte Especializado" },
|
|
||||||
{ "description", "Equipe dedicada 24/7 para garantir seu sucesso em cada etapa" }
|
|
||||||
},
|
|
||||||
new Dictionary<string, object>
|
|
||||||
{
|
|
||||||
{ "icon", "fas fa-chart-line" },
|
|
||||||
{ "title", "Crescimento Sustentável" },
|
|
||||||
{ "description", "Estratégias de longo prazo para crescimento consistente e duradouro" }
|
|
||||||
},
|
|
||||||
new Dictionary<string, object>
|
|
||||||
{
|
|
||||||
{ "icon", "fas fa-cog" },
|
|
||||||
{ "title", "Automatização" },
|
|
||||||
{ "description", "Processos automatizados que trabalham para você 24 horas por dia" }
|
|
||||||
},
|
|
||||||
new Dictionary<string, object>
|
|
||||||
{
|
|
||||||
{ "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<string, object>
|
|
||||||
{
|
|
||||||
{ "testimonials", new[]
|
|
||||||
{
|
|
||||||
new Dictionary<string, object>
|
|
||||||
{
|
|
||||||
{ "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<string, object>
|
|
||||||
{
|
|
||||||
{ "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<string, object>
|
|
||||||
{
|
|
||||||
{ "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 <span class='text-warning'>Transformar</span> 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<string, object>
|
|
||||||
{
|
|
||||||
{ "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<ContentBlock> CreateEnglishBlocks()
|
|
||||||
{
|
|
||||||
return new List<ContentBlock>
|
|
||||||
{
|
|
||||||
new ContentBlock
|
|
||||||
{
|
|
||||||
Type = "hero",
|
|
||||||
Title = "Transform Your Business with Our <span class='text-gradient'>Innovative Solution</span>",
|
|
||||||
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<string, object>
|
|
||||||
{
|
|
||||||
{ "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<string, object>
|
|
||||||
{
|
|
||||||
{ "feature_list", new[]
|
|
||||||
{
|
|
||||||
new Dictionary<string, object>
|
|
||||||
{
|
|
||||||
{ "icon", "fas fa-rocket" },
|
|
||||||
{ "title", "Fast Results" },
|
|
||||||
{ "description", "See first results within 30 days with our tested methodology" }
|
|
||||||
},
|
|
||||||
new Dictionary<string, object>
|
|
||||||
{
|
|
||||||
{ "icon", "fas fa-shield-alt" },
|
|
||||||
{ "title", "Full Guarantee" },
|
|
||||||
{ "description", "100% guarantee or your money back. We take the risk for you" }
|
|
||||||
},
|
|
||||||
new Dictionary<string, object>
|
|
||||||
{
|
|
||||||
{ "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<string, object>
|
|
||||||
{
|
|
||||||
{ "testimonials", new[]
|
|
||||||
{
|
|
||||||
new Dictionary<string, object>
|
|
||||||
{
|
|
||||||
{ "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 <span class='text-warning'>Transform</span> Your Business?",
|
|
||||||
Content = "Join over 1000 companies that have already achieved extraordinary results.",
|
|
||||||
ButtonText = "Start My Transformation",
|
|
||||||
ButtonUrl = "#conversion-form",
|
|
||||||
Order = 4
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<ContentBlock> CreateSpanishBlocks()
|
|
||||||
{
|
|
||||||
return new List<ContentBlock>
|
|
||||||
{
|
|
||||||
new ContentBlock
|
|
||||||
{
|
|
||||||
Type = "hero",
|
|
||||||
Title = "Transforma Tu Negocio con Nuestra <span class='text-gradient'>Solución Innovadora</span>",
|
|
||||||
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<string, object>
|
|
||||||
{
|
|
||||||
{ "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 <span class='text-warning'>Transformar</span> 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<FormField>
|
|
||||||
{
|
|
||||||
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<FormField>
|
|
||||||
{
|
|
||||||
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<FormField>
|
|
||||||
{
|
|
||||||
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 }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
181
OnlyOneAccessTemplate/Controllers/TextConversionApiController.cs
Normal file
181
OnlyOneAccessTemplate/Controllers/TextConversionApiController.cs
Normal file
@ -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<TextConversionApiController> _logger;
|
||||||
|
private readonly IAsyncPolicy<string> _retryPolicy;
|
||||||
|
|
||||||
|
public TextConversionApiController(ILogger<TextConversionApiController> logger)
|
||||||
|
{
|
||||||
|
_logger = logger;
|
||||||
|
|
||||||
|
// Configurar política de retry com Polly
|
||||||
|
_retryPolicy = Policy
|
||||||
|
.Handle<Exception>()
|
||||||
|
.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<string>();
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost("sentence-case")]
|
||||||
|
public async Task<IActionResult> 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<IActionResult> 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<IActionResult> 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<string, string>? 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<string, object>? Metadata { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -11,12 +11,41 @@ namespace OnlyOneAccessTemplate.Models
|
|||||||
public string Id { get; set; } = string.Empty;
|
public string Id { get; set; } = string.Empty;
|
||||||
|
|
||||||
[Required]
|
[Required]
|
||||||
public string Language { get; set; } = "pt";
|
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 SeoConfig Seo { get; set; } = new();
|
|
||||||
public Dictionary<string, PageContent> Pages { get; set; } = new();
|
|
||||||
public ConversionConfig Conversion { get; set; } = new();
|
|
||||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||||
public DateTime UpdatedAt { 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<ConverterCard> 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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,6 +17,8 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="MongoDB.Driver" Version="3.4.0" />
|
<PackageReference Include="MongoDB.Driver" Version="3.4.0" />
|
||||||
|
<PackageReference Include="Polly" Version="8.4.0" />
|
||||||
|
<PackageReference Include="Polly.Extensions.Http" Version="3.0.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@ -35,14 +35,14 @@ builder.Services.AddScoped<ILanguageService, LanguageService>();
|
|||||||
builder.Services.AddScoped<ISeoService, SeoService>();
|
builder.Services.AddScoped<ISeoService, SeoService>();
|
||||||
builder.Services.AddScoped<IConversionService, ConversionService>();
|
builder.Services.AddScoped<IConversionService, ConversionService>();
|
||||||
|
|
||||||
// Converter Services - Registrar todos os conversores disponíveis
|
// Converter Services - Registrar todos os conversores dispon<EFBFBD>veis
|
||||||
builder.Services.AddScoped<TextCaseConverterService>();
|
builder.Services.AddScoped<TextCaseConverterService>();
|
||||||
builder.Services.AddScoped<CsvToJsonConverterService>();
|
builder.Services.AddScoped<CsvToJsonConverterService>();
|
||||||
builder.Services.AddScoped<ImageToTextConverterService>();
|
builder.Services.AddScoped<ImageToTextConverterService>();
|
||||||
|
|
||||||
builder.Services.AddScoped<UpperLowerConversorService>();
|
// Substituindo UpperLowerConversorService por API com Polly
|
||||||
// Adicione aqui novos conversores conforme necessário:
|
builder.Services.AddHttpClient<ITextConversionApiService, TextConversionApiService>();
|
||||||
// builder.Services.AddScoped<SeuNovoConverterService>();
|
builder.Services.AddScoped<ApiBasedSentenceConverterService>();
|
||||||
|
|
||||||
// HttpClient for external calls
|
// HttpClient for external calls
|
||||||
builder.Services.AddHttpClient<ConversionService>();
|
builder.Services.AddHttpClient<ConversionService>();
|
||||||
@ -96,22 +96,15 @@ app.MapControllerRoute(
|
|||||||
pattern: "converter/config/{converterType}",
|
pattern: "converter/config/{converterType}",
|
||||||
defaults: new { controller = "Converter", action = "GetConverterConfig" });
|
defaults: new { controller = "Converter", action = "GetConverterConfig" });
|
||||||
|
|
||||||
|
// Rotas específicas por idioma
|
||||||
app.MapControllerRoute(
|
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?}");
|
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
|
// SEO Routes
|
||||||
app.MapGet("/sitemap.xml", async (ISiteConfigurationService siteConfig) =>
|
app.MapGet("/sitemap.xml", async (ISiteConfigurationService siteConfig) =>
|
||||||
{
|
{
|
||||||
@ -137,21 +130,21 @@ if (app.Environment.IsDevelopment())
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// Verificar se já existem configurações
|
// Verificar se j<EFBFBD> existem configura<72><61>es
|
||||||
var languages = new[] { "pt", "en", "es" };
|
var languages = new[] { "pt", "en", "es" };
|
||||||
foreach (var lang in languages)
|
foreach (var lang in languages)
|
||||||
{
|
{
|
||||||
var configExists = await siteConfigService.ConfigurationExistsAsync(lang);
|
var configExists = await siteConfigService.ConfigurationExistsAsync(lang);
|
||||||
if (!configExists)
|
if (!configExists)
|
||||||
{
|
{
|
||||||
logger.LogInformation("Criando configuração padrão para idioma: {Language}", lang);
|
logger.LogInformation("Criando configura<EFBFBD><EFBFBD>o padr<64>o para idioma: {Language}", lang);
|
||||||
_ = await siteConfigService.GetConfigurationAsync(lang); // Isso criará a configuração padrão
|
_ = await siteConfigService.GetConfigurationAsync(lang); // Isso criar<EFBFBD> a configura<72><61>o padr<64>o
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
logger.LogError(ex, "Erro ao inicializar dados padrão");
|
logger.LogError(ex, "Erro ao inicializar dados padr<EFBFBD>o");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -65,16 +65,16 @@ namespace OnlyOneAccessTemplate.Services
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Enviar para webhook se configurado
|
// Enviar para webhook se configurado
|
||||||
if (!string.IsNullOrEmpty(configuration.Conversion.WebhookUrl))
|
//if (!string.IsNullOrEmpty(configuration..Conversion.WebhookUrl))
|
||||||
{
|
//{
|
||||||
_ = Task.Run(() => SendWebhookAsync(configuration.Conversion.WebhookUrl, conversionRecord));
|
// _ = Task.Run(() => SendWebhookAsync(configuration.Conversion.WebhookUrl, conversionRecord));
|
||||||
}
|
//}
|
||||||
|
|
||||||
// Disparar pixel de conversão se configurado
|
//// Disparar pixel de conversão se configurado
|
||||||
if (!string.IsNullOrEmpty(configuration.Conversion.ConversionPixel))
|
//if (!string.IsNullOrEmpty(configuration.Conversion.ConversionPixel))
|
||||||
{
|
//{
|
||||||
_ = Task.Run(() => FireConversionPixelAsync(configuration.Conversion.ConversionPixel, conversionRecord));
|
// _ = Task.Run(() => FireConversionPixelAsync(configuration.Conversion.ConversionPixel, conversionRecord));
|
||||||
}
|
//}
|
||||||
|
|
||||||
_logger.LogInformation("Conversão processada com sucesso: {ConversionId}", conversionRecord.Id);
|
_logger.LogInformation("Conversão processada com sucesso: {ConversionId}", conversionRecord.Id);
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@ -6,7 +6,6 @@ namespace OnlyOneAccessTemplate.Services
|
|||||||
{
|
{
|
||||||
Task<SiteConfiguration> GetConfigurationAsync(string language);
|
Task<SiteConfiguration> GetConfigurationAsync(string language);
|
||||||
SiteConfiguration GetConfiguration(string language); // Versão síncrona para o BaseController
|
SiteConfiguration GetConfiguration(string language); // Versão síncrona para o BaseController
|
||||||
Task<PageContent?> GetPageContentAsync(string language, string pageName);
|
|
||||||
Task<string> GenerateSitemapAsync();
|
Task<string> GenerateSitemapAsync();
|
||||||
Task<string> GenerateRobotsAsync();
|
Task<string> GenerateRobotsAsync();
|
||||||
Task UpdateConfigurationAsync(SiteConfiguration config);
|
Task UpdateConfigurationAsync(SiteConfiguration config);
|
||||||
|
|||||||
@ -42,19 +42,19 @@ namespace OnlyOneAccessTemplate.Services
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
var configuration = await _siteConfig.GetConfigurationAsync(language);
|
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<string>("SEO:DefaultDomain") ?? "https://localhost";
|
var baseUrl = _configuration.GetValue<string>("SEO:DefaultDomain") ?? "https://localhost";
|
||||||
var canonicalUrl = BuildCanonicalUrl(baseUrl, language, pageName);
|
var canonicalUrl = BuildCanonicalUrl(baseUrl, language, pageName);
|
||||||
var hreflangUrls = _languageService.GetHreflangUrls(canonicalUrl, language);
|
var hreflangUrls = _languageService.GetHreflangUrls(canonicalUrl, language);
|
||||||
|
|
||||||
var metadata = new SeoMetadata(
|
var metadata = new SeoMetadata(
|
||||||
Title: content?.MetaTitle ?? content?.Title ?? configuration.Seo.DefaultTitle,
|
Title: content?.MetaTitle ?? content?.Title ?? configuration.SiteTitle,
|
||||||
Description: content?.Description ?? configuration.Seo.DefaultDescription,
|
Description: content?.Description ?? configuration.SiteDescription,
|
||||||
Keywords: content?.Keywords ?? configuration.Seo.DefaultKeywords,
|
Keywords: content?.Keywords ?? configuration.SiteKeywords,
|
||||||
OgTitle: content?.Title ?? configuration.Seo.DefaultTitle,
|
OgTitle: content?.Title ?? configuration.SiteTitle,
|
||||||
OgDescription: content?.Description ?? configuration.Seo.DefaultDescription,
|
OgDescription: content?.Description ?? configuration.SiteDescription,
|
||||||
OgImage: content?.OgImage ?? configuration.Seo.OgImage,
|
OgImage: content?.OgImage ?? configuration.OgImage,
|
||||||
CanonicalUrl: canonicalUrl,
|
CanonicalUrl: canonicalUrl,
|
||||||
HreflangUrls: hreflangUrls,
|
HreflangUrls: hreflangUrls,
|
||||||
StructuredData: await GenerateStructuredDataAsync(language, "WebPage", new { content, configuration })
|
StructuredData: await GenerateStructuredDataAsync(language, "WebPage", new { content, configuration })
|
||||||
@ -138,14 +138,14 @@ namespace OnlyOneAccessTemplate.Services
|
|||||||
{
|
{
|
||||||
Context = "https://schema.org",
|
Context = "https://schema.org",
|
||||||
Type = "WebPage",
|
Type = "WebPage",
|
||||||
Name = config.Seo.SiteName,
|
Name = config.SiteTitle,
|
||||||
Description = config.Seo.DefaultDescription,
|
Description = config.SiteDescription,
|
||||||
Url = baseUrl,
|
Url = baseUrl,
|
||||||
InLanguage = language,
|
InLanguage = language,
|
||||||
IsPartOf = new
|
IsPartOf = new
|
||||||
{
|
{
|
||||||
Type = "WebSite",
|
Type = "WebSite",
|
||||||
Name = config.Seo.SiteName,
|
Name = config.SiteTitle,
|
||||||
Url = baseUrl
|
Url = baseUrl
|
||||||
},
|
},
|
||||||
DatePublished = DateTime.UtcNow.ToString("yyyy-MM-dd"),
|
DatePublished = DateTime.UtcNow.ToString("yyyy-MM-dd"),
|
||||||
@ -239,7 +239,7 @@ namespace OnlyOneAccessTemplate.Services
|
|||||||
{
|
{
|
||||||
Context = "https://schema.org",
|
Context = "https://schema.org",
|
||||||
Type = "Organization",
|
Type = "Organization",
|
||||||
Name = config.Seo.SiteName,
|
Name = config.SiteTitle,
|
||||||
Url = baseUrl,
|
Url = baseUrl,
|
||||||
Logo = new
|
Logo = new
|
||||||
{
|
{
|
||||||
@ -312,7 +312,7 @@ namespace OnlyOneAccessTemplate.Services
|
|||||||
Brand = new
|
Brand = new
|
||||||
{
|
{
|
||||||
Type = "Brand",
|
Type = "Brand",
|
||||||
Name = config.Seo.SiteName
|
Name = config.SiteTitle
|
||||||
},
|
},
|
||||||
Offers = new
|
Offers = new
|
||||||
{
|
{
|
||||||
|
|||||||
@ -85,17 +85,6 @@ namespace OnlyOneAccessTemplate.Services
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<PageContent?> 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<string> GenerateSitemapAsync()
|
public async Task<string> GenerateSitemapAsync()
|
||||||
{
|
{
|
||||||
@ -215,15 +204,7 @@ namespace OnlyOneAccessTemplate.Services
|
|||||||
|
|
||||||
private async Task<SiteConfiguration> CreateDefaultConfigurationAsync(string language)
|
private async Task<SiteConfiguration> CreateDefaultConfigurationAsync(string language)
|
||||||
{
|
{
|
||||||
var defaultConfig = new SiteConfiguration
|
var defaultConfig = CreateDefaultConfiguration(language);
|
||||||
{
|
|
||||||
Language = language,
|
|
||||||
Seo = CreateDefaultSeoConfig(language),
|
|
||||||
Pages = CreateDefaultPages(language),
|
|
||||||
Conversion = CreateDefaultConversionConfig(language),
|
|
||||||
CreatedAt = DateTime.UtcNow,
|
|
||||||
UpdatedAt = DateTime.UtcNow
|
|
||||||
};
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@ -240,17 +221,54 @@ namespace OnlyOneAccessTemplate.Services
|
|||||||
|
|
||||||
private SiteConfiguration CreateDefaultConfiguration(string language)
|
private SiteConfiguration CreateDefaultConfiguration(string language)
|
||||||
{
|
{
|
||||||
var defaultConfig = new SiteConfiguration
|
return language switch
|
||||||
{
|
{
|
||||||
Language = language,
|
"en" => new SiteConfiguration
|
||||||
Seo = CreateDefaultSeoConfig(language),
|
{
|
||||||
Pages = CreateDefaultPages(language),
|
Language = "en",
|
||||||
Conversion = CreateDefaultConversionConfig(language),
|
Currency = "USD",
|
||||||
CreatedAt = DateTime.UtcNow,
|
CountryCode = "US",
|
||||||
UpdatedAt = DateTime.UtcNow
|
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<bool> ConfigurationExistsAsync(string language)
|
public async Task<bool> ConfigurationExistsAsync(string language)
|
||||||
@ -288,140 +306,6 @@ namespace OnlyOneAccessTemplate.Services
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private SeoConfig CreateDefaultSeoConfig(string language)
|
|
||||||
{
|
|
||||||
var defaultSiteName = _config.GetValue<string>("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<string>("SEO:GoogleTagManagerId") ?? "",
|
|
||||||
GoogleAnalyticsId = _config.GetValue<string>("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<string>("SEO:GoogleTagManagerId") ?? "",
|
|
||||||
GoogleAnalyticsId = _config.GetValue<string>("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<string>("SEO:GoogleTagManagerId") ?? "",
|
|
||||||
GoogleAnalyticsId = _config.GetValue<string>("SEO:GoogleAnalyticsId") ?? "",
|
|
||||||
Author = "Equipe Site de Conversão"
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private Dictionary<string, PageContent> CreateDefaultPages(string language)
|
|
||||||
{
|
|
||||||
return language switch
|
|
||||||
{
|
|
||||||
"en" => new Dictionary<string, PageContent>
|
|
||||||
{
|
|
||||||
["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<string, PageContent>
|
|
||||||
{
|
|
||||||
["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<string, PageContent>
|
|
||||||
{
|
|
||||||
["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<FormField>
|
|
||||||
{
|
|
||||||
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<FormField>
|
|
||||||
{
|
|
||||||
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<FormField>
|
|
||||||
{
|
|
||||||
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)
|
private string BuildUrl(string baseUrl, string language, string page)
|
||||||
{
|
{
|
||||||
|
|||||||
207
OnlyOneAccessTemplate/Services/TextConversionApiService.cs
Normal file
207
OnlyOneAccessTemplate/Services/TextConversionApiService.cs
Normal file
@ -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<string> ConvertToSentenceCaseAsync(string text);
|
||||||
|
Task<string> ConvertToUpperCaseAsync(string text);
|
||||||
|
Task<string> ConvertToLowerCaseAsync(string text);
|
||||||
|
Task<bool> IsHealthyAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
public class TextConversionApiService : ITextConversionApiService
|
||||||
|
{
|
||||||
|
private readonly HttpClient _httpClient;
|
||||||
|
private readonly ILogger<TextConversionApiService> _logger;
|
||||||
|
private readonly IAsyncPolicy<HttpResponseMessage> _retryPolicy;
|
||||||
|
private readonly string _baseUrl;
|
||||||
|
|
||||||
|
public TextConversionApiService(
|
||||||
|
HttpClient httpClient,
|
||||||
|
ILogger<TextConversionApiService> logger,
|
||||||
|
IConfiguration configuration)
|
||||||
|
{
|
||||||
|
_httpClient = httpClient;
|
||||||
|
_logger = logger;
|
||||||
|
_baseUrl = configuration.GetValue<string>("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<string> 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<string> 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<string> 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<bool> 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<TextConversionApiResponse> 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<TextConversionApiResponse>(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<string, object>? 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<ApiBasedSentenceConverterService> logger,
|
||||||
|
IConfiguration configuration,
|
||||||
|
ITextConversionApiService apiService)
|
||||||
|
: base(logger, configuration)
|
||||||
|
{
|
||||||
|
_apiService = apiService;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async Task<ConversionResult> 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
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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<UpperLowerConversorService> logger, IConfiguration configuration)
|
|
||||||
: base(logger, configuration) { }
|
|
||||||
|
|
||||||
public override async Task<ConversionResult> 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
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -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";
|
Layout = "~/Views/Shared/_Layout.cshtml";
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -69,10 +69,10 @@
|
|||||||
<div class="col-lg-11">
|
<div class="col-lg-11">
|
||||||
<!-- Título e Descrição -->
|
<!-- Título e Descrição -->
|
||||||
<div class="text-center mb-4">
|
<div class="text-center mb-4">
|
||||||
<h1 class="display-4 fw-bold text-primary mb-3">
|
<h1 class="display-4 fw-bold text-gradient mb-3">
|
||||||
@(ViewBag.ConverterTitle ?? "CONVERSOR ONLINE")
|
@(ViewBag.ConverterTitle ?? "CONVERSOR ONLINE")
|
||||||
</h1>
|
</h1>
|
||||||
<p class="lead text-muted mb-4">
|
<p class="lead text-green-light mb-4">
|
||||||
@(ViewBag.ConverterDescription ?? "Converta seus arquivos de forma rápida e segura")
|
@(ViewBag.ConverterDescription ?? "Converta seus arquivos de forma rápida e segura")
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@ -97,23 +97,23 @@
|
|||||||
<div class="row text-center mb-4">
|
<div class="row text-center mb-4">
|
||||||
<div class="col-md-4 mb-3">
|
<div class="col-md-4 mb-3">
|
||||||
<div class="step-indicator">
|
<div class="step-indicator">
|
||||||
<span class="badge bg-primary rounded-circle p-3 fs-5 mb-2">1</span>
|
<span class="badge bg-gradient-green rounded-circle p-3 fs-5 mb-2 text-white">1</span>
|
||||||
<h6 class="fw-bold">@(ViewBag.Step1Title ?? "Upload")</h6>
|
<h6 class="fw-bold text-green-light">@(ViewBag.Step1Title ?? "Digite")</h6>
|
||||||
<small class="text-muted">@(ViewBag.Step1Description ?? "Selecione seu arquivo")</small>
|
<small class="text-muted">@(ViewBag.Step1Description ?? "Digite seu texto")</small>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-4 mb-3">
|
<div class="col-md-4 mb-3">
|
||||||
<div class="step-indicator">
|
<div class="step-indicator">
|
||||||
<span class="badge bg-primary rounded-circle p-3 fs-5 mb-2">2</span>
|
<span class="badge bg-gradient-green rounded-circle p-3 fs-5 mb-2 text-white">2</span>
|
||||||
<h6 class="fw-bold">@(ViewBag.Step2Title ?? "Processar")</h6>
|
<h6 class="fw-bold text-green-light">@(ViewBag.Step2Title ?? "Converter")</h6>
|
||||||
<small class="text-muted">@(ViewBag.Step2Description ?? "Aguarde o processamento")</small>
|
<small class="text-muted">@(ViewBag.Step2Description ?? "Clique para converter")</small>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-4 mb-3">
|
<div class="col-md-4 mb-3">
|
||||||
<div class="step-indicator">
|
<div class="step-indicator">
|
||||||
<span class="badge bg-success rounded-circle p-3 fs-5 mb-2">3</span>
|
<span class="badge bg-gradient-green rounded-circle p-3 fs-5 mb-2 text-white">3</span>
|
||||||
<h6 class="fw-bold">@(ViewBag.Step3Title ?? "Download")</h6>
|
<h6 class="fw-bold text-green-light">@(ViewBag.Step3Title ?? "Copiar")</h6>
|
||||||
<small class="text-muted">@(ViewBag.Step3Description ?? "Baixe o resultado")</small>
|
<small class="text-muted">@(ViewBag.Step3Description ?? "Copie o resultado")</small>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -128,13 +128,13 @@
|
|||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<div class="d-flex align-items-center mb-2">
|
<div class="d-flex align-items-center mb-2">
|
||||||
<i class="fas fa-shield-alt text-success me-2"></i>
|
<i class="fas fa-shield-alt text-success me-2"></i>
|
||||||
<small class="text-muted">@(ViewBag.SecurityText ?? "Seus dados estão seguros")</small>
|
<small class="text-green-light fw-bold">@(ViewBag.SecurityText ?? "Seus dados estão seguros")</small>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<div class="d-flex align-items-center mb-2">
|
<div class="d-flex align-items-center mb-2">
|
||||||
<i class="fas fa-file-alt text-primary me-2"></i>
|
<i class="fas fa-bolt text-success me-2"></i>
|
||||||
<small class="text-muted">@(ViewBag.FileInfoText ?? "Processamento rápido e seguro")</small>
|
<small class="text-green-light fw-bold">@(ViewBag.FileInfoText ?? "Processamento rápido e seguro")</small>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -175,8 +175,8 @@
|
|||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-lg-10 mx-auto text-center mb-5">
|
<div class="col-lg-10 mx-auto text-center mb-5">
|
||||||
<h2 class="h3 fw-bold mb-3">@(ViewBag.BenefitsTitle ?? "Por Que Usar Nossa Ferramenta?")</h2>
|
<h2 class="h3 fw-bold mb-3 text-gradient">@(ViewBag.BenefitsTitle ?? "Por Que Usar Nossa Ferramenta?")</h2>
|
||||||
<p class="text-muted">@(ViewBag.BenefitsSubtitle ?? "Descubra os benefícios de nossa solução")</p>
|
<p class="text-green-light">@(ViewBag.BenefitsSubtitle ?? "Descubra os benefícios de nossa solução")</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -185,36 +185,36 @@
|
|||||||
<div class="col-md-6 col-lg-3">
|
<div class="col-md-6 col-lg-3">
|
||||||
<div class="text-center p-3">
|
<div class="text-center p-3">
|
||||||
<div class="feature-icon mb-3">
|
<div class="feature-icon mb-3">
|
||||||
<i class="fas fa-rocket fa-2x text-primary"></i>
|
<i class="fas fa-rocket fa-2x text-success"></i>
|
||||||
</div>
|
</div>
|
||||||
<h6 class="fw-bold">@(ViewBag.Feature1Title ?? "Rápido e Fácil")</h6>
|
<h6 class="fw-bold text-green-light">@(ViewBag.Feature1Title ?? "Rápido e Fácil")</h6>
|
||||||
<small class="text-muted">@(ViewBag.Feature1Description ?? "Conversão instantânea")</small>
|
<small class="text-muted">@(ViewBag.Feature1Description ?? "Conversão instantânea")</small>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-6 col-lg-3">
|
<div class="col-md-6 col-lg-3">
|
||||||
<div class="text-center p-3">
|
<div class="text-center p-3">
|
||||||
<div class="feature-icon mb-3">
|
<div class="feature-icon mb-3">
|
||||||
<i class="fas fa-shield-alt fa-2x text-primary"></i>
|
<i class="fas fa-shield-alt fa-2x text-success"></i>
|
||||||
</div>
|
</div>
|
||||||
<h6 class="fw-bold">@(ViewBag.Feature2Title ?? "Seguro")</h6>
|
<h6 class="fw-bold text-green-light">@(ViewBag.Feature2Title ?? "Seguro")</h6>
|
||||||
<small class="text-muted">@(ViewBag.Feature2Description ?? "Dados protegidos")</small>
|
<small class="text-muted">@(ViewBag.Feature2Description ?? "Dados protegidos")</small>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-6 col-lg-3">
|
<div class="col-md-6 col-lg-3">
|
||||||
<div class="text-center p-3">
|
<div class="text-center p-3">
|
||||||
<div class="feature-icon mb-3">
|
<div class="feature-icon mb-3">
|
||||||
<i class="fas fa-users fa-2x text-primary"></i>
|
<i class="fas fa-users fa-2x text-success"></i>
|
||||||
</div>
|
</div>
|
||||||
<h6 class="fw-bold">@(ViewBag.Feature3Title ?? "Confiável")</h6>
|
<h6 class="fw-bold text-green-light">@(ViewBag.Feature3Title ?? "Confiável")</h6>
|
||||||
<small class="text-muted">@(ViewBag.Feature3Description ?? "Resultados precisos")</small>
|
<small class="text-muted">@(ViewBag.Feature3Description ?? "Resultados precisos")</small>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-6 col-lg-3">
|
<div class="col-md-6 col-lg-3">
|
||||||
<div class="text-center p-3">
|
<div class="text-center p-3">
|
||||||
<div class="feature-icon mb-3">
|
<div class="feature-icon mb-3">
|
||||||
<i class="fas fa-clock fa-2x text-primary"></i>
|
<i class="fas fa-clock fa-2x text-success"></i>
|
||||||
</div>
|
</div>
|
||||||
<h6 class="fw-bold">Rápido</h6>
|
<h6 class="fw-bold text-green-light">Rápido</h6>
|
||||||
<small class="text-muted">Conversão em segundos</small>
|
<small class="text-muted">Conversão em segundos</small>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -239,9 +239,9 @@
|
|||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-lg-8 mx-auto text-center">
|
<div class="col-lg-8 mx-auto text-center">
|
||||||
<h2 class="h3 fw-bold mb-3">@(ViewBag.FinalCtaTitle ?? "Pronto para Converter?")</h2>
|
<h2 class="h3 fw-bold mb-3 text-gradient">@(ViewBag.FinalCtaTitle ?? "Pronto para Converter?")</h2>
|
||||||
<p class="text-muted mb-4">@(ViewBag.FinalCtaSubtitle ?? "Use nossa ferramenta gratuita agora mesmo")</p>
|
<p class="text-green-light mb-4">@(ViewBag.FinalCtaSubtitle ?? "Use nossa ferramenta gratuita agora mesmo")</p>
|
||||||
<a href="#converter-container" class="btn btn-primary btn-lg">
|
<a href="#converter-container" class="btn btn-primary btn-lg hover-lift">
|
||||||
@(ViewBag.FinalCtaButtonText ?? "Começar Conversão")
|
@(ViewBag.FinalCtaButtonText ?? "Começar Conversão")
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
<div class="container">
|
<div class="container">
|
||||||
<a class="navbar-brand d-flex align-items-center" href="@ViewBag.HomeUrl">
|
<a class="navbar-brand d-flex align-items-center" href="@ViewBag.HomeUrl">
|
||||||
<img src="@ViewBag.LogoUrl" alt="?" height="40" class="me-2">
|
<img src="@ViewBag.LogoUrl" alt="?" height="40" class="me-2">
|
||||||
<span class="fw-bold text-primary">@ViewBag.SiteName</span>
|
<span class="fw-bold text-gradient">@ViewBag.SiteName</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav"
|
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav"
|
||||||
@ -34,7 +34,7 @@
|
|||||||
|
|
||||||
<!-- Language Switcher -->
|
<!-- Language Switcher -->
|
||||||
<div class="dropdown me-3">
|
<div class="dropdown me-3">
|
||||||
<button class="btn btn-outline-secondary btn-sm dropdown-toggle" type="button"
|
<button class="btn btn-outline-success btn-sm dropdown-toggle" type="button"
|
||||||
id="languageDropdown" data-bs-toggle="dropdown" aria-expanded="false">
|
id="languageDropdown" data-bs-toggle="dropdown" aria-expanded="false">
|
||||||
<i class="fas fa-globe me-1"></i>
|
<i class="fas fa-globe me-1"></i>
|
||||||
@ViewBag.CurrentLanguageDisplay
|
@ViewBag.CurrentLanguageDisplay
|
||||||
@ -56,7 +56,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- CTA Button -->
|
<!-- CTA Button -->
|
||||||
<a href="#conversion-form" class="btn btn-primary btn-sm scroll-to-form">
|
<a href="#conversion-form" class="btn btn-primary btn-sm scroll-to-form hover-lift">
|
||||||
@ViewBag.CtaButtonText
|
@ViewBag.CtaButtonText
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -25,6 +25,10 @@
|
|||||||
"DatabaseName": "text_case_converter_db"
|
"DatabaseName": "text_case_converter_db"
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"TextConversionApi": {
|
||||||
|
"BaseUrl": "https://localhost:7071"
|
||||||
|
},
|
||||||
|
|
||||||
"SEO": {
|
"SEO": {
|
||||||
"DefaultDomain": "https://maiusculasminusculas.com",
|
"DefaultDomain": "https://maiusculasminusculas.com",
|
||||||
"DefaultSiteName": "Maiúsculas para 1a. minúsculas online",
|
"DefaultSiteName": "Maiúsculas para 1a. minúsculas online",
|
||||||
|
|||||||
@ -1,9 +1,12 @@
|
|||||||
/* Custom CSS for Conversion Template */
|
/* Custom CSS for Conversion Template */
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
--primary-color: #667eea;
|
--primary-color: #28a745;
|
||||||
--secondary-color: #764ba2;
|
--secondary-color: #20c997;
|
||||||
--success-color: #28a745;
|
--success-color: #28a745;
|
||||||
|
--accent-color: #34ce57;
|
||||||
|
--light-green: #e8f5e8;
|
||||||
|
--dark-green: #1e7e34;
|
||||||
--warning-color: #ffc107;
|
--warning-color: #ffc107;
|
||||||
--danger-color: #dc3545;
|
--danger-color: #dc3545;
|
||||||
--info-color: #17a2b8;
|
--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%);
|
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 {
|
.text-gradient {
|
||||||
background: linear-gradient(135deg, var(--primary-color), var(--secondary-color));
|
background: linear-gradient(135deg, var(--primary-color), var(--secondary-color));
|
||||||
-webkit-background-clip: text;
|
-webkit-background-clip: text;
|
||||||
@ -117,18 +124,24 @@ h1, h2, h3, h4, h5, h6 {
|
|||||||
background-clip: text;
|
background-clip: text;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.text-green-light {
|
||||||
|
color: var(--accent-color) !important;
|
||||||
|
}
|
||||||
|
|
||||||
.shadow-custom {
|
.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 */
|
/* Header Styles */
|
||||||
.navbar {
|
.navbar {
|
||||||
transition: var(--transition);
|
transition: var(--transition);
|
||||||
backdrop-filter: blur(10px);
|
backdrop-filter: blur(10px);
|
||||||
|
background-color: #ffffff !important;
|
||||||
|
border-bottom: 1px solid var(--light-green);
|
||||||
}
|
}
|
||||||
|
|
||||||
.navbar.scrolled {
|
.navbar.scrolled {
|
||||||
background: rgba(255, 255, 255, 0.95) !important;
|
background: rgba(255, 255, 255, 0.98) !important;
|
||||||
box-shadow: var(--box-shadow);
|
box-shadow: var(--box-shadow);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -141,6 +154,7 @@ h1, h2, h3, h4, h5, h6 {
|
|||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
transition: var(--transition);
|
transition: var(--transition);
|
||||||
position: relative;
|
position: relative;
|
||||||
|
color: #666 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-link:hover,
|
.nav-link:hover,
|
||||||
@ -165,6 +179,54 @@ h1, h2, h3, h4, h5, h6 {
|
|||||||
width: 100%;
|
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 */
|
||||||
.hero-section {
|
.hero-section {
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
@ -192,7 +254,7 @@ h1, h2, h3, h4, h5, h6 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.hero-features .fas {
|
.hero-features .fas {
|
||||||
color: #28a745;
|
color: var(--accent-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.scroll-indicator {
|
.scroll-indicator {
|
||||||
@ -230,7 +292,7 @@ h1, h2, h3, h4, h5, h6 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.conversion-section {
|
.conversion-section {
|
||||||
background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
|
background: linear-gradient(135deg, var(--light-green) 0%, #f8f9fa 100%);
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-control,
|
.form-control,
|
||||||
@ -276,14 +338,15 @@ h1, h2, h3, h4, h5, h6 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.btn-primary {
|
.btn-primary {
|
||||||
background: linear-gradient(135deg, var(--primary-color), var(--secondary-color));
|
background: linear-gradient(135deg, var(--primary-color), var(--accent-color));
|
||||||
border: none;
|
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 {
|
.btn-primary:hover {
|
||||||
transform: translateY(-2px);
|
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 {
|
.btn-lg {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user