fix: converter para de maiúsculas para primeira maiúcula.

This commit is contained in:
Ricardo Carneiro 2025-05-31 15:25:04 -03:00
parent b1d75213ab
commit 97c2d32e03
18 changed files with 2180 additions and 529 deletions

View File

@ -6,7 +6,6 @@ using OnlyOneAccessTemplate.Models;
namespace OnlyOneAccessTemplate.Controllers
{
public abstract class BaseController : Controller
{
protected readonly ISiteConfigurationService _siteConfig;
@ -37,6 +36,7 @@ namespace OnlyOneAccessTemplate.Controllers
SetupSeoViewBag(config, language, currentUrl);
SetupContentViewBag(config, language);
SetupConverterViewBag(language); // Nova configuração específica do conversor
base.OnActionExecuting(context);
}
@ -118,6 +118,246 @@ namespace OnlyOneAccessTemplate.Controllers
ViewBag.ConversionConfig = config.Conversion;
}
// Nova função para configurar textos específicos do conversor
protected void SetupConverterViewBag(string language)
{
var converterType = _configuration.GetValue<string>("Converter:Type") ?? "generic";
var converterName = _configuration.GetValue<string>("Converter:Name") ?? "Conversor Online";
switch (language)
{
case "en":
SetupEnglishConverterContent(converterType, converterName);
break;
case "es":
SetupSpanishConverterContent(converterType, converterName);
break;
default:
SetupPortugueseConverterContent(converterType, converterName);
break;
}
}
protected virtual void SetupPortugueseConverterContent(string converterType, string converterName)
{
// Configurações específicas por tipo de conversor
switch (converterType)
{
case "text-case":
ViewBag.ConverterTitle = "CONVERSOR DE MAIÚSCULAS E MINÚSCULAS";
ViewBag.ConverterDescription = "Converta texto entre maiúsculas, minúsculas, título e primeira maiúscula";
ViewBag.Step1Title = "Cole o Texto";
ViewBag.Step1Description = "Digite ou cole o texto que deseja converter";
ViewBag.Step2Title = "Escolha o Formato";
ViewBag.Step2Description = "Selecione o tipo de conversão desejada";
ViewBag.Step3Title = "Copie o Resultado";
ViewBag.Step3Description = "Copie o texto convertido";
ViewBag.InputLabel = "Texto Original";
ViewBag.OutputLabel = "Texto Convertido";
ViewBag.InputPlaceholder = "Digite ou cole seu texto aqui...";
ViewBag.OutputPlaceholder = "O texto convertido aparecerá aqui...";
ViewBag.ConvertButtonText = "Converter Texto";
ViewBag.ConverterType = "text";
ViewBag.OutputType = "text";
ViewBag.HasAdvancedOptions = true;
break;
case "csv-json":
ViewBag.ConverterTitle = "CONVERSOR CSV PARA JSON";
ViewBag.ConverterDescription = "Converta arquivos CSV para formato JSON de forma rápida e fácil";
ViewBag.Step1Title = "Upload CSV";
ViewBag.Step1Description = "Selecione um arquivo CSV ou cole o conteúdo";
ViewBag.Step2Title = "Processar";
ViewBag.Step2Description = "Aguarde a conversão automática";
ViewBag.Step3Title = "Baixar JSON";
ViewBag.Step3Description = "Baixe ou copie o resultado";
ViewBag.InputLabel = "Arquivo CSV";
ViewBag.OutputLabel = "Resultado JSON";
ViewBag.InputPlaceholder = "Cole o conteúdo CSV aqui ou faça upload de um arquivo...";
ViewBag.OutputPlaceholder = "O JSON convertido aparecerá aqui...";
ViewBag.ConvertButtonText = "Converter para JSON";
ViewBag.ConverterType = "text"; // Aceita texto e arquivo
ViewBag.OutputType = "text";
ViewBag.AcceptedFileTypes = ".csv,.txt";
ViewBag.FileHelpText = "Tamanho máximo: 10MB. Formatos: CSV, TXT";
break;
case "image-ocr":
ViewBag.ConverterTitle = "CONVERSOR DE IMAGEM PARA TEXTO - OCR ONLINE";
ViewBag.ConverterDescription = "Extraia texto de imagens usando tecnologia de Reconhecimento Óptico de Caracteres";
ViewBag.Step1Title = "Upload Imagem";
ViewBag.Step1Description = "Selecione uma imagem com texto";
ViewBag.Step2Title = "Processar OCR";
ViewBag.Step2Description = "Aguarde a extração do texto";
ViewBag.Step3Title = "Copiar Texto";
ViewBag.Step3Description = "Copie ou baixe o texto extraído";
ViewBag.InputLabel = "Imagem";
ViewBag.OutputLabel = "Texto Extraído";
ViewBag.SelectFileText = "SELECIONAR IMAGEM";
ViewBag.ConvertButtonText = "Extrair Texto";
ViewBag.ConverterType = "file";
ViewBag.OutputType = "text";
ViewBag.AcceptedFileTypes = ".jpg,.jpeg,.png,.gif,.bmp,.pdf";
ViewBag.FileHelpText = "Tamanho máximo: 15MB. Formatos: JPG, PNG, GIF, BMP, PDF";
ViewBag.HasAdvancedOptions = true;
break;
default:
// Configuração genérica
ViewBag.ConverterTitle = converterName.ToUpper();
ViewBag.ConverterDescription = "Converta seus arquivos de forma rápida e segura";
ViewBag.Step1Title = "Entrada";
ViewBag.Step1Description = "Selecione ou cole seu conteúdo";
ViewBag.Step2Title = "Processar";
ViewBag.Step2Description = "Aguarde o processamento";
ViewBag.Step3Title = "Resultado";
ViewBag.Step3Description = "Baixe ou copie o resultado";
ViewBag.ConvertButtonText = "Converter";
ViewBag.ConverterType = "text";
ViewBag.OutputType = "text";
break;
}
// Textos comuns do conversor
ViewBag.LoadingText = "Processando...";
ViewBag.ProcessingText = "Convertendo seu arquivo...";
ViewBag.CopyButtonText = "Copiar";
ViewBag.ClearButtonText = "Limpar";
ViewBag.ShareButtonText = "Compartilhar";
ViewBag.CopiedText = "Copiado!";
ViewBag.DownloadButtonText = "Baixar";
ViewBag.DownloadReadyText = "Seu arquivo está pronto!";
ViewBag.SecurityText = "Seus dados estão seguros conosco";
ViewBag.DefaultErrorMessage = "Erro ao processar. Tente novamente.";
ViewBag.AdvancedOptionsText = "Opções Avançadas";
ViewBag.PreviewPlaceholder = "O resultado aparecerá aqui...";
// Configurações de benefícios
ViewBag.BenefitsTitle = "Por Que Usar Nossa Ferramenta?";
ViewBag.BenefitsSubtitle = "Descubra os benefícios de nossa solução";
ViewBag.FinalCtaTitle = "Pronto para Converter?";
ViewBag.FinalCtaSubtitle = "Use nossa ferramenta gratuita agora mesmo";
ViewBag.FinalCtaButtonText = "Começar Conversão";
// Features padrão se não especificadas
if (ViewBag.Feature1Title == null)
{
ViewBag.Feature1Title = "Rápido e Fácil";
ViewBag.Feature1Description = "Conversão instantânea sem complicações";
ViewBag.Feature2Title = "Seguro";
ViewBag.Feature2Description = "Seus dados são processados com segurança";
ViewBag.Feature3Title = "Gratuito";
ViewBag.Feature3Description = "Use nossa ferramenta sem custos";
}
}
protected virtual void SetupEnglishConverterContent(string converterType, string converterName)
{
switch (converterType)
{
case "text-case":
ViewBag.ConverterTitle = "TEXT CASE CONVERTER";
ViewBag.ConverterDescription = "Convert text between uppercase, lowercase, title case and sentence case";
ViewBag.Step1Title = "Paste Text";
ViewBag.Step1Description = "Type or paste the text you want to convert";
ViewBag.Step2Title = "Choose Format";
ViewBag.Step2Description = "Select the desired conversion type";
ViewBag.Step3Title = "Copy Result";
ViewBag.Step3Description = "Copy the converted text";
ViewBag.InputLabel = "Original Text";
ViewBag.OutputLabel = "Converted Text";
ViewBag.InputPlaceholder = "Type or paste your text here...";
ViewBag.OutputPlaceholder = "Converted text will appear here...";
ViewBag.ConvertButtonText = "Convert Text";
break;
case "csv-json":
ViewBag.ConverterTitle = "CSV TO JSON CONVERTER";
ViewBag.ConverterDescription = "Convert CSV files to JSON format quickly and easily";
ViewBag.Step1Title = "Upload CSV";
ViewBag.Step1Description = "Select a CSV file or paste content";
ViewBag.Step2Title = "Process";
ViewBag.Step2Description = "Wait for automatic conversion";
ViewBag.Step3Title = "Download JSON";
ViewBag.Step3Description = "Download or copy the result";
ViewBag.ConvertButtonText = "Convert to JSON";
break;
case "image-ocr":
ViewBag.ConverterTitle = "IMAGE TO TEXT CONVERTER - OCR ONLINE";
ViewBag.ConverterDescription = "Extract text from images using Optical Character Recognition technology";
ViewBag.Step1Title = "Upload Image";
ViewBag.Step1Description = "Select an image with text";
ViewBag.Step2Title = "Process OCR";
ViewBag.Step2Description = "Wait for text extraction";
ViewBag.Step3Title = "Copy Text";
ViewBag.Step3Description = "Copy or download extracted text";
ViewBag.SelectFileText = "SELECT IMAGE";
ViewBag.ConvertButtonText = "Extract Text";
break;
default:
ViewBag.ConverterTitle = converterName.ToUpper();
ViewBag.ConverterDescription = "Convert your files quickly and securely";
ViewBag.ConvertButtonText = "Convert";
break;
}
// Common English texts
ViewBag.LoadingText = "Processing...";
ViewBag.ProcessingText = "Converting your file...";
ViewBag.CopyButtonText = "Copy";
ViewBag.ClearButtonText = "Clear";
ViewBag.ShareButtonText = "Share";
ViewBag.CopiedText = "Copied!";
ViewBag.DownloadButtonText = "Download";
ViewBag.SecurityText = "Your data is safe with us";
ViewBag.BenefitsTitle = "Why Use Our Tool?";
ViewBag.FinalCtaTitle = "Ready to Convert?";
}
protected virtual void SetupSpanishConverterContent(string converterType, string converterName)
{
switch (converterType)
{
case "text-case":
ViewBag.ConverterTitle = "CONVERTIDOR DE MAYÚSCULAS Y MINÚSCULAS";
ViewBag.ConverterDescription = "Convierte texto entre mayúsculas, minúsculas, título y oración";
ViewBag.ConvertButtonText = "Convertir Texto";
break;
case "csv-json":
ViewBag.ConverterTitle = "CONVERTIDOR CSV A JSON";
ViewBag.ConverterDescription = "Convierte archivos CSV a formato JSON de forma rápida y fácil";
ViewBag.ConvertButtonText = "Convertir a JSON";
break;
case "image-ocr":
ViewBag.ConverterTitle = "CONVERTIDOR DE IMAGEN A TEXTO - OCR EN LÍNEA";
ViewBag.ConverterDescription = "Extrae texto de imágenes usando tecnología de Reconocimiento Óptico de Caracteres";
ViewBag.SelectFileText = "SELECCIONAR IMAGEN";
ViewBag.ConvertButtonText = "Extraer Texto";
break;
default:
ViewBag.ConverterTitle = converterName.ToUpper();
ViewBag.ConverterDescription = "Convierte tus archivos de forma rápida y segura";
ViewBag.ConvertButtonText = "Convertir";
break;
}
// Common Spanish texts
ViewBag.LoadingText = "Procesando...";
ViewBag.ProcessingText = "Convirtiendo tu archivo...";
ViewBag.CopyButtonText = "Copiar";
ViewBag.ClearButtonText = "Limpiar";
ViewBag.CopiedText = "¡Copiado!";
ViewBag.SecurityText = "Tus datos están seguros con nosotros";
ViewBag.BenefitsTitle = "¿Por Qué Usar Nuestra Herramienta?";
ViewBag.FinalCtaTitle = "¿Listo para Convertir?";
}
// Resto dos métodos permanece igual...
protected void SetupPortugueseContent()
{
// Menu
@ -127,104 +367,18 @@ namespace OnlyOneAccessTemplate.Controllers
ViewBag.MenuPrivacy = "Privacidade";
ViewBag.MenuTerms = "Termos";
// Hero Section
ViewBag.DefaultHeroTitle = "Transforme Seu Negócio com Nossa Solução";
ViewBag.DefaultHeroSubtitle = "Aumente suas vendas em até 300% com nossa metodologia comprovada e suporte especializado.";
ViewBag.DefaultCtaText = "Comece Agora";
ViewBag.WatchVideoText = "Assistir Vídeo";
// Features
ViewBag.DefaultFeaturesTitle = "Por Que Escolher Nossa Solução?";
ViewBag.DefaultFeaturesSubtitle = "Descubra os benefícios que já transformaram mais de 1000 empresas";
ViewBag.Feature1Title = "Resultados Rápidos";
ViewBag.Feature1Description = "Veja os primeiros resultados em até 30 dias";
ViewBag.Feature2Title = "Suporte 24/7";
ViewBag.Feature2Description = "Equipe especializada sempre disponível";
ViewBag.Feature3Title = "Metodologia Comprovada";
ViewBag.Feature3Description = "Mais de 1000 casos de sucesso documentados";
// Form
ViewBag.FormTitle = "Solicite uma Demonstração Gratuita";
ViewBag.FormSubtitle = "Preencha o formulário e nossa equipe entrará em contato";
ViewBag.NameLabel = "Nome Completo";
ViewBag.NamePlaceholder = "Digite seu nome completo";
ViewBag.EmailLabel = "E-mail";
ViewBag.EmailPlaceholder = "Digite seu e-mail";
ViewBag.PhoneLabel = "Telefone";
ViewBag.PhonePlaceholder = "Digite seu telefone";
ViewBag.SubmitButtonText = "Solicitar Demonstração";
ViewBag.LoadingText = "Enviando...";
// Quick Form
ViewBag.QuickFormTitle = "Acesso Rápido";
ViewBag.QuickSubmitText = "Começar Agora";
ViewBag.QuickConsentText = "Concordo em receber informações por e-mail";
// Validation
ViewBag.RequiredFieldMessage = "Este campo é obrigatório";
ViewBag.EmailValidationMessage = "Digite um e-mail válido";
ViewBag.PhoneValidationMessage = "Digite um telefone válido";
ViewBag.ConsentValidationMessage = "Você deve concordar para continuar";
// Consent
ViewBag.ConsentText = "Concordo em receber comunicações e com a <a href='/privacy'>Política de Privacidade</a>";
// Benefits
ViewBag.BenefitsTitle = "O que você vai receber:";
ViewBag.DefaultBenefit1 = "Consultoria personalizada";
ViewBag.DefaultBenefit2 = "Suporte técnico especializado";
ViewBag.DefaultBenefit3 = "Garantia de resultados";
// Testimonials
ViewBag.DefaultTestimonialsTitle = "O Que Nossos Clientes Dizem";
ViewBag.DefaultTestimonialsSubtitle = "Mais de 1000 empresas já transformaram seus resultados";
SetupPortugueseTestimonials();
// CTA
ViewBag.DefaultCtaTitle = "Pronto para Transformar Seu Negócio?";
ViewBag.DefaultCtaSubtitle = "Junte-se a mais de 1000 empresas que já alcançaram resultados extraordinários";
ViewBag.DefaultCtaButtonText = "Começar Transformação";
// Footer
ViewBag.FooterDescription = "Transformando negócios através de soluções inovadoras";
ViewBag.FooterDescription = "Transformando dados através de soluções inovadoras";
ViewBag.FooterMenuTitle = "Links";
ViewBag.FooterLegalTitle = "Legal";
ViewBag.FooterContactTitle = "Contato";
ViewBag.FooterCopyright = "Todos os direitos reservados.";
ViewBag.FooterMadeWith = "Feito com ❤️ no Brasil";
// Success
ViewBag.SuccessTitle = "Obrigado!";
ViewBag.SuccessMessage = "Recebemos sua solicitação. Nossa equipe entrará em contato em breve.";
ViewBag.CloseButtonText = "Fechar";
// Thank You Page
ViewBag.NextStepsTitle = "O que acontece agora?";
ViewBag.Step1Title = "Análise";
ViewBag.Step1Description = "Nossa equipe analisará sua solicitação";
ViewBag.Step2Title = "Contato";
ViewBag.Step2Description = "Entraremos em contato em até 24h";
ViewBag.Step3Title = "Implementação";
ViewBag.Step3Description = "Iniciaremos sua transformação";
ViewBag.ContactInfoTitle = "Precisa falar conosco?";
ViewBag.BackToHomeText = "Voltar ao Início";
ViewBag.WhatsAppText = "Falar no WhatsApp";
ViewBag.RedirectConfirmText = "Deseja voltar à página inicial?";
// Security
ViewBag.SecurityText = "Seus dados estão seguros conosco";
// Time
ViewBag.DaysText = "Dias";
ViewBag.HoursText = "Horas";
ViewBag.MinutesText = "Min";
ViewBag.SecondsText = "Seg";
// Contact info
ViewBag.ContactEmail = "contato@seusite.com";
ViewBag.ContactPhone = "(11) 99999-9999";
ViewBag.ContactAddress = "São Paulo, SP";
ViewBag.TestimonialsCount = "Mais de 500 avaliações 5 estrelas";
}
protected void SetupEnglishContent()
@ -236,104 +390,18 @@ namespace OnlyOneAccessTemplate.Controllers
ViewBag.MenuPrivacy = "Privacy";
ViewBag.MenuTerms = "Terms";
// Hero Section
ViewBag.DefaultHeroTitle = "Transform Your Business with Our Solution";
ViewBag.DefaultHeroSubtitle = "Increase your sales by up to 300% with our proven methodology and expert support.";
ViewBag.DefaultCtaText = "Get Started";
ViewBag.WatchVideoText = "Watch Video";
// Features
ViewBag.DefaultFeaturesTitle = "Why Choose Our Solution?";
ViewBag.DefaultFeaturesSubtitle = "Discover the benefits that have already transformed over 1000 companies";
ViewBag.Feature1Title = "Fast Results";
ViewBag.Feature1Description = "See first results within 30 days";
ViewBag.Feature2Title = "24/7 Support";
ViewBag.Feature2Description = "Specialized team always available";
ViewBag.Feature3Title = "Proven Method";
ViewBag.Feature3Description = "Over 1000 documented success cases";
// Form
ViewBag.FormTitle = "Request a Free Demo";
ViewBag.FormSubtitle = "Fill out the form and our team will contact you";
ViewBag.NameLabel = "Full Name";
ViewBag.NamePlaceholder = "Enter your full name";
ViewBag.EmailLabel = "Email";
ViewBag.EmailPlaceholder = "Enter your email";
ViewBag.PhoneLabel = "Phone";
ViewBag.PhonePlaceholder = "Enter your phone";
ViewBag.SubmitButtonText = "Request Demo";
ViewBag.LoadingText = "Sending...";
// Quick Form
ViewBag.QuickFormTitle = "Quick Access";
ViewBag.QuickSubmitText = "Start Now";
ViewBag.QuickConsentText = "I agree to receive information by email";
// Validation
ViewBag.RequiredFieldMessage = "This field is required";
ViewBag.EmailValidationMessage = "Enter a valid email";
ViewBag.PhoneValidationMessage = "Enter a valid phone";
ViewBag.ConsentValidationMessage = "You must agree to continue";
// Consent
ViewBag.ConsentText = "I agree to receive communications and with the <a href='/en/privacy'>Privacy Policy</a>";
// Benefits
ViewBag.BenefitsTitle = "What you'll receive:";
ViewBag.DefaultBenefit1 = "Personalized consultation";
ViewBag.DefaultBenefit2 = "Specialized technical support";
ViewBag.DefaultBenefit3 = "Results guarantee";
// Testimonials
ViewBag.DefaultTestimonialsTitle = "What Our Clients Say";
ViewBag.DefaultTestimonialsSubtitle = "Over 1000 companies have already transformed their results";
SetupEnglishTestimonials();
// CTA
ViewBag.DefaultCtaTitle = "Ready to Transform Your Business?";
ViewBag.DefaultCtaSubtitle = "Join over 1000 companies that have already achieved extraordinary results";
ViewBag.DefaultCtaButtonText = "Start Transformation";
// Footer
ViewBag.FooterDescription = "Transforming businesses through innovative solutions";
ViewBag.FooterDescription = "Transforming data through innovative solutions";
ViewBag.FooterMenuTitle = "Links";
ViewBag.FooterLegalTitle = "Legal";
ViewBag.FooterContactTitle = "Contact";
ViewBag.FooterCopyright = "All rights reserved.";
ViewBag.FooterMadeWith = "Made with ❤️ in Brazil";
// Success
ViewBag.SuccessTitle = "Thank You!";
ViewBag.SuccessMessage = "We received your request. Our team will contact you soon.";
ViewBag.CloseButtonText = "Close";
// Thank You Page
ViewBag.NextStepsTitle = "What happens next?";
ViewBag.Step1Title = "Analysis";
ViewBag.Step1Description = "Our team will analyze your request";
ViewBag.Step2Title = "Contact";
ViewBag.Step2Description = "We'll contact you within 24h";
ViewBag.Step3Title = "Implementation";
ViewBag.Step3Description = "We'll start your transformation";
ViewBag.ContactInfoTitle = "Need to talk to us?";
ViewBag.BackToHomeText = "Back to Home";
ViewBag.WhatsAppText = "Chat on WhatsApp";
ViewBag.RedirectConfirmText = "Would you like to return to the home page?";
// Security
ViewBag.SecurityText = "Your data is safe with us";
// Time
ViewBag.DaysText = "Days";
ViewBag.HoursText = "Hours";
ViewBag.MinutesText = "Min";
ViewBag.SecondsText = "Sec";
// Contact info
ViewBag.ContactEmail = "contact@yoursite.com";
ViewBag.ContactPhone = "+1 (555) 123-4567";
ViewBag.ContactAddress = "New York, NY";
ViewBag.TestimonialsCount = "Over 500 five-star reviews";
}
protected void SetupSpanishContent()
@ -345,149 +413,18 @@ namespace OnlyOneAccessTemplate.Controllers
ViewBag.MenuPrivacy = "Privacidad";
ViewBag.MenuTerms = "Términos";
// Hero Section
ViewBag.DefaultHeroTitle = "Transforma Tu Negocio con Nuestra Solución";
ViewBag.DefaultHeroSubtitle = "Aumenta tus ventas hasta un 300% con nuestra metodología probada y soporte especializado.";
ViewBag.DefaultCtaText = "Comenzar Ahora";
ViewBag.WatchVideoText = "Ver Video";
// Features
ViewBag.DefaultFeaturesTitle = "¿Por Qué Elegir Nuestra Solución?";
ViewBag.DefaultFeaturesSubtitle = "Descubre los beneficios que ya han transformado más de 1000 empresas";
ViewBag.Feature1Title = "Resultados Rápidos";
ViewBag.Feature1Description = "Ve los primeros resultados en 30 días";
ViewBag.Feature2Title = "Soporte 24/7";
ViewBag.Feature2Description = "Equipo especializado siempre disponible";
ViewBag.Feature3Title = "Metodología Probada";
ViewBag.Feature3Description = "Más de 1000 casos de éxito documentados";
// Form
ViewBag.FormTitle = "Solicita una Demo Gratuita";
ViewBag.FormSubtitle = "Completa el formulario y nuestro equipo te contactará";
ViewBag.NameLabel = "Nombre Completo";
ViewBag.NamePlaceholder = "Ingresa tu nombre completo";
ViewBag.EmailLabel = "Correo";
ViewBag.EmailPlaceholder = "Ingresa tu correo";
ViewBag.PhoneLabel = "Teléfono";
ViewBag.PhonePlaceholder = "Ingresa tu teléfono";
ViewBag.SubmitButtonText = "Solicitar Demo";
ViewBag.LoadingText = "Enviando...";
// Quick Form
ViewBag.QuickFormTitle = "Acceso Rápido";
ViewBag.QuickSubmitText = "Comenzar Ahora";
ViewBag.QuickConsentText = "Acepto recibir información por correo";
// Validation
ViewBag.RequiredFieldMessage = "Este campo es obligatorio";
ViewBag.EmailValidationMessage = "Ingresa un correo válido";
ViewBag.PhoneValidationMessage = "Ingresa un teléfono válido";
ViewBag.ConsentValidationMessage = "Debes aceptar para continuar";
// Consent
ViewBag.ConsentText = "Acepto recibir comunicaciones y la <a href='/es/privacy'>Política de Privacidad</a>";
// Benefits
ViewBag.BenefitsTitle = "Lo que recibirás:";
ViewBag.DefaultBenefit1 = "Consultoría personalizada";
ViewBag.DefaultBenefit2 = "Soporte técnico especializado";
ViewBag.DefaultBenefit3 = "Garantía de resultados";
// Testimonials
ViewBag.DefaultTestimonialsTitle = "Lo Que Dicen Nuestros Clientes";
ViewBag.DefaultTestimonialsSubtitle = "Más de 1000 empresas ya han transformado sus resultados";
SetupSpanishTestimonials();
// CTA
ViewBag.DefaultCtaTitle = "¿Listo para Transformar Tu Negocio?";
ViewBag.DefaultCtaSubtitle = "Únete a más de 1000 empresas que ya han logrado resultados extraordinarios";
ViewBag.DefaultCtaButtonText = "Comenzar Transformación";
// Footer
ViewBag.FooterDescription = "Transformando negocios a través de soluciones innovadoras";
ViewBag.FooterDescription = "Transformando datos a través de soluciones innovadoras";
ViewBag.FooterMenuTitle = "Enlaces";
ViewBag.FooterLegalTitle = "Legal";
ViewBag.FooterContactTitle = "Contacto";
ViewBag.FooterCopyright = "Todos los derechos reservados.";
ViewBag.FooterMadeWith = "Hecho con ❤️ en Brasil";
// Success
ViewBag.SuccessTitle = "¡Gracias!";
ViewBag.SuccessMessage = "Recibimos tu solicitud. Nuestro equipo te contactará pronto.";
ViewBag.CloseButtonText = "Cerrar";
// Thank You Page
ViewBag.NextStepsTitle = "¿Qué pasa ahora?";
ViewBag.Step1Title = "Análisis";
ViewBag.Step1Description = "Nuestro equipo analizará tu solicitud";
ViewBag.Step2Title = "Contacto";
ViewBag.Step2Description = "Te contactaremos en 24h";
ViewBag.Step3Title = "Implementación";
ViewBag.Step3Description = "Comenzaremos tu transformación";
ViewBag.ContactInfoTitle = "¿Necesitas hablar con nosotros?";
ViewBag.BackToHomeText = "Volver al Inicio";
ViewBag.WhatsAppText = "Hablar por WhatsApp";
ViewBag.RedirectConfirmText = "¿Te gustaría volver a la página principal?";
// Security
ViewBag.SecurityText = "Tus datos están seguros con nosotros";
// Time
ViewBag.DaysText = "Días";
ViewBag.HoursText = "Horas";
ViewBag.MinutesText = "Min";
ViewBag.SecondsText = "Seg";
// Contact info
ViewBag.ContactEmail = "contacto@tusitioweb.com";
ViewBag.ContactPhone = "+34 123 456 789";
ViewBag.ContactAddress = "Madrid, España";
ViewBag.TestimonialsCount = "Más de 500 reseñas de 5 estrellas";
}
protected void SetupPortugueseTestimonials()
{
ViewBag.Testimonial1Quote = "Aumentamos nossas vendas em 250% em apenas 3 meses. Incrível!";
ViewBag.Testimonial1Name = "Maria Silva";
ViewBag.Testimonial1Position = "CEO, TechStart";
ViewBag.Testimonial2Quote = "O suporte é excepcional. Sempre prontos a ajudar quando precisamos.";
ViewBag.Testimonial2Name = "João Santos";
ViewBag.Testimonial2Position = "Diretor, InovaCorp";
ViewBag.Testimonial3Quote = "A metodologia realmente funciona. Resultados visíveis desde o primeiro mês.";
ViewBag.Testimonial3Name = "Ana Costa";
ViewBag.Testimonial3Position = "Gerente, GrowBiz";
}
protected void SetupEnglishTestimonials()
{
ViewBag.Testimonial1Quote = "We increased our sales by 250% in just 3 months. Amazing!";
ViewBag.Testimonial1Name = "Mary Johnson";
ViewBag.Testimonial1Position = "CEO, TechStart";
ViewBag.Testimonial2Quote = "The support is exceptional. Always ready to help when we need it.";
ViewBag.Testimonial2Name = "John Smith";
ViewBag.Testimonial2Position = "Director, InovaCorp";
ViewBag.Testimonial3Quote = "The methodology really works. Visible results from the first month.";
ViewBag.Testimonial3Name = "Sarah Davis";
ViewBag.Testimonial3Position = "Manager, GrowBiz";
}
protected void SetupSpanishTestimonials()
{
ViewBag.Testimonial1Quote = "Aumentamos nuestras ventas un 250% en solo 3 meses. ¡Increíble!";
ViewBag.Testimonial1Name = "María García";
ViewBag.Testimonial1Position = "CEO, TechStart";
ViewBag.Testimonial2Quote = "El soporte es excepcional. Siempre listos para ayudar cuando lo necesitamos.";
ViewBag.Testimonial2Name = "Carlos López";
ViewBag.Testimonial2Position = "Director, InovaCorp";
ViewBag.Testimonial3Quote = "La metodología realmente funciona. Resultados visibles desde el primer mes.";
ViewBag.Testimonial3Name = "Ana Rodríguez";
ViewBag.Testimonial3Position = "Gerente, GrowBiz";
}
protected void SetupHreflangUrls(string currentUrl, string currentLanguage)

