Files
bakery-ia/frontend/src/pages/public/ContactPage.tsx
Claude 7741dd8067 Fix frontend build TypeScript errors
Fixed multiple TypeScript type errors that were preventing the build from working properly:

1. Fixed infinite query type issue in forecasting.ts by excluding 'select' from options
2. Fixed Card variant type errors by changing contentPadding="default" to contentPadding="md"
3. Fixed router export issues by removing non-existent exports (ROUTE_CONFIGS, getRoutesForRole, etc.)
4. Fixed router readonly array type issues by updating RouteConfig interface
5. Fixed ProtectedRoute requiredRoles prop issue by removing invalid prop usage
6. Fixed auth store User type compatibility by allowing null for tenant_id
7. Fixed missing useToasts export from ui.store by removing from exports
8. Fixed permissions utility boolean type issues by wrapping expressions in Boolean()

The frontend build now completes successfully.
2025-11-06 18:39:20 +00:00

445 lines
18 KiB
TypeScript

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="md"
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;