Improve public pages
This commit is contained in:
444
frontend/src/pages/public/ContactPage.tsx
Normal file
444
frontend/src/pages/public/ContactPage.tsx
Normal file
@@ -0,0 +1,444 @@
|
||||
import React, { useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { PublicLayout } from '../../components/layout';
|
||||
import {
|
||||
MessageSquare,
|
||||
Mail,
|
||||
Phone,
|
||||
MapPin,
|
||||
Clock,
|
||||
Send,
|
||||
CheckCircle2,
|
||||
AlertCircle,
|
||||
HelpCircle
|
||||
} from 'lucide-react';
|
||||
|
||||
interface ContactMethod {
|
||||
id: string;
|
||||
title: string;
|
||||
description: string;
|
||||
detail: string;
|
||||
icon: React.ComponentType<{ className?: string }>;
|
||||
action?: () => void;
|
||||
link?: string;
|
||||
}
|
||||
|
||||
const ContactPage: React.FC = () => {
|
||||
const { t } = useTranslation();
|
||||
const [formState, setFormState] = useState({
|
||||
name: '',
|
||||
email: '',
|
||||
phone: '',
|
||||
bakeryName: '',
|
||||
subject: '',
|
||||
message: '',
|
||||
type: 'general' as 'general' | 'technical' | 'sales' | 'feedback',
|
||||
});
|
||||
const [submitStatus, setSubmitStatus] = useState<'idle' | 'loading' | 'success' | 'error'>('idle');
|
||||
|
||||
const contactMethods: ContactMethod[] = [
|
||||
{
|
||||
id: 'chat',
|
||||
title: 'Chat en Vivo',
|
||||
description: 'Respuesta inmediata',
|
||||
detail: 'Lunes a Viernes: 9:00 - 21:00 CET',
|
||||
icon: MessageSquare,
|
||||
link: '#chat',
|
||||
},
|
||||
{
|
||||
id: 'email',
|
||||
title: 'Email',
|
||||
description: 'soporte@panaderia-ia.com',
|
||||
detail: 'Respuesta en menos de 4 horas',
|
||||
icon: Mail,
|
||||
link: 'mailto:soporte@panaderia-ia.com',
|
||||
},
|
||||
{
|
||||
id: 'phone',
|
||||
title: 'Teléfono',
|
||||
description: '+34 XXX XXX XXX',
|
||||
detail: 'Lunes a Viernes: 10:00 - 19:00 CET',
|
||||
icon: Phone,
|
||||
link: 'tel:+34XXXXXXXXX',
|
||||
},
|
||||
{
|
||||
id: 'office',
|
||||
title: 'Oficina',
|
||||
description: 'Barcelona, España',
|
||||
detail: 'Con cita previa',
|
||||
icon: MapPin,
|
||||
},
|
||||
];
|
||||
|
||||
const handleChange = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement>) => {
|
||||
setFormState({
|
||||
...formState,
|
||||
[e.target.name]: e.target.value,
|
||||
});
|
||||
};
|
||||
|
||||
const handleSubmit = async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
setSubmitStatus('loading');
|
||||
|
||||
// Simulate API call
|
||||
await new Promise((resolve) => setTimeout(resolve, 1500));
|
||||
|
||||
// In production, this would be an actual API call
|
||||
console.log('Form submitted:', formState);
|
||||
|
||||
setSubmitStatus('success');
|
||||
setTimeout(() => {
|
||||
setSubmitStatus('idle');
|
||||
setFormState({
|
||||
name: '',
|
||||
email: '',
|
||||
phone: '',
|
||||
bakeryName: '',
|
||||
subject: '',
|
||||
message: '',
|
||||
type: 'general',
|
||||
});
|
||||
}, 3000);
|
||||
};
|
||||
|
||||
return (
|
||||
<PublicLayout
|
||||
variant="default"
|
||||
contentPadding="default"
|
||||
headerProps={{
|
||||
showThemeToggle: true,
|
||||
showAuthButtons: true,
|
||||
showLanguageSelector: true,
|
||||
variant: 'default',
|
||||
}}
|
||||
>
|
||||
{/* Hero Section */}
|
||||
<section className="bg-gradient-to-br from-[var(--bg-primary)] via-[var(--bg-secondary)] to-[var(--color-primary)]/5 py-20">
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div className="text-center max-w-4xl mx-auto">
|
||||
<div className="inline-flex items-center gap-2 bg-[var(--color-primary)]/10 text-[var(--color-primary)] px-4 py-2 rounded-full text-sm font-medium mb-6">
|
||||
<MessageSquare className="w-4 h-4" />
|
||||
<span>Contacto y Soporte</span>
|
||||
</div>
|
||||
<h1 className="text-4xl lg:text-6xl font-extrabold text-[var(--text-primary)] mb-6">
|
||||
Estamos Aquí Para
|
||||
<span className="block text-[var(--color-primary)]">Ayudarte</span>
|
||||
</h1>
|
||||
<p className="text-xl text-[var(--text-secondary)] leading-relaxed mb-8">
|
||||
¿Tienes preguntas? ¿Necesitas ayuda? Nuestro equipo está listo para asistirte
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Contact Methods */}
|
||||
<section className="py-20 bg-[var(--bg-primary)]">
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div className="text-center mb-12">
|
||||
<h2 className="text-3xl lg:text-4xl font-extrabold text-[var(--text-primary)] mb-4">
|
||||
Múltiples Formas de Contactar
|
||||
</h2>
|
||||
<p className="text-xl text-[var(--text-secondary)]">
|
||||
Elige el método que más te convenga
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="grid md:grid-cols-2 lg:grid-cols-4 gap-6 mb-16">
|
||||
{contactMethods.map((method) => {
|
||||
const ContactIcon = method.icon;
|
||||
const content = (
|
||||
<>
|
||||
<div className="w-16 h-16 bg-[var(--color-primary)]/10 rounded-full flex items-center justify-center mx-auto mb-4 group-hover:bg-[var(--color-primary)] transition-colors">
|
||||
<ContactIcon className="w-8 h-8 text-[var(--color-primary)] group-hover:text-white transition-colors" />
|
||||
</div>
|
||||
<h3 className="text-xl font-bold text-[var(--text-primary)] mb-2">
|
||||
{method.title}
|
||||
</h3>
|
||||
<p className="text-sm text-[var(--text-secondary)] mb-1">
|
||||
{method.description}
|
||||
</p>
|
||||
<p className="text-xs text-[var(--text-tertiary)]">
|
||||
{method.detail}
|
||||
</p>
|
||||
</>
|
||||
);
|
||||
|
||||
if (method.link) {
|
||||
return (
|
||||
<a
|
||||
key={method.id}
|
||||
href={method.link}
|
||||
className="bg-[var(--bg-secondary)] rounded-2xl p-8 border border-[var(--border-primary)] hover:border-[var(--color-primary)] hover:shadow-xl transition-all text-center group"
|
||||
>
|
||||
{content}
|
||||
</a>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
key={method.id}
|
||||
className="bg-[var(--bg-secondary)] rounded-2xl p-8 border border-[var(--border-primary)] text-center"
|
||||
>
|
||||
{content}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Contact Form */}
|
||||
<section className="py-20 bg-[var(--bg-secondary)]">
|
||||
<div className="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div className="text-center mb-12">
|
||||
<h2 className="text-3xl lg:text-4xl font-extrabold text-[var(--text-primary)] mb-4">
|
||||
Envíanos un Mensaje
|
||||
</h2>
|
||||
<p className="text-xl text-[var(--text-secondary)]">
|
||||
Completa el formulario y te responderemos lo antes posible
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<form onSubmit={handleSubmit} className="bg-[var(--bg-primary)] rounded-2xl p-8 border border-[var(--border-primary)]">
|
||||
{/* Success/Error Messages */}
|
||||
{submitStatus === 'success' && (
|
||||
<div className="mb-6 p-4 bg-green-50 dark:bg-green-900/20 border border-green-200 dark:border-green-800 rounded-xl flex items-center gap-3">
|
||||
<CheckCircle2 className="w-5 h-5 text-green-600 flex-shrink-0" />
|
||||
<p className="text-sm text-green-800 dark:text-green-200">
|
||||
<strong>¡Mensaje enviado!</strong> Te responderemos pronto.
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{submitStatus === 'error' && (
|
||||
<div className="mb-6 p-4 bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800 rounded-xl flex items-center gap-3">
|
||||
<AlertCircle className="w-5 h-5 text-red-600 flex-shrink-0" />
|
||||
<p className="text-sm text-red-800 dark:text-red-200">
|
||||
<strong>Error al enviar.</strong> Por favor, inténtalo de nuevo.
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="grid md:grid-cols-2 gap-6">
|
||||
{/* Name */}
|
||||
<div>
|
||||
<label htmlFor="name" className="block text-sm font-medium text-[var(--text-primary)] mb-2">
|
||||
Nombre Completo <span className="text-red-500">*</span>
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
id="name"
|
||||
name="name"
|
||||
value={formState.name}
|
||||
onChange={handleChange}
|
||||
required
|
||||
className="w-full px-4 py-3 bg-[var(--bg-secondary)] border border-[var(--border-primary)] rounded-xl text-[var(--text-primary)] placeholder:text-[var(--text-tertiary)] focus:outline-none focus:border-[var(--color-primary)] transition-colors"
|
||||
placeholder="Tu nombre"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Email */}
|
||||
<div>
|
||||
<label htmlFor="email" className="block text-sm font-medium text-[var(--text-primary)] mb-2">
|
||||
Email <span className="text-red-500">*</span>
|
||||
</label>
|
||||
<input
|
||||
type="email"
|
||||
id="email"
|
||||
name="email"
|
||||
value={formState.email}
|
||||
onChange={handleChange}
|
||||
required
|
||||
className="w-full px-4 py-3 bg-[var(--bg-secondary)] border border-[var(--border-primary)] rounded-xl text-[var(--text-primary)] placeholder:text-[var(--text-tertiary)] focus:outline-none focus:border-[var(--color-primary)] transition-colors"
|
||||
placeholder="tu@email.com"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Phone */}
|
||||
<div>
|
||||
<label htmlFor="phone" className="block text-sm font-medium text-[var(--text-primary)] mb-2">
|
||||
Teléfono (opcional)
|
||||
</label>
|
||||
<input
|
||||
type="tel"
|
||||
id="phone"
|
||||
name="phone"
|
||||
value={formState.phone}
|
||||
onChange={handleChange}
|
||||
className="w-full px-4 py-3 bg-[var(--bg-secondary)] border border-[var(--border-primary)] rounded-xl text-[var(--text-primary)] placeholder:text-[var(--text-tertiary)] focus:outline-none focus:border-[var(--color-primary)] transition-colors"
|
||||
placeholder="+34 XXX XXX XXX"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Bakery Name */}
|
||||
<div>
|
||||
<label htmlFor="bakeryName" className="block text-sm font-medium text-[var(--text-primary)] mb-2">
|
||||
Nombre de tu Panadería (opcional)
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
id="bakeryName"
|
||||
name="bakeryName"
|
||||
value={formState.bakeryName}
|
||||
onChange={handleChange}
|
||||
className="w-full px-4 py-3 bg-[var(--bg-secondary)] border border-[var(--border-primary)] rounded-xl text-[var(--text-primary)] placeholder:text-[var(--text-tertiary)] focus:outline-none focus:border-[var(--color-primary)] transition-colors"
|
||||
placeholder="Panadería Ejemplo"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Type */}
|
||||
<div>
|
||||
<label htmlFor="type" className="block text-sm font-medium text-[var(--text-primary)] mb-2">
|
||||
Tipo de Consulta <span className="text-red-500">*</span>
|
||||
</label>
|
||||
<select
|
||||
id="type"
|
||||
name="type"
|
||||
value={formState.type}
|
||||
onChange={handleChange}
|
||||
required
|
||||
className="w-full px-4 py-3 bg-[var(--bg-secondary)] border border-[var(--border-primary)] rounded-xl text-[var(--text-primary)] focus:outline-none focus:border-[var(--color-primary)] transition-colors"
|
||||
>
|
||||
<option value="general">Consulta General</option>
|
||||
<option value="technical">Soporte Técnico</option>
|
||||
<option value="sales">Información Comercial</option>
|
||||
<option value="feedback">Feedback/Sugerencias</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
{/* Subject */}
|
||||
<div>
|
||||
<label htmlFor="subject" className="block text-sm font-medium text-[var(--text-primary)] mb-2">
|
||||
Asunto <span className="text-red-500">*</span>
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
id="subject"
|
||||
name="subject"
|
||||
value={formState.subject}
|
||||
onChange={handleChange}
|
||||
required
|
||||
className="w-full px-4 py-3 bg-[var(--bg-secondary)] border border-[var(--border-primary)] rounded-xl text-[var(--text-primary)] placeholder:text-[var(--text-tertiary)] focus:outline-none focus:border-[var(--color-primary)] transition-colors"
|
||||
placeholder="¿En qué podemos ayudarte?"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Message */}
|
||||
<div className="mt-6">
|
||||
<label htmlFor="message" className="block text-sm font-medium text-[var(--text-primary)] mb-2">
|
||||
Mensaje <span className="text-red-500">*</span>
|
||||
</label>
|
||||
<textarea
|
||||
id="message"
|
||||
name="message"
|
||||
value={formState.message}
|
||||
onChange={handleChange}
|
||||
required
|
||||
rows={6}
|
||||
className="w-full px-4 py-3 bg-[var(--bg-secondary)] border border-[var(--border-primary)] rounded-xl text-[var(--text-primary)] placeholder:text-[var(--text-tertiary)] focus:outline-none focus:border-[var(--color-primary)] transition-colors resize-none"
|
||||
placeholder="Cuéntanos más sobre tu consulta o problema..."
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Submit Button */}
|
||||
<div className="mt-8">
|
||||
<button
|
||||
type="submit"
|
||||
disabled={submitStatus === 'loading'}
|
||||
className="w-full flex items-center justify-center gap-2 px-8 py-4 bg-[var(--color-primary)] text-white rounded-xl font-bold hover:shadow-xl transition-all hover:scale-105 disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:scale-100"
|
||||
>
|
||||
{submitStatus === 'loading' ? (
|
||||
<>
|
||||
<div className="w-5 h-5 border-2 border-white border-t-transparent rounded-full animate-spin" />
|
||||
<span>Enviando...</span>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Send className="w-5 h-5" />
|
||||
<span>Enviar Mensaje</span>
|
||||
</>
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<p className="text-xs text-[var(--text-tertiary)] text-center mt-4">
|
||||
Al enviar este formulario, aceptas nuestra{' '}
|
||||
<a href="/privacy" className="text-[var(--color-primary)] hover:underline">
|
||||
Política de Privacidad
|
||||
</a>
|
||||
</p>
|
||||
</form>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Office Hours */}
|
||||
<section className="py-20 bg-[var(--bg-primary)]">
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div className="grid md:grid-cols-2 gap-8">
|
||||
{/* Hours */}
|
||||
<div className="bg-[var(--bg-secondary)] rounded-2xl p-8 border border-[var(--border-primary)]">
|
||||
<div className="flex items-start gap-4 mb-6">
|
||||
<Clock className="w-6 h-6 text-[var(--color-primary)] flex-shrink-0 mt-1" />
|
||||
<div>
|
||||
<h3 className="text-xl font-bold text-[var(--text-primary)] mb-4">
|
||||
Horarios de Atención
|
||||
</h3>
|
||||
<div className="space-y-3 text-sm text-[var(--text-secondary)]">
|
||||
<div>
|
||||
<strong className="text-[var(--text-primary)]">Chat en Vivo:</strong>
|
||||
<p>Lunes a Viernes: 9:00 - 21:00 CET</p>
|
||||
<p>Sábados: 10:00 - 18:00 CET</p>
|
||||
</div>
|
||||
<div>
|
||||
<strong className="text-[var(--text-primary)]">Email:</strong>
|
||||
<p>24/7 (respuesta en menos de 4 horas en horario laboral)</p>
|
||||
</div>
|
||||
<div>
|
||||
<strong className="text-[var(--text-primary)]">Teléfono:</strong>
|
||||
<p>Lunes a Viernes: 10:00 - 19:00 CET (solo clientes activos)</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* FAQ Link */}
|
||||
<div className="bg-gradient-to-br from-[var(--color-primary)]/10 to-orange-600/10 rounded-2xl p-8 border border-[var(--color-primary)]/20">
|
||||
<div className="flex items-start gap-4">
|
||||
<HelpCircle className="w-6 h-6 text-[var(--color-primary)] flex-shrink-0 mt-1" />
|
||||
<div>
|
||||
<h3 className="text-xl font-bold text-[var(--text-primary)] mb-2">
|
||||
¿Buscas Respuestas Rápidas?
|
||||
</h3>
|
||||
<p className="text-sm text-[var(--text-secondary)] mb-4">
|
||||
Muchas preguntas ya tienen respuesta en nuestro Centro de Ayuda y Documentación
|
||||
</p>
|
||||
<div className="space-y-2">
|
||||
<a
|
||||
href="/help"
|
||||
className="inline-flex items-center gap-2 text-[var(--color-primary)] hover:underline font-medium text-sm"
|
||||
>
|
||||
Ver Centro de Ayuda →
|
||||
</a>
|
||||
<br />
|
||||
<a
|
||||
href="/help/docs"
|
||||
className="inline-flex items-center gap-2 text-[var(--color-primary)] hover:underline font-medium text-sm"
|
||||
>
|
||||
Leer Documentación →
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</PublicLayout>
|
||||
);
|
||||
};
|
||||
|
||||
export default ContactPage;
|
||||
Reference in New Issue
Block a user