View File

@ -0,0 +1,129 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Caching.Memory;
using OnlyOneAccessTemplate.Services.OnlyOneAccessTemplate.Services;
using OnlyOneAccessTemplate.Services;
using OnlyOneAccessTemplate.Models;
namespace OnlyOneAccessTemplate.Controllers
{
public class ConverterController : BaseController
{
private readonly IServiceProvider _serviceProvider;
private readonly ILogger _logger;
private readonly Dictionary<string, Type> _converterTypes;
public ConverterController(
ISiteConfigurationService siteConfig,
ILanguageService languageService,
ISeoService seoService,
IMemoryCache cache,
IConfiguration configuration,
IServiceProvider serviceProvider,
ILogger logger
)
: base(siteConfig, languageService, seoService, cache, configuration)
{
_serviceProvider = serviceProvider;
this._logger = logger;
// Registrar tipos de conversores disponíveis
_converterTypes = new Dictionary<string, Type>
{
["text-case"] = typeof(TextCaseConverterService),
["csv-json"] = typeof(CsvToJsonConverterService),
["image-ocr"] = typeof(ImageToTextConverterService)
// Adicionar novos conversores aqui
};
}
[HttpPost("api/convert/{converterType}")]
public async Task<IActionResult> Convert(string converterType, [FromForm] ConversionRequestDto requestDto)
{
try
{
if (!_converterTypes.ContainsKey(converterType))
{
return BadRequest(new { success = false, message = "Conversor não encontrado" });
}
var converterService = (IConverterService)_serviceProvider.GetRequiredService(_converterTypes[converterType]);
var request = new ConversionRequest(
Language: requestDto.Language ?? GetCurrentLanguage(),
InputType: requestDto.InputType,
TextInput: requestDto.TextInput,
FileInput: requestDto.FileInput,
UrlInput: requestDto.UrlInput,
Options: requestDto.Options
);
// Validar entrada
if (!await converterService.ValidateInputAsync(request))
{
return BadRequest(new { success = false, message = "Entrada inválida" });
}
// Executar conversão
var result = await converterService.ConvertAsync(request);
if (!result.Success)
{
return BadRequest(new { success = false, message = result.ErrorMessage });
}
// Retornar resultado baseado no tipo de saída
if (!string.IsNullOrEmpty(result.OutputText))
{
return Ok(new
{
success = true,
outputText = result.OutputText,
metadata = result.Metadata
});
}
else if (result.OutputFile != null)
{
return File(result.OutputFile, result.OutputMimeType ?? "application/octet-stream",
result.OutputFileName ?? "converted_file");
}
else
{
return Ok(new
{
success = true,
previewHtml = result.PreviewHtml,
metadata = result.Metadata
});
}
}
catch (Exception ex)
{
_logger.LogError(ex, "Erro na conversão {ConverterType}", converterType);
return StatusCode(500, new { success = false, message = "Erro interno do servidor" });
}
}
[HttpGet("config/{converterType}")]
public IActionResult GetConverterConfig(string converterType)
{
try
{
if (!_converterTypes.ContainsKey(converterType))
{
return NotFound(new { success = false, message = "Conversor não encontrado" });
}
var converterService = (IConverterService)_serviceProvider.GetRequiredService(_converterTypes[converterType]);
var language = GetCurrentLanguage();
var config = converterService.GetConfiguration(language);
return Ok(new { success = true, config });
}
catch (Exception ex)
{
_logger.LogError(ex, "Erro ao buscar configuração do conversor {ConverterType}", converterType);
return StatusCode(500, new { success = false, message = "Erro interno do servidor" });
}
}
}
}

View File

@ -0,0 +1,12 @@
namespace OnlyOneAccessTemplate.Models
{
public class ConversionRequestDto
{
public string? Language { get; set; }
public string InputType { get; set; } = "text";
public string? TextInput { get; set; }
public IFormFile? FileInput { get; set; }
public string? UrlInput { get; set; }
public Dictionary<string, object>? Options { get; set; }
}
}

View File

@ -9,7 +9,6 @@
public bool Required { get; init; } = false;
public string ValidationRegex { get; init; } = string.Empty;
public int Order { get; init; } = 0;
public Dictionary<string, List<string>> Properties { get; set; }
public Dictionary<string, object> Properties { get; init; } = new();
}
}

View File

@ -6,6 +6,10 @@
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<Content Remove="Views\Shared\_AdvancedOptions.cshtml" />
</ItemGroup>
<ItemGroup>
<Folder Include="Views\En\" />
<Folder Include="Views\Es\" />
@ -15,4 +19,16 @@
<PackageReference Include="MongoDB.Driver" Version="3.4.0" />
</ItemGroup>
<ItemGroup>
<UpToDateCheckInput Remove="Views\Shared\_AdvancedOptions.cshtml" />
</ItemGroup>
<ItemGroup>
<_ContentIncludedByDefault Remove="Views\Shared\_AdvancedOptions.cshtml" />
</ItemGroup>
<ItemGroup>
<None Include="Views\Shared\_AdvancedOptions.cshtml" />
</ItemGroup>
</Project>

View File

@ -35,8 +35,17 @@ builder.Services.AddScoped<ILanguageService, LanguageService>();
builder.Services.AddScoped<ISeoService, SeoService>();
builder.Services.AddScoped<IConversionService, ConversionService>();
// Converter Services - Registrar todos os conversores disponíveis
builder.Services.AddScoped<TextCaseConverterService>();
builder.Services.AddScoped<CsvToJsonConverterService>();
builder.Services.AddScoped<ImageToTextConverterService>();
// Adicione aqui novos conversores conforme necessário:
// builder.Services.AddScoped<SeuNovoConverterService>();
// HttpClient for external calls
builder.Services.AddHttpClient<ConversionService>();
builder.Services.AddHttpClient<ImageToTextConverterService>();
// Response Compression
builder.Services.AddResponseCompression(options =>
@ -50,6 +59,11 @@ builder.Services.AddResponseCaching();
// Memory Cache
builder.Services.AddMemoryCache();
// Logging
builder.Logging.ClearProviders();
builder.Logging.AddConsole();
builder.Logging.AddDebug();
var app = builder.Build();
// Configure the HTTP request pipeline
@ -72,6 +86,16 @@ app.UseResponseCaching();
app.UseRouting();
// Custom routing for multilingual support
app.MapControllerRoute(
name: "converter-api",
pattern: "converter/api/{action}/{converterType?}",
defaults: new { controller = "Converter" });
app.MapControllerRoute(
name: "converter-config",
pattern: "converter/config/{converterType}",
defaults: new { controller = "Converter", action = "GetConverterConfig" });
app.MapControllerRoute(
name: "default-pt",
pattern: "{controller=Home}/{action=Index}/{id?}");
@ -101,4 +125,34 @@ app.MapGet("/robots.txt", async (ISiteConfigurationService siteConfig) =>
return Results.Content(robots, "text/plain");
});
// Health check endpoint
app.MapGet("/health", () => Results.Ok(new { status = "healthy", timestamp = DateTime.UtcNow }));
// Initialize default data on startup (only in development)
if (app.Environment.IsDevelopment())
{
using var scope = app.Services.CreateScope();
var siteConfigService = scope.ServiceProvider.GetRequiredService<ISiteConfigurationService>();
var logger = scope.ServiceProvider.GetRequiredService<ILogger<Program>>();
try
{
// 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 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 padrão");
}
}
app.Run();

View File

@ -0,0 +1,206 @@
using global::OnlyOneAccessTemplate.Controllers;
using global::OnlyOneAccessTemplate.Services.OnlyOneAccessTemplate.Services;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Caching.Memory;
namespace OnlyOneAccessTemplate.Services
{
namespace OnlyOneAccessTemplate.Services
{
// Interface principal para conversores
public interface IConverterService
{
string ConverterType { get; }
string ConverterName { get; }
Task<ConversionResult> ConvertAsync(ConversionRequest request);
Task<bool> ValidateInputAsync(ConversionRequest request);
ConverterConfiguration GetConfiguration(string language);
}
// Configuração específica do conversor
public record ConverterConfiguration
{
public string ConverterType { get; init; } = "text"; // "text", "file", "url"
public string OutputType { get; init; } = "text"; // "text", "download", "preview"
public string[] AcceptedFileTypes { get; init; } = Array.Empty<string>();
public int MaxFileSize { get; init; } = 10 * 1024 * 1024; // 10MB
public bool HasAdvancedOptions { get; init; } = false;
public bool AllowShare { get; init; } = false;
public Dictionary<string, string> LocalizedTexts { get; init; } = new();
}
// Request de conversão
public record ConversionRequest(
string Language,
string InputType, // "text", "file", "url"
string? TextInput = null,
IFormFile? FileInput = null,
string? UrlInput = null,
Dictionary<string, object>? Options = null
);
// Resultado da conversão
public record ConversionResult(
bool Success,
string? OutputText = null,
byte[]? OutputFile = null,
string? OutputFileName = null,
string? OutputMimeType = null,
string? PreviewHtml = null,
string? ErrorMessage = null,
Dictionary<string, object>? Metadata = null
);
// Serviço base para conversores
public abstract class BaseConverterService : IConverterService
{
protected readonly ILogger _logger;
protected readonly IConfiguration _configuration;
public abstract string ConverterType { get; }
public abstract string ConverterName { get; }
protected BaseConverterService(ILogger logger, IConfiguration configuration)
{
_logger = logger;
_configuration = configuration;
}
public abstract Task<ConversionResult> ConvertAsync(ConversionRequest request);
public virtual async Task<bool> ValidateInputAsync(ConversionRequest request)
{
var config = GetConfiguration(request.Language);
// Validação básica baseada no tipo
switch (request.InputType)
{
case "text":
return !string.IsNullOrWhiteSpace(request.TextInput);
case "file":
if (request.FileInput == null || request.FileInput.Length == 0)
return false;
// Verificar tamanho do arquivo
if (request.FileInput.Length > config.MaxFileSize)
return false;
// Verificar tipo do arquivo se especificado
if (config.AcceptedFileTypes.Length > 0)
{
var extension = Path.GetExtension(request.FileInput.FileName);
return config.AcceptedFileTypes.Contains(extension, StringComparer.OrdinalIgnoreCase);
}
return true;
case "url":
return Uri.TryCreate(request.UrlInput, UriKind.Absolute, out _);
default:
return false;
}
}
public abstract ConverterConfiguration GetConfiguration(string language);
protected virtual Dictionary<string, string> GetLocalizedTexts(string language)
{
return language switch
{
"en" => GetEnglishTexts(),
"es" => GetSpanishTexts(),
_ => GetPortugueseTexts()
};
}
protected virtual Dictionary<string, string> GetPortugueseTexts()
{
return new Dictionary<string, string>
{
["ConverterTitle"] = "CONVERSOR ONLINE",
["ConverterDescription"] = "Converta seus arquivos de forma rápida e segura",
["Step1Title"] = "Upload",
["Step1Description"] = "Selecione ou cole seu conteúdo",
["Step2Title"] = "Configurar",
["Step2Description"] = "Escolha as opções de conversão",
["Step3Title"] = "Converter",
["Step3Description"] = "Baixe o resultado",
["InputLabel"] = "Entrada",
["OutputLabel"] = "Resultado",
["SelectFileText"] = "Selecionar Arquivo",
["ConvertButtonText"] = "Converter",
["LoadingText"] = "Processando...",
["ProcessingText"] = "Convertendo seu arquivo...",
["CopyButtonText"] = "Copiar",
["ClearButtonText"] = "Limpar",
["ShareButtonText"] = "Compartilhar",
["CopiedText"] = "Copiado!",
["DownloadButtonText"] = "Baixar",
["DownloadReadyText"] = "Seu arquivo está pronto!",
["SecurityText"] = "Seus dados estão seguros conosco",
["DefaultErrorMessage"] = "Erro ao processar. Tente novamente."
};
}
protected virtual Dictionary<string, string> GetEnglishTexts()
{
return new Dictionary<string, string>
{
["ConverterTitle"] = "ONLINE CONVERTER",
["ConverterDescription"] = "Convert your files quickly and securely",
["Step1Title"] = "Upload",
["Step1Description"] = "Select or paste your content",
["Step2Title"] = "Configure",
["Step2Description"] = "Choose conversion options",
["Step3Title"] = "Convert",
["Step3Description"] = "Download the result",
["InputLabel"] = "Input",
["OutputLabel"] = "Output",
["SelectFileText"] = "Select File",
["ConvertButtonText"] = "Convert",
["LoadingText"] = "Processing...",
["ProcessingText"] = "Converting your file...",
["CopyButtonText"] = "Copy",
["ClearButtonText"] = "Clear",
["ShareButtonText"] = "Share",
["CopiedText"] = "Copied!",
["DownloadButtonText"] = "Download",
["DownloadReadyText"] = "Your file is ready!",
["SecurityText"] = "Your data is safe with us",
["DefaultErrorMessage"] = "Processing error. Please try again."
};
}
protected virtual Dictionary<string, string> GetSpanishTexts()
{
return new Dictionary<string, string>
{
["ConverterTitle"] = "CONVERTIDOR EN LÍNEA",
["ConverterDescription"] = "Convierte tus archivos de forma rápida y segura",
["Step1Title"] = "Subir",
["Step1Description"] = "Selecciona o pega tu contenido",
["Step2Title"] = "Configurar",
["Step2Description"] = "Elige las opciones de conversión",
["Step3Title"] = "Convertir",
["Step3Description"] = "Descarga el resultado",
["InputLabel"] = "Entrada",
["OutputLabel"] = "Resultado",
["SelectFileText"] = "Seleccionar Archivo",
["ConvertButtonText"] = "Convertir",
["LoadingText"] = "Procesando...",
["ProcessingText"] = "Convirtiendo tu archivo...",
["CopyButtonText"] = "Copiar",
["ClearButtonText"] = "Limpiar",
["ShareButtonText"] = "Compartir",
["CopiedText"] = "¡Copiado!",
["DownloadButtonText"] = "Descargar",
["DownloadReadyText"] = "¡Tu archivo está listo!",
["SecurityText"] = "Tus datos están seguros con nosotros",
["DefaultErrorMessage"] = "Error al procesar. Inténtalo de nuevo."
};
}
}
}
}

View File

@ -0,0 +1,114 @@
using OnlyOneAccessTemplate.Services.OnlyOneAccessTemplate.Services;
namespace OnlyOneAccessTemplate.Services
{
public class CsvToJsonConverterService : BaseConverterService
{
public override string ConverterType => "csv-json";
public override string ConverterName => "CSV to JSON Converter";
public CsvToJsonConverterService(ILogger<CsvToJsonConverterService> logger, IConfiguration configuration)
: base(logger, configuration) { }
public override async Task<ConversionResult> ConvertAsync(ConversionRequest request)
{
try
{
string csvContent;
if (request.InputType == "file" && request.FileInput != null)
{
using var reader = new StreamReader(request.FileInput.OpenReadStream());
csvContent = await reader.ReadToEndAsync();
}
else if (request.InputType == "text" && !string.IsNullOrEmpty(request.TextInput))
{
csvContent = request.TextInput;
}
else
{
return new ConversionResult(false, ErrorMessage: "Nenhum conteúdo CSV fornecido");
}
var lines = csvContent.Split('\n', StringSplitOptions.RemoveEmptyEntries);
if (lines.Length < 2)
{
return new ConversionResult(false, ErrorMessage: "CSV deve ter pelo menos cabeçalho e uma linha de dados");
}
var headers = lines[0].Split(',').Select(h => h.Trim().Trim('"')).ToArray();
var jsonObjects = new List<Dictionary<string, string>>();
for (int i = 1; i < lines.Length; i++)
{
var values = lines[i].Split(',').Select(v => v.Trim().Trim('"')).ToArray();
var obj = new Dictionary<string, string>();
for (int j = 0; j < Math.Min(headers.Length, values.Length); j++)
{
obj[headers[j]] = values[j];
}
jsonObjects.Add(obj);
}
var jsonResult = System.Text.Json.JsonSerializer.Serialize(jsonObjects, new System.Text.Json.JsonSerializerOptions
{
WriteIndented = true,
Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping
});
return new ConversionResult(true, OutputText: jsonResult);
}
catch (Exception ex)
{
_logger.LogError(ex, "Erro na conversão CSV para JSON");
return new ConversionResult(false, ErrorMessage: "Erro ao converter CSV para JSON");
}
}
public override ConverterConfiguration GetConfiguration(string language)
{
var texts = GetLocalizedTexts(language);
texts["ConverterTitle"] = language switch
{
"en" => "CSV TO JSON CONVERTER",
"es" => "CONVERTIDOR CSV A JSON",
_ => "CONVERSOR CSV PARA JSON"
};
texts["ConverterDescription"] = language switch
{
"en" => "Convert CSV files to JSON format quickly and easily",
"es" => "Convierte archivos CSV a formato JSON de forma rápida y fácil",
_ => "Converta arquivos CSV para formato JSON de forma rápida e fácil"
};
texts["InputPlaceholder"] = language switch
{
"en" => "Paste your CSV content here or upload a file...",
"es" => "Pega tu contenido CSV aquí o sube un archivo...",
_ => "Cole seu conteúdo CSV aqui ou faça upload de um arquivo..."
};
texts["FileHelpText"] = language switch
{
"en" => "Max file size: 10MB. Supported formats: .csv, .txt",
"es" => "Tamaño máximo: 10MB. Formatos soportados: .csv, .txt",
_ => "Tamanho máximo: 10MB. Formatos suportados: .csv, .txt"
};
return new ConverterConfiguration
{
ConverterType = "text", // Aceita tanto texto quanto arquivo
OutputType = "text",
AcceptedFileTypes = new[] { ".csv", ".txt" },
MaxFileSize = 10 * 1024 * 1024,
HasAdvancedOptions = false,
AllowShare = true,
LocalizedTexts = texts
};
}
}
}

View File

@ -0,0 +1,94 @@
using OnlyOneAccessTemplate.Services.OnlyOneAccessTemplate.Services;
namespace OnlyOneAccessTemplate.Services
{
public class ImageToTextConverterService : BaseConverterService
{
private readonly HttpClient _httpClient;
public override string ConverterType => "image-ocr";
public override string ConverterName => "Image to Text (OCR)";
public ImageToTextConverterService(ILogger<ImageToTextConverterService> logger,
IConfiguration configuration, HttpClient httpClient)
: base(logger, configuration)
{
_httpClient = httpClient;
}
public override async Task<ConversionResult> ConvertAsync(ConversionRequest request)
{
try
{
if (request.FileInput == null)
{
return new ConversionResult(false, ErrorMessage: "Nenhuma imagem fornecida");
}
// Aqui você integraria com um serviço de OCR real como:
// - Azure Computer Vision
// - Google Cloud Vision
// - AWS Textract
// - Tesseract local
// Por agora, simular processamento
await Task.Delay(2000); // Simular processamento
// Exemplo de resultado simulado
var extractedText = $"Texto extraído da imagem: {request.FileInput.FileName}\n\n" +
"Este é um exemplo de texto que seria extraído da imagem usando OCR.\n" +
"Em uma implementação real, aqui estaria o texto real da imagem.";
return new ConversionResult(true, OutputText: extractedText);
}
catch (Exception ex)
{
_logger.LogError(ex, "Erro na conversão de imagem para texto");
return new ConversionResult(false, ErrorMessage: "Erro ao processar imagem");
}
}
public override ConverterConfiguration GetConfiguration(string language)
{
var texts = GetLocalizedTexts(language);
texts["ConverterTitle"] = language switch
{
"en" => "IMAGE TO TEXT CONVERTER - OCR ONLINE",
"es" => "CONVERTIDOR DE IMAGEN A TEXTO - OCR EN LÍNEA",
_ => "CONVERSOR DE IMAGEM PARA TEXTO - OCR ONLINE"
};
texts["ConverterDescription"] = language switch
{
"en" => "Extract text from images using Optical Character Recognition technology",
"es" => "Extrae texto de imágenes usando tecnología de Reconocimiento Óptico de Caracteres",
_ => "Extraia texto de imagens usando tecnologia de Reconhecimento Óptico de Caracteres"
};
texts["SelectFileText"] = language switch
{
"en" => "SELECT IMAGE",
"es" => "SELECCIONAR IMAGEN",
_ => "SELECIONAR IMAGEM"
};
texts["FileHelpText"] = language switch
{
"en" => "Max file size: 15MB. Supported formats: JPG, PNG, PDF, TIFF",
"es" => "Tamaño máximo: 15MB. Formatos soportados: JPG, PNG, PDF, TIFF",
_ => "Tamanho máximo: 15MB. Formatos suportados: JPG, PNG, PDF, TIFF"
};
return new ConverterConfiguration
{
ConverterType = "file",
OutputType = "text",
AcceptedFileTypes = new[] { ".jpg", ".jpeg", ".png", ".pdf", ".tiff", ".bmp" },
MaxFileSize = 15 * 1024 * 1024,
HasAdvancedOptions = true,
AllowShare = false, // OCR pode conter informações sensíveis
LocalizedTexts = texts
};
}
}
}

View File

@ -0,0 +1,84 @@
using OnlyOneAccessTemplate.Services.OnlyOneAccessTemplate.Services;
namespace OnlyOneAccessTemplate.Services
{
public class TextCaseConverterService : BaseConverterService
{
public override string ConverterType => "text-case";
public override string ConverterName => "Text Case Converter";
public TextCaseConverterService(ILogger<TextCaseConverterService> logger, IConfiguration configuration)
: base(logger, configuration) { }
public override async Task<ConversionResult> ConvertAsync(ConversionRequest request)
{
try
{
if (string.IsNullOrEmpty(request.TextInput))
{
return new ConversionResult(false, ErrorMessage: "Texto não fornecido");
}
var caseType = request.Options?.GetValueOrDefault("caseType", "upper")?.ToString() ?? "upper";
var result = caseType switch
{
"upper" => request.TextInput.ToUpperInvariant(),
"lower" => request.TextInput.ToLowerInvariant(),
"title" => System.Globalization.CultureInfo.CurrentCulture.TextInfo.ToTitleCase(request.TextInput.ToLower()),
"sentence" => char.ToUpper(request.TextInput[0]) + request.TextInput.Substring(1).ToLower(),
_ => request.TextInput
};
return new ConversionResult(true, OutputText: result);
}
catch (Exception ex)
{
_logger.LogError(ex, "Erro na conversão de texto");
return new ConversionResult(false, ErrorMessage: "Erro ao converter texto");
}
}
public override ConverterConfiguration GetConfiguration(string language)
{
var texts = GetLocalizedTexts(language);
texts["ConverterTitle"] = language switch
{
"en" => "TEXT CASE CONVERTER",
"es" => "CONVERTIDOR DE MAYÚSCULAS Y MINÚSCULAS",
_ => "CONVERSOR DE MAIÚSCULAS E MINÚSCULAS"
};
texts["ConverterDescription"] = language switch
{
"en" => "Convert text between uppercase, lowercase, title case and sentence case",
"es" => "Convierte texto entre mayúsculas, minúsculas, título y oración",
_ => "Converta texto entre maiúsculas, minúsculas, título e primeira maiúscula"
};
texts["InputPlaceholder"] = language switch
{
"en" => "Enter your text here...",
"es" => "Ingresa tu texto aquí...",
_ => "Digite seu texto aqui..."
};
texts["OutputPlaceholder"] = language switch
{
"en" => "Converted text will appear here...",
"es" => "El texto convertido aparecerá aquí...",
_ => "O texto convertido aparecerá aqui..."
};
return new ConverterConfiguration
{
ConverterType = "text",
OutputType = "text",
HasAdvancedOptions = true,
AllowShare = true,
LocalizedTexts = texts
};
}
}
}

View File

@ -1,212 +1,255 @@
@model OnlyOneAccessTemplate.Models.PageContent
@model PageContent;
@{
ViewData["Title"] = Model?.MetaTitle ?? Model?.Title ?? ViewBag.Title;
ViewData["Title"] = ViewBag.Title ?? "Conversor Online";
Layout = "~/Views/Shared/_Layout.cshtml";
}
@section Head {
<!-- Page specific meta tags -->
<meta name="description" content="@(Model?.Description ?? ViewBag.Description)">
<meta name="keywords" content="@(Model?.Keywords ?? ViewBag.Keywords)">
<!-- Custom CSS for animations -->
<style>
.hover-lift {
transition: transform 0.3s ease, box-shadow 0.3s ease;
}
.hover-lift:hover {
transform: translateY(-5px);
box-shadow: 0 10px 25px rgba(0,0,0,0.15) !important;
}
.animate-bounce {
animation: bounce 2s infinite;
}
.scroll-smooth {
scroll-behavior: smooth;
}
.bg-gradient-primary {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
}
.countdown-number {
min-width: 60px;
min-height: 60px;
display: flex;
align-items: center;
justify-content: center;
}
</style>
}
<!-- Hero Section -->
@{
var heroBlock = Model?.Blocks?.FirstOrDefault(b => b.Type == "hero");
}
@await Html.PartialAsync("Components/_Hero", heroBlock)
<!-- Features Section -->
@{
var featuresBlock = Model?.Blocks?.FirstOrDefault(b => b.Type == "features");
}
@await Html.PartialAsync("Components/_Features", featuresBlock)
<!-- Benefits/How it Works Section -->
@if (Model?.Blocks?.Any(b => b.Type == "benefits") == true)
{
var benefitsBlock = Model.Blocks.First(b => b.Type == "benefits");
<section class="benefits-section py-5">
<!-- Seção Principal do Conversor -->
<section class="converter-hero bg-light py-5">
<div class="container">
<div class="row justify-content-center mb-5">
<div class="col-lg-8 text-center">
<h2 class="display-5 fw-bold mb-3" data-aos="fade-up">
@benefitsBlock.Title
</h2>
<p class="lead text-muted" data-aos="fade-up" data-aos-delay="100">
@benefitsBlock.Content
<div class="row justify-content-center">
<div class="col-lg-10">
<!-- Título e Descrição -->
<div class="text-center mb-5">
<h1 class="display-4 fw-bold text-primary mb-3">
@(ViewBag.ConverterTitle ?? "CONVERSOR ONLINE")
</h1>
<p class="lead text-muted mb-4">
@(ViewBag.ConverterDescription ?? "Converta seus arquivos de forma rápida e segura")
</p>
</div>
<!-- Card do Conversor -->
<div class="card shadow-lg border-0 rounded-3">
<div class="card-body p-5">
<!-- Steps do Processo -->
<div class="row text-center mb-4">
<div class="col-md-4 mb-3">
<div class="step-indicator">
<span class="badge bg-primary rounded-circle p-3 fs-5 mb-2">1</span>
<h6 class="fw-bold">@(ViewBag.Step1Title ?? "Upload")</h6>
<small class="text-muted">@(ViewBag.Step1Description ?? "Selecione seu arquivo")</small>
</div>
</div>
<div class="col-md-4 mb-3">
<div class="step-indicator">
<span class="badge bg-primary rounded-circle p-3 fs-5 mb-2">2</span>
<h6 class="fw-bold">@(ViewBag.Step2Title ?? "Processar")</h6>
<small class="text-muted">@(ViewBag.Step2Description ?? "Aguarde o processamento")</small>
</div>
</div>
<div class="col-md-4 mb-3">
<div class="step-indicator">
<span class="badge bg-success rounded-circle p-3 fs-5 mb-2">3</span>
<h6 class="fw-bold">@(ViewBag.Step3Title ?? "Download")</h6>
<small class="text-muted">@(ViewBag.Step3Description ?? "Baixe o resultado")</small>
</div>
</div>
</div>
<!-- Área do Conversor -->
<div id="converter-container">
@await Html.PartialAsync("_ConverterWidget")
</div>
<!-- Informações Adicionais -->
<div class="row mt-4">
<div class="col-md-6">
<div class="d-flex align-items-center mb-2">
<i class="fas fa-shield-alt text-success me-2"></i>
<small class="text-muted">@(ViewBag.SecurityText ?? "Seus dados estão seguros")</small>
</div>
</div>
<div class="col-md-6">
<div class="d-flex align-items-center mb-2">
<i class="fas fa-file-alt text-primary me-2"></i>
<small class="text-muted">@(ViewBag.FileInfoText ?? "Processamento rápido e seguro")</small>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- Seção de Benefícios -->
<section class="benefits-section py-5">
<div class="container">
<div class="row">
<div class="col-lg-8 mx-auto text-center mb-5">
<h2 class="h3 fw-bold mb-3">@(ViewBag.BenefitsTitle ?? "Por Que Usar Nossa Ferramenta?")</h2>
<p class="text-muted">@(ViewBag.BenefitsSubtitle ?? "Descubra os benefícios de nossa solução")</p>
</div>
</div>
<div class="row g-4">
<div class="col-md-6" data-aos="fade-right">
<div class="benefit-item d-flex mb-4">
<div class="benefit-icon me-4">
<i class="fas fa-check-circle fa-2x text-success"></i>
@{
bool hasFeatureBlocks = false;
if (Model != null && Model.Blocks != null)
{
var featureBlocks = Model.Blocks.Where(b => b.Type == "features").ToList();
if (featureBlocks.Any())
{
hasFeatureBlocks = true;
var firstFeatureBlock = featureBlocks.First();
if (firstFeatureBlock.Properties != null && firstFeatureBlock.Properties.ContainsKey("feature_list"))
{
var featureList = firstFeatureBlock.Properties["feature_list"] as System.Collections.IEnumerable;
if (featureList != null)
{
foreach (var featureObj in featureList)
{
var featureDict = featureObj as Dictionary<string, object>;
if (featureDict != null)
{
<div class="col-md-6 col-lg-3">
<div class="text-center p-3">
<div class="feature-icon mb-3">
<i class="@(featureDict.GetValueOrDefault("icon", "fas fa-star")) fa-2x text-primary"></i>
</div>
<div>
<h5 class="fw-bold mb-2">Resultado Garantido</h5>
<p class="text-muted mb-0">Metodologia comprovada com mais de 1000 casos de sucesso documentados.</p>
<h6 class="fw-bold">@(featureDict.GetValueOrDefault("title", "Funcionalidade"))</h6>
<small class="text-muted">@(featureDict.GetValueOrDefault("description", "Descrição da funcionalidade"))</small>
</div>
</div>
}
}
}
}
}
}
}
<div class="benefit-item d-flex mb-4">
<div class="benefit-icon me-4">
@if (!hasFeatureBlocks)
{
<!-- Features padrão se não houver dados do modelo -->
<div class="col-md-6 col-lg-3">
<div class="text-center p-3">
<div class="feature-icon mb-3">
<i class="fas fa-rocket fa-2x text-primary"></i>
</div>
<h6 class="fw-bold">@(ViewBag.Feature1Title ?? "Rápido e Fácil")</h6>
<small class="text-muted">@(ViewBag.Feature1Description ?? "Conversão instantânea")</small>
</div>
</div>
<div class="col-md-6 col-lg-3">
<div class="text-center p-3">
<div class="feature-icon mb-3">
<i class="fas fa-shield-alt fa-2x text-primary"></i>
</div>
<h6 class="fw-bold">@(ViewBag.Feature2Title ?? "Seguro")</h6>
<small class="text-muted">@(ViewBag.Feature2Description ?? "Dados protegidos")</small>
</div>
</div>
<div class="col-md-6 col-lg-3">
<div class="text-center p-3">
<div class="feature-icon mb-3">
<i class="fas fa-users fa-2x text-primary"></i>
</div>
<h6 class="fw-bold">@(ViewBag.Feature3Title ?? "Confiável")</h6>
<small class="text-muted">@(ViewBag.Feature3Description ?? "Resultados precisos")</small>
</div>
</div>
<div class="col-md-6 col-lg-3">
<div class="text-center p-3">
<div class="feature-icon mb-3">
<i class="fas fa-clock fa-2x text-primary"></i>
</div>
<div>
<h5 class="fw-bold mb-2">Implementação Rápida</h5>
<p class="text-muted mb-0">Veja os primeiros resultados em até 30 dias após a implementação.</p>
</div>
</div>
<div class="benefit-item d-flex">
<div class="benefit-icon me-4">
<i class="fas fa-users fa-2x text-warning"></i>
</div>
<div>
<h5 class="fw-bold mb-2">Suporte Especializado</h5>
<p class="text-muted mb-0">Equipe dedicada para garantir o seu sucesso em cada etapa.</p>
</div>
</div>
</div>
<div class="col-md-6" data-aos="fade-left">
@if (!string.IsNullOrEmpty(benefitsBlock.ImageUrl))
{
<img src="@benefitsBlock.ImageUrl" alt="@benefitsBlock.Title" class="img-fluid rounded-3 shadow">
}
else
{
<div class="stats-container bg-light p-4 rounded-3">
<div class="row text-center">
<div class="col-4">
<div class="stat-item">
<h3 class="fw-bold text-primary mb-1">1000+</h3>
<small class="text-muted">Clientes Atendidos</small>
</div>
</div>
<div class="col-4">
<div class="stat-item">
<h3 class="fw-bold text-success mb-1">95%</h3>
<small class="text-muted">Taxa de Sucesso</small>
</div>
</div>
<div class="col-4">
<div class="stat-item">
<h3 class="fw-bold text-warning mb-1">24h</h3>
<small class="text-muted">Suporte</small>
</div>
</div>
<h6 class="fw-bold">Rápido</h6>
<small class="text-muted">Conversão em segundos</small>
</div>
</div>
}
</div>
</div>
</section>
<!-- Seção de Depoimentos (Opcional) -->
@{
bool hasTestimonialBlocks = false;
if (Model != null && Model.Blocks != null)
{
var testimonialBlocks = Model.Blocks.Where(b => b.Type == "testimonials").ToList();
if (testimonialBlocks.Any())
{
hasTestimonialBlocks = true;
}
}
}
@if (hasTestimonialBlocks)
{
<section class="testimonials-section py-5 bg-light">
<div class="container">
<div class="row">
<div class="col-lg-8 mx-auto text-center mb-5">
<h2 class="h3 fw-bold mb-3">@(ViewBag.DefaultTestimonialsTitle ?? "O Que Nossos Clientes Dizem")</h2>
<p class="text-muted">@(ViewBag.TestimonialsCount ?? "Avaliações positivas")</p>
</div>
</div>
<div class="row">
<div class="col-md-4 mb-4">
<div class="card border-0 h-100">
<div class="card-body text-center">
<p class="mb-3">"@(ViewBag.Testimonial1Quote ?? "Excelente ferramenta!")"</p>
<h6 class="fw-bold">@(ViewBag.Testimonial1Name ?? "Cliente Satisfeito")</h6>
<small class="text-muted">@(ViewBag.Testimonial1Position ?? "Usuário")</small>
</div>
</div>
</div>
<div class="col-md-4 mb-4">
<div class="card border-0 h-100">
<div class="card-body text-center">
<p class="mb-3">"@(ViewBag.Testimonial2Quote ?? "Muito útil e fácil de usar!")"</p>
<h6 class="fw-bold">@(ViewBag.Testimonial2Name ?? "Outro Cliente")</h6>
<small class="text-muted">@(ViewBag.Testimonial2Position ?? "Usuário")</small>
</div>
</div>
</div>
<div class="col-md-4 mb-4">
<div class="card border-0 h-100">
<div class="card-body text-center">
<p class="mb-3">"@(ViewBag.Testimonial3Quote ?? "Recomendo para todos!")"</p>
<h6 class="fw-bold">@(ViewBag.Testimonial3Name ?? "Mais um Cliente")</h6>
<small class="text-muted">@(ViewBag.Testimonial3Position ?? "Usuário")</small>
</div>
</div>
</div>
</div>
</div>
</section>
}
<!-- Testimonials Section -->
@{
var testimonialsBlock = Model?.Blocks?.FirstOrDefault(b => b.Type == "testimonials");
}
@await Html.PartialAsync("Components/_Testimonials", testimonialsBlock)
<!-- Conversion Form Section -->
@{
var conversionConfig = ViewBag.ConversionConfig as OnlyOneAccessTemplate.Models.ConversionConfig;
}
@await Html.PartialAsync("Components/_ConversionForm", conversionConfig)
<!-- CTA Section -->
@{
var ctaBlock = Model?.Blocks?.FirstOrDefault(b => b.Type == "cta");
}
@await Html.PartialAsync("Components/_CTA", ctaBlock)
<!-- Seção CTA Final -->
<section class="final-cta py-5">
<div class="container">
<div class="row">
<div class="col-lg-8 mx-auto text-center">
<h2 class="h3 fw-bold mb-3">@(ViewBag.FinalCtaTitle ?? "Pronto para Converter?")</h2>
<p class="text-muted mb-4">@(ViewBag.FinalCtaSubtitle ?? "Use nossa ferramenta gratuita agora mesmo")</p>
<a href="#converter-container" class="btn btn-primary btn-lg">
@(ViewBag.FinalCtaButtonText ?? "Começar Conversão")
</a>
</div>
</div>
</div>
</section>
<!-- Scripts específicos do conversor -->
@section Scripts {
<!-- AOS Animation JS -->
<script src="https://unpkg.com/aos@2.3.1/dist/aos.js"></script>
<script src="~/js/converter.js"></script>
@{
var converterType = ViewBag.ConverterType ?? "generic";
var jsFileName = $"~/js/converters/{converterType.ToString().ToLower()}-converter.js";
}
<script src="@jsFileName"></script>
<script>
// Initialize AOS
AOS.init({
duration: 800,
once: true,
offset: 100
});
// Smooth scroll for anchor links
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
anchor.addEventListener('click', function (e) {
e.preventDefault();
const target = document.querySelector(this.getAttribute('href'));
if (target) {
target.scrollIntoView({
behavior: 'smooth',
block: 'start'
});
// Inicializar conversor específico
document.addEventListener('DOMContentLoaded', function () {
if (typeof initializeConverter === 'function') {
initializeConverter();
}
});
});
// Countdown timer
const countdown = document.querySelector('[data-countdown]');
if (countdown) {
const targetDate = new Date(countdown.dataset.countdown).getTime();
setInterval(() => {
const now = new Date().getTime();
const distance = targetDate - now;
const days = Math.floor(distance / (1000 * 60 * 60 * 24));
const hours = Math.floor((distance % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
const minutes = Math.floor((distance % (1000 * 60 * 60)) / (1000 * 60));
const seconds = Math.floor((distance % (1000 * 60)) / 1000);
countdown.querySelector('[data-days]').textContent = days.toString().padStart(2, '0');
countdown.querySelector('[data-hours]').textContent = hours.toString().padStart(2, '0');
countdown.querySelector('[data-minutes]').textContent = minutes.toString().padStart(2, '0');
countdown.querySelector('[data-seconds]').textContent = seconds.toString().padStart(2, '0');
if (distance < 0) {
countdown.style.display = 'none';
}
}, 1000);
}
</script>
}

View File

@ -0,0 +1,202 @@
@* Views/Shared/_AdvancedOptions.cshtml *@
<div class="advanced-options-container">
@{
var converterType = ViewBag.ConverterType?.ToString() ?? "text";
}
@if (converterType == "text-case")
{
<!-- Opções para Conversor de Maiúsculas/Minúsculas -->
<div class="row">
<div class="col-md-6 mb-3">
<label class="form-label">Tipo de Conversão</label>
<select name="caseType" class="form-select">
<option value="upper">MAIÚSCULAS</option>
<option value="lower">minúsculas</option>
<option value="title">Primeira Maiúscula Em Cada Palavra</option>
<option value="sentence">Primeira maiúscula apenas</option>
</select>
<div class="form-text">Escolha como você quer converter o texto</div>
</div>
<div class="col-md-6 mb-3">
<label class="form-label">Preservar Formatação</label>
<div class="form-check">
<input class="form-check-input" type="checkbox" name="preserveSpaces" id="preserveSpaces" checked>
<label class="form-check-label" for="preserveSpaces">
Manter espaços e quebras de linha
</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" name="preserveNumbers" id="preserveNumbers" checked>
<label class="form-check-label" for="preserveNumbers">
Não alterar números
</label>
</div>
</div>
</div>
}
else if (converterType == "csv-json")
{
<!-- Opções para Conversor CSV para JSON -->
<div class="row">
<div class="col-md-6 mb-3">
<label class="form-label">Separador</label>
<select name="delimiter" class="form-select">
<option value=",">Vírgula (,)</option>
<option value=";">Ponto e vírgula (;)</option>
<option value="|">Pipe (|)</option>
<option value="tab">Tab</option>
</select>
</div>
<div class="col-md-6 mb-3">
<label class="form-label">Codificação</label>
<select name="encoding" class="form-select">
<option value="utf8">UTF-8</option>
<option value="latin1">Latin-1</option>
<option value="ascii">ASCII</option>
</select>
</div>
<div class="col-12">
<div class="form-check">
<input class="form-check-input" type="checkbox" name="includeHeaders" id="includeHeaders" checked>
<label class="form-check-label" for="includeHeaders">
Primeira linha contém cabeçalhos
</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" name="skipEmptyLines" id="skipEmptyLines" checked>
<label class="form-check-label" for="skipEmptyLines">
Ignorar linhas vazias
</label>
</div>
</div>
</div>
}
else if (converterType == "image-ocr")
{
<!-- Opções para OCR -->
<div class="row">
<div class="col-md-6 mb-3">
<label class="form-label">Idioma do OCR</label>
<select name="ocrLanguage" class="form-select">
<option value="por">Português</option>
<option value="eng">English</option>
<option value="spa">Español</option>
<option value="fra">Français</option>
<option value="deu">Deutsch</option>
<option value="ita">Italiano</option>
</select>
</div>
<div class="col-md-6 mb-3">
<label class="form-label">Qualidade vs Velocidade</label>
<select name="ocrMode" class="form-select">
<option value="balanced">Balanceado</option>
<option value="fast">Rápido</option>
<option value="accurate">Preciso</option>
</select>
</div>
<div class="col-12">
<div class="form-check">
<input class="form-check-input" type="checkbox" name="autoRotate" id="autoRotate" checked>
<label class="form-check-label" for="autoRotate">
Detectar e corrigir rotação automaticamente
</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" name="enhanceImage" id="enhanceImage">
<label class="form-check-label" for="enhanceImage">
Melhorar qualidade da imagem antes do OCR
</label>
</div>
</div>
</div>
}
else if (converterType == "json-csv")
{
<!-- Opções para JSON para CSV -->
<div class="row">
<div class="col-md-6 mb-3">
<label class="form-label">Separador CSV</label>
<select name="csvDelimiter" class="form-select">
<option value=",">Vírgula (,)</option>
<option value=";">Ponto e vírgula (;)</option>
<option value="|">Pipe (|)</option>
</select>
</div>
<div class="col-md-6 mb-3">
<label class="form-label">Tratamento de Objetos Aninhados</label>
<select name="nestedHandling" class="form-select">
<option value="flatten">Achatar (objeto.propriedade)</option>
<option value="json">Manter como JSON</option>
<option value="ignore">Ignorar</option>
</select>
</div>
<div class="col-12">
<div class="form-check">
<input class="form-check-input" type="checkbox" name="includeHeaders" id="csvHeaders" checked>
<label class="form-check-label" for="csvHeaders">
Incluir linha de cabeçalho
</label>
</div>
</div>
</div>
}
else
{
<!-- Opções genéricas para outros conversores -->
<div class="row">
<div class="col-md-6 mb-3">
<label class="form-label">Qualidade</label>
<select name="quality" class="form-select">
<option value="high">Alta</option>
<option value="medium" selected>Média</option>
<option value="low">Baixa</option>
</select>
</div>
<div class="col-md-6 mb-3">
<label class="form-label">Formato de Saída</label>
<select name="outputFormat" class="form-select">
<option value="auto">Automático</option>
<option value="text">Texto</option>
<option value="file">Arquivo</option>
</select>
</div>
</div>
}
<!-- Opções Comuns -->
<div class="row mt-3">
<div class="col-12">
<div class="form-check">
<input class="form-check-input" type="checkbox" name="optimizeOutput" id="optimizeOutput">
<label class="form-check-label" for="optimizeOutput">
Otimizar resultado para melhor performance
</label>
</div>
</div>
</div>
</div>
<style>
.advanced-options-container {
background-color: #f8f9fa;
border: 1px solid #e9ecef;
border-radius: 0.375rem;
padding: 1rem;
}
.advanced-options-container .form-label {
font-weight: 600;
color: #495057;
font-size: 0.875rem;
}
.advanced-options-container .form-text {
font-size: 0.75rem;
}
.advanced-options-container .form-check-label {
font-size: 0.875rem;
}
</style>

View File

@ -0,0 +1,270 @@
@* Views/Shared/_ConverterWidget.cshtml *@
<!-- Widget Base do Conversor - Personalizável para cada tipo -->
<div class="converter-widget">
<form id="converterForm" enctype="multipart/form-data">
<div class="row">
<!-- Input Area -->
<div class="col-md-6 mb-4">
<div class="input-section">
<label class="form-label fw-bold">@ViewBag.InputLabel</label>
<!-- File Upload (se for conversão de arquivo) -->
@if (ViewBag.ConverterType == "file")
{
<div class="upload-area border-2 border-dashed rounded-3 p-4 text-center">
<input type="file" id="fileInput" name="file" class="d-none" accept="@ViewBag.AcceptedFileTypes">
<div class="upload-placeholder">
<i class="fas fa-cloud-upload-alt fa-3x text-muted mb-3"></i>
<p class="mb-2">
<button type="button" class="btn btn-outline-primary" onclick="document.getElementById('fileInput').click()">
@ViewBag.SelectFileText
</button>
</p>
<small class="text-muted">@ViewBag.FileHelpText</small>
<div class="file-info mt-2" style="display: none;">
<small class="text-success">
<i class="fas fa-check-circle me-1"></i>
<span id="fileName"></span>
</small>
</div>
</div>
</div>
}
<!-- Text Input (se for conversão de texto) -->
@if (ViewBag.ConverterType == "text")
{
<textarea id="inputText" name="inputText" class="form-control" rows="10"
placeholder="@ViewBag.InputPlaceholder">@ViewBag.SampleInput</textarea>
<div class="form-text">@ViewBag.InputHelpText</div>
}
<!-- URL Input (se for conversão de URL) -->
@if (ViewBag.ConverterType == "url")
{
<input type="url" id="inputUrl" name="inputUrl" class="form-control"
placeholder="@ViewBag.InputPlaceholder" value="@ViewBag.SampleInput">
<div class="form-text">@ViewBag.InputHelpText</div>
}
</div>
<!-- Configurações Adicionais -->
@if (ViewBag.HasAdvancedOptions == true)
{
<div class="advanced-options mt-3">
<button type="button" class="btn btn-sm btn-outline-secondary" data-bs-toggle="collapse" data-bs-target="#advancedSettings">
<i class="fas fa-cog me-1"></i> @(ViewBag.AdvancedOptionsText ?? "Opções Avançadas")
</button>
<div class="collapse mt-3" id="advancedSettings">
@{
try
{
await Html.RenderPartialAsync("_AdvancedOptions");
}
catch
{
<!-- Fallback se a partial não existir -->
<div class="alert alert-info">
<small>Opções avançadas não disponíveis para este conversor.</small>
</div>
}
}
</div>
</div>
}
</div>
<!-- Output Area -->
<div class="col-md-6 mb-4">
<div class="output-section">
<label class="form-label fw-bold">@ViewBag.OutputLabel</label>
<!-- Loading State -->
<div id="loadingState" class="text-center p-5" style="display: none;">
<div class="spinner-border text-primary mb-3" role="status">
<span class="visually-hidden">@ViewBag.LoadingText</span>
</div>
<p class="text-muted">@ViewBag.ProcessingText</p>
</div>
<!-- Output Display -->
<div id="outputArea">
@if (ViewBag.OutputType == "text")
{
<textarea id="outputText" class="form-control" rows="10" readonly
placeholder="@ViewBag.OutputPlaceholder"></textarea>
}
else if (ViewBag.OutputType == "download")
{
<div class="download-area border rounded-3 p-4 text-center" style="display: none;">
<i class="fas fa-download fa-2x text-success mb-3"></i>
<p class="mb-3">@ViewBag.DownloadReadyText</p>
<button type="button" id="downloadBtn" class="btn btn-success">
<i class="fas fa-download me-1"></i> @ViewBag.DownloadButtonText
</button>
</div>
}
else if (ViewBag.OutputType == "preview")
{
<div id="previewArea" class="preview-container border rounded-3 p-3" style="min-height: 200px;">
<div class="text-center text-muted p-4">
<i class="fas fa-eye fa-2x mb-2"></i>
<p>@ViewBag.PreviewPlaceholder</p>
</div>
</div>
}
</div>
<!-- Error Display -->
<div id="errorArea" class="alert alert-danger mt-3" style="display: none;">
<i class="fas fa-exclamation-triangle me-2"></i>
<span id="errorMessage"></span>
</div>
<!-- Success Actions -->
<div id="successActions" class="mt-3" style="display: none;">
<div class="d-flex gap-2 flex-wrap">
<button type="button" id="copyBtn" class="btn btn-sm btn-outline-primary">
<i class="fas fa-copy me-1"></i> @ViewBag.CopyButtonText
</button>
<button type="button" id="clearBtn" class="btn btn-sm btn-outline-secondary">
<i class="fas fa-trash me-1"></i> @ViewBag.ClearButtonText
</button>
@if (ViewBag.AllowShare == true)
{
<button type="button" id="shareBtn" class="btn btn-sm btn-outline-info">
<i class="fas fa-share me-1"></i> @ViewBag.ShareButtonText
</button>
}
</div>
</div>
</div>
</div>
</div>
<!-- Convert Button -->
<div class="text-center mt-4">
<button type="submit" id="convertBtn" class="btn btn-primary btn-lg px-5">
<i class="fas fa-sync-alt me-2"></i>
@ViewBag.ConvertButtonText
</button>
</div>
</form>
</div>
<!-- JavaScript Base do Conversor -->
<script>
document.addEventListener('DOMContentLoaded', function () {
const form = document.getElementById('converterForm');
const convertBtn = document.getElementById('convertBtn');
const loadingState = document.getElementById('loadingState');
const outputArea = document.getElementById('outputArea');
const errorArea = document.getElementById('errorArea');
const successActions = document.getElementById('successActions');
// File input handler
const fileInput = document.getElementById('fileInput');
if (fileInput) {
fileInput.addEventListener('change', function (e) {
const file = e.target.files[0];
if (file) {
document.getElementById('fileName').textContent = file.name;
document.querySelector('.file-info').style.display = 'block';
}
});
}
// Form submission
form.addEventListener('submit', async function (e) {
e.preventDefault();
// Reset states
hideElement(errorArea);
hideElement(successActions);
showElement(loadingState);
hideElement(outputArea.querySelector('.download-area, #previewArea'));
// Disable button
convertBtn.disabled = true;
try {
const formData = new FormData(form);
// Call converter-specific function
if (typeof performConversion === 'function') {
await performConversion(formData);
} else {
throw new Error('Converter function not implemented');
}
} catch (error) {
showError(error.message || '@ViewBag.DefaultErrorMessage');
} finally {
hideElement(loadingState);
convertBtn.disabled = false;
}
});
// Utility functions
function showElement(element) {
if (element) element.style.display = 'block';
}
function hideElement(element) {
if (element) element.style.display = 'none';
}
function showError(message) {
document.getElementById('errorMessage').textContent = message;
showElement(errorArea);
}
// Copy button functionality
const copyBtn = document.getElementById('copyBtn');
if (copyBtn) {
copyBtn.addEventListener('click', function () {
const outputText = document.getElementById('outputText');
if (outputText) {
outputText.select();
document.execCommand('copy');
// Visual feedback
const originalText = copyBtn.innerHTML;
copyBtn.innerHTML = '<i class="fas fa-check me-1"></i> @ViewBag.CopiedText';
setTimeout(() => {
copyBtn.innerHTML = originalText;
}, 2000);
}
});
}
// Clear button functionality
const clearBtn = document.getElementById('clearBtn');
if (clearBtn) {
clearBtn.addEventListener('click', function () {
form.reset();
hideElement(successActions);
hideElement(errorArea);
// Clear outputs
const outputText = document.getElementById('outputText');
if (outputText) outputText.value = '';
const previewArea = document.getElementById('previewArea');
if (previewArea) {
previewArea.innerHTML = `
<div class="text-center text-muted p-4">
<i class="fas fa-eye fa-2x mb-2"></i>
<p>@ViewBag.PreviewPlaceholder</p>
</div>
`;
}
// Hide file info
const fileInfo = document.querySelector('.file-info');
if (fileInfo) fileInfo.style.display = 'none';
});
}
});
</script>

View File

@ -0,0 +1,56 @@
@* Views/Shared/_TestimonialsSection.cshtml *@
@model OnlyOneAccessTemplate.Models.ContentBlock
<div class="row justify-content-center">
@{
var testimonials = new[]
{
new { Quote = ViewBag.Testimonial1Quote ?? "Excelente ferramenta, muito fácil de usar!", Name = ViewBag.Testimonial1Name ?? "Maria Silva", Position = ViewBag.Testimonial1Position ?? "Usuária" },
new { Quote = ViewBag.Testimonial2Quote ?? "Economizou muito do meu tempo no trabalho.", Name = ViewBag.Testimonial2Name ?? "João Santos", Position = ViewBag.Testimonial2Position ?? "Profissional" },
new { Quote = ViewBag.Testimonial3Quote ?? "Recomendo para todos que precisam converter arquivos.", Name = ViewBag.Testimonial3Name ?? "Ana Costa", Position = ViewBag.Testimonial3Position ?? "Cliente" }
};
}
@foreach (var testimonial in testimonials)
{
<div class="col-lg-4 col-md-6 mb-4">
<div class="card border-0 shadow-sm h-100">
<div class="card-body text-center p-4">
<div class="mb-3">
<i class="fas fa-quote-left text-primary fa-2x opacity-50"></i>
</div>
<p class="card-text mb-4 fst-italic">
"@testimonial.Quote"
</p>
<div class="d-flex align-items-center justify-content-center">
<div class="avatar me-3">
<div class="bg-primary rounded-circle d-flex align-items-center justify-content-center" style="width: 50px; height: 50px;">
<span class="text-white fw-bold">@testimonial.Name.Substring(0, 1)</span>
</div>
</div>
<div class="text-start">
<h6 class="mb-0 fw-bold">@testimonial.Name</h6>
<small class="text-muted">@testimonial.Position</small>
</div>
</div>
</div>
</div>
</div>
}
</div>
<!-- Avaliação Geral -->
<div class="row mt-4">
<div class="col-12 text-center">
<div class="d-inline-flex align-items-center bg-success bg-opacity-10 rounded-pill px-4 py-2">
<div class="me-2">
@for (int i = 1; i <= 5; i++)
{
<i class="fas fa-star text-warning"></i>
}
</div>
<span class="fw-bold text-success">4.9/5</span>
<span class="text-muted ms-2">@(ViewBag.TestimonialsCount ?? "Mais de 500 avaliações positivas")</span>
</div>
</div>
</div>

View File

@ -1,8 +0,0 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}

View File

@ -0,0 +1,18 @@
{
"ConnectionStrings": {
"MongoDB": "mongodb+srv://usuario:senha@cluster.mongodb.net/text_case_converter_db?retryWrites=true&w=majority"
},
"SEO": {
"DefaultDomain": "https://maiusculasminusculas.com",
"GoogleTagManagerId": "GTM-REAL123",
"GoogleAnalyticsId": "GA-REAL456"
},
"Logging": {
"LogLevel": {
"Default": "Warning",
"Microsoft.AspNetCore": "Warning"
}
}
}

View File

@ -1,31 +1,43 @@
{
// Exemplo 1: Conversor de Maiúsculas/Minúsculas
"Converter": {
"Type": "text-case",
"Name": "Conversor de Maiúsculas e Minúsculas"
},
// Exemplo 2: Conversor CSV para JSON
// "Converter": {
// "Type": "csv-json",
// "Name": "Conversor CSV para JSON"
// },
// Exemplo 3: Conversor OCR (Imagem para Texto)
// "Converter": {
// "Type": "image-ocr",
// "Name": "Conversor de Imagem para Texto"
// },
"ConnectionStrings": {
"MongoDB": "mongodb://admin:c4rn31r0@k3sw2:27017,k3ss1:27017/?authSource=admin"
},
"MongoDB": {
"DatabaseName": "text_case_converter_db"
},
"SEO": {
"DefaultDomain": "https://maiusculasminusculas.com",
"DefaultSiteName": "Conversor de Maiúsculas e Minúsculas Online",
"GoogleTagManagerId": "GTM-XXXXXXX",
"GoogleAnalyticsId": "GA-XXXXXXXX"
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"ConnectionStrings": {
"MongoDB": "mongodb://admin:c4rn31r0@k3sw2:27017,k3ss1:27017/?authSource=admin"
},
"MongoDB": {
"DatabaseName": "ConversionSite",
"Collections": {
"SiteConfigurations": "site_configurations",
"ConversionData": "conversion_data",
"Analytics": "analytics"
}
},
"SEO": {
"DefaultSiteName": "Seu Site de Conversão",
"DefaultDomain": "https://seusite.com",
"GoogleTagManagerId": "GTM-XXXXXXX",
"GoogleAnalyticsId": "GA-XXXXXXXX",
"FacebookPixelId": "XXXXXXXXX"
},
"ResponseCaching": {
"Duration": 300,
"VaryByHeader": "Accept-Language"
}
"AllowedHosts": "*"
}

View File

@ -0,0 +1,413 @@
// wwwroot/js/converters/text-case-converter.js
// Implementação específica para conversor de maiúsculas/minúsculas
let converterConfig = null;
// Função principal chamada pelo widget base
async function performConversion(formData) {
const textInput = formData.get('inputText');
const caseType = formData.get('caseType') || 'upper';
if (!textInput || textInput.trim() === '') {
throw new Error('Por favor, insira algum texto para converter');
}
// Fazer requisição para o backend
const requestData = new FormData();
requestData.append('inputType', 'text');
requestData.append('textInput', textInput);
requestData.append('language', document.documentElement.lang || 'pt');
// Adicionar opções
const options = { caseType: caseType };
requestData.append('options', JSON.stringify(options));
const response = await fetch('/converter/api/convert/text-case', {
method: 'POST',
body: requestData
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.message || 'Erro na conversão');
}
const result = await response.json();
// Mostrar resultado
showConversionResult(result.outputText);
}
// Função para mostrar o resultado
function showConversionResult(outputText) {
const outputArea = document.getElementById('outputArea');
const outputTextElement = document.getElementById('outputText');
const successActions = document.getElementById('successActions');
if (outputTextElement) {
outputTextElement.value = outputText;
outputArea.style.display = 'block';
successActions.style.display = 'block';
}
}
// Inicialização específica do conversor
function initializeConverter() {
loadConverterConfig();
setupAdvancedOptions();
}
// Carregar configuração do conversor
async function loadConverterConfig() {
try {
const response = await fetch('/converter/config/text-case');
if (response.ok) {
converterConfig = await response.json();
updateUIWithConfig(converterConfig.config);
}
} catch (error) {
console.error('Erro ao carregar configuração:', error);
}
}
// Atualizar UI com configuração
function updateUIWithConfig(config) {
const texts = config.localizedTexts;
// Atualizar textos localizados
Object.keys(texts).forEach(key => {
const elements = document.querySelectorAll(`[data-text="${key}"]`);
elements.forEach(el => {
if (el.tagName === 'INPUT' && (el.type === 'button' || el.type === 'submit')) {
el.value = texts[key];
} else {
el.textContent = texts[key];
}
});
});
}
// Configurar opções avançadas
function setupAdvancedOptions() {
const advancedContainer = document.getElementById('advancedSettings');
if (advancedContainer) {
advancedContainer.innerHTML = `
<div class="row">
<div class="col-md-6">
<label class="form-label">Tipo de Conversão</label>
<select name="caseType" class="form-select">
<option value="upper">MAIÚSCULAS</option>
<option value="lower">minúsculas</option>
<option value="title">Primeira Maiúscula Em Cada Palavra</option>
<option value="sentence">Primeira maiúscula apenas</option>
</select>
</div>
</div>
`;
}
}
// wwwroot/js/converters/csv-json-converter.js
// Implementação específica para conversor CSV para JSON
async function performConversion(formData) {
const textInput = formData.get('inputText');
const fileInput = formData.get('file');
if (!textInput && !fileInput) {
throw new Error('Por favor, forneça conteúdo CSV ou faça upload de um arquivo');
}
const requestData = new FormData();
if (fileInput && fileInput.size > 0) {
requestData.append('inputType', 'file');
requestData.append('fileInput', fileInput);
} else {
requestData.append('inputType', 'text');
requestData.append('textInput', textInput);
}
requestData.append('language', document.documentElement.lang || 'pt');
const response = await fetch('/converter/api/convert/csv-json', {
method: 'POST',
body: requestData
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.message || 'Erro na conversão');
}
const result = await response.json();
// Mostrar resultado formatado
showJsonResult(result.outputText);
}
function showJsonResult(jsonText) {
const outputTextElement = document.getElementById('outputText');
const successActions = document.getElementById('successActions');
if (outputTextElement) {
// Formatar JSON para melhor visualização
try {
const parsed = JSON.parse(jsonText);
outputTextElement.value = JSON.stringify(parsed, null, 2);
} catch {
outputTextElement.value = jsonText;
}
document.getElementById('outputArea').style.display = 'block';
successActions.style.display = 'block';
// Adicionar botão de download
addDownloadButton(jsonText);
}
}
function addDownloadButton(content) {
const successActions = document.getElementById('successActions');
// Verificar se já existe botão de download
if (document.getElementById('downloadJsonBtn')) return;
const downloadBtn = document.createElement('button');
downloadBtn.id = 'downloadJsonBtn';
downloadBtn.type = 'button';
downloadBtn.className = 'btn btn-sm btn-outline-success';
downloadBtn.innerHTML = '<i class="fas fa-download me-1"></i> Baixar JSON';
downloadBtn.addEventListener('click', () => {
const blob = new Blob([content], { type: 'application/json' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'converted.json';
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
});
successActions.appendChild(downloadBtn);
}
function initializeConverter() {
// CSV específico: permitir preview dos dados
setupCsvPreview();
}
function setupCsvPreview() {
const textInput = document.getElementById('inputText');
if (textInput) {
textInput.addEventListener('input', debounce(showCsvPreview, 500));
}
}
function showCsvPreview() {
const textInput = document.getElementById('inputText');
const csvContent = textInput.value.trim();
if (csvContent.length > 0) {
try {
const lines = csvContent.split('\n').slice(0, 5); // Mostrar só 5 linhas
const preview = lines.map(line =>
line.split(',').map(cell => `<td>${cell.trim()}</td>`).join('')
).map(row => `<tr>${row}</tr>`).join('');
// Mostrar preview se houver elemento para isso
const previewContainer = document.getElementById('csvPreview');
if (previewContainer) {
previewContainer.innerHTML = `
<small class="text-muted">Preview (primeiras 5 linhas):</small>
<table class="table table-sm table-bordered mt-1">
${preview}
</table>
`;
}
} catch (error) {
// Ignorar erros de preview
}
}
}
// Utility function
function debounce(func, wait) {
let timeout;
return function executedFunction(...args) {
const later = () => {
clearTimeout(timeout);
func(...args);
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
}
// wwwroot/js/converters/image-ocr-converter.js
// Implementação específica para conversor de imagem para texto (OCR)
async function performConversion(formData) {
const fileInput = formData.get('file');
if (!fileInput || fileInput.size === 0) {
throw new Error('Por favor, selecione uma imagem');
}
// Validar tipo de arquivo
const allowedTypes = ['image/jpeg', 'image/jpg', 'image/png', 'image/gif', 'image/bmp', 'application/pdf'];
if (!allowedTypes.includes(fileInput.type)) {
throw new Error('Formato de arquivo não suportado. Use JPG, PNG, GIF, BMP ou PDF.');
}
// Validar tamanho (15MB)
if (fileInput.size > 15 * 1024 * 1024) {
throw new Error('Arquivo muito grande. Tamanho máximo: 15MB');
}
const requestData = new FormData();
requestData.append('inputType', 'file');
requestData.append('fileInput', fileInput);
requestData.append('language', document.documentElement.lang || 'pt');
// Adicionar opções de OCR se houver
const ocrLanguage = formData.get('ocrLanguage') || 'por';
const options = { ocrLanguage: ocrLanguage };
requestData.append('options', JSON.stringify(options));
const response = await fetch('/converter/api/convert/image-ocr', {
method: 'POST',
body: requestData
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.message || 'Erro no processamento da imagem');
}
const result = await response.json();
// Mostrar resultado do OCR
showOcrResult(result.outputText, fileInput);
}
function showOcrResult(extractedText, originalFile) {
const outputTextElement = document.getElementById('outputText');
const successActions = document.getElementById('successActions');
if (outputTextElement) {
outputTextElement.value = extractedText;
document.getElementById('outputArea').style.display = 'block';
successActions.style.display = 'block';
// Mostrar preview da imagem
showImagePreview(originalFile);
// Adicionar botão para salvar texto
addSaveTextButton(extractedText);
}
}
function showImagePreview(file) {
const previewContainer = document.getElementById('imagePreview');
if (previewContainer && file.type.startsWith('image/')) {
const reader = new FileReader();
reader.onload = function (e) {
previewContainer.innerHTML = `
<div class="mt-3">
<small class="text-muted">Imagem processada:</small><br>
<img src="${e.target.result}" alt="Preview" class="img-thumbnail mt-1" style="max-height: 200px;">
</div>
`;
};
reader.readAsDataURL(file);
}
}
function addSaveTextButton(text) {
const successActions = document.getElementById('successActions');
if (document.getElementById('saveTextBtn')) return;
const saveBtn = document.createElement('button');
saveBtn.id = 'saveTextBtn';
saveBtn.type = 'button';
saveBtn.className = 'btn btn-sm btn-outline-success';
saveBtn.innerHTML = '<i class="fas fa-save me-1"></i> Salvar Texto';
saveBtn.addEventListener('click', () => {
const blob = new Blob([text], { type: 'text/plain;charset=utf-8' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'texto-extraido.txt';
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
});
successActions.appendChild(saveBtn);
}
function initializeConverter() {
setupAdvancedOcrOptions();
setupImagePreview();
}
function setupAdvancedOcrOptions() {
const advancedContainer = document.getElementById('advancedSettings');
if (advancedContainer) {
advancedContainer.innerHTML = `
<div class="row">
<div class="col-md-6">
<label class="form-label">Idioma do OCR</label>
<select name="ocrLanguage" class="form-select">
<option value="por">Português</option>
<option value="eng">English</option>
<option value="spa">Español</option>
<option value="fra">Français</option>
<option value="deu">Deutsch</option>
</select>
</div>
<div class="col-md-6">
<label class="form-label">Qualidade</label>
<select name="quality" class="form-select">
<option value="balanced">Balanceada</option>
<option value="fast">Rápida</option>
<option value="accurate">Precisa</option>
</select>
</div>
</div>
`;
}
}
function setupImagePreview() {
const fileInput = document.getElementById('fileInput');
if (fileInput) {
fileInput.addEventListener('change', function (e) {
const file = e.target.files[0];
if (file && file.type.startsWith('image/')) {
const reader = new FileReader();
reader.onload = function (event) {
let previewContainer = document.getElementById('imagePreview');
if (!previewContainer) {
previewContainer = document.createElement('div');
previewContainer.id = 'imagePreview';
fileInput.parentNode.appendChild(previewContainer);
}
previewContainer.innerHTML = `
<div class="mt-2">
<img src="${event.target.result}" alt="Preview" class="img-thumbnail" style="max-height: 150px;">
</div>
`;
};
reader.readAsDataURL(file);
}
});
}
}