436 lines
17 KiB
TypeScript
436 lines
17 KiB
TypeScript
import React, { useState } from 'react';
|
|
import { useTranslation } from 'react-i18next';
|
|
import { PublicLayout } from '../../components/layout';
|
|
import {
|
|
MessageSquare,
|
|
Heart,
|
|
ThumbsUp,
|
|
ThumbsDown,
|
|
Lightbulb,
|
|
AlertTriangle,
|
|
Send,
|
|
CheckCircle2,
|
|
AlertCircle,
|
|
Star
|
|
} from 'lucide-react';
|
|
|
|
interface FeedbackCategory {
|
|
id: string;
|
|
title: string;
|
|
description: string;
|
|
icon: React.ComponentType<{ className?: string }>;
|
|
color: string;
|
|
}
|
|
|
|
const FeedbackPage: React.FC = () => {
|
|
const { t } = useTranslation();
|
|
const [formState, setFormState] = useState({
|
|
name: '',
|
|
email: '',
|
|
category: 'suggestion' as 'suggestion' | 'bug' | 'feature' | 'praise' | 'complaint',
|
|
title: '',
|
|
description: '',
|
|
rating: 0,
|
|
});
|
|
const [submitStatus, setSubmitStatus] = useState<'idle' | 'loading' | 'success' | 'error'>('idle');
|
|
|
|
const categories: FeedbackCategory[] = [
|
|
{
|
|
id: 'suggestion',
|
|
title: 'Sugerencia',
|
|
description: 'Ideas para mejorar el producto',
|
|
icon: Lightbulb,
|
|
color: 'blue',
|
|
},
|
|
{
|
|
id: 'feature',
|
|
title: 'Nueva Funcionalidad',
|
|
description: 'Solicita una característica nueva',
|
|
icon: Star,
|
|
color: 'purple',
|
|
},
|
|
{
|
|
id: 'bug',
|
|
title: 'Reportar Bug',
|
|
description: 'Algo no funciona como debería',
|
|
icon: AlertTriangle,
|
|
color: 'red',
|
|
},
|
|
{
|
|
id: 'praise',
|
|
title: 'Elogio',
|
|
description: '¡Algo te encanta!',
|
|
icon: Heart,
|
|
color: 'green',
|
|
},
|
|
{
|
|
id: 'complaint',
|
|
title: 'Queja',
|
|
description: 'Algo te molesta o decepciona',
|
|
icon: ThumbsDown,
|
|
color: 'amber',
|
|
},
|
|
];
|
|
|
|
const handleChange = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement>) => {
|
|
setFormState({
|
|
...formState,
|
|
[e.target.name]: e.target.value,
|
|
});
|
|
};
|
|
|
|
const handleRatingChange = (rating: number) => {
|
|
setFormState({
|
|
...formState,
|
|
rating,
|
|
});
|
|
};
|
|
|
|
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('Feedback submitted:', formState);
|
|
|
|
setSubmitStatus('success');
|
|
setTimeout(() => {
|
|
setSubmitStatus('idle');
|
|
setFormState({
|
|
name: '',
|
|
email: '',
|
|
category: 'suggestion',
|
|
title: '',
|
|
description: '',
|
|
rating: 0,
|
|
});
|
|
}, 3000);
|
|
};
|
|
|
|
const getCategoryColor = (color: string) => {
|
|
const colors: Record<string, string> = {
|
|
blue: 'from-blue-500/10 to-blue-600/10 border-blue-500/20',
|
|
purple: 'from-purple-500/10 to-purple-600/10 border-purple-500/20',
|
|
red: 'from-red-500/10 to-red-600/10 border-red-500/20',
|
|
green: 'from-green-500/10 to-green-600/10 border-green-500/20',
|
|
amber: 'from-amber-500/10 to-amber-600/10 border-amber-500/20',
|
|
};
|
|
return colors[color] || colors.blue;
|
|
};
|
|
|
|
const getCategoryIconColor = (color: string) => {
|
|
const colors: Record<string, string> = {
|
|
blue: 'text-blue-600',
|
|
purple: 'text-purple-600',
|
|
red: 'text-red-600',
|
|
green: 'text-green-600',
|
|
amber: 'text-amber-600',
|
|
};
|
|
return colors[color] || colors.blue;
|
|
};
|
|
|
|
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>Tu Opinión Importa</span>
|
|
</div>
|
|
<h1 className="text-4xl lg:text-6xl font-extrabold text-[var(--text-primary)] mb-6">
|
|
Ayúdanos a Mejorar
|
|
<span className="block text-[var(--color-primary)]">Panadería IA</span>
|
|
</h1>
|
|
<p className="text-xl text-[var(--text-secondary)] leading-relaxed mb-8">
|
|
Tu feedback es fundamental para construir el mejor producto para panaderías artesanales
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
{/* Why Feedback Matters */}
|
|
<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">
|
|
¿Por Qué Tu Feedback Es Importante?
|
|
</h2>
|
|
<p className="text-xl text-[var(--text-secondary)]">
|
|
Estamos construyendo esto juntos
|
|
</p>
|
|
</div>
|
|
|
|
<div className="grid md:grid-cols-3 gap-6 mb-16">
|
|
<div className="bg-[var(--bg-secondary)] rounded-2xl p-6 border border-[var(--border-primary)] text-center">
|
|
<ThumbsUp className="w-12 h-12 text-[var(--color-primary)] mx-auto mb-4" />
|
|
<h3 className="text-lg font-bold text-[var(--text-primary)] mb-2">
|
|
Priorizamos Tu Feedback
|
|
</h3>
|
|
<p className="text-sm text-[var(--text-secondary)]">
|
|
Cada sugerencia es revisada y considerada para el roadmap de producto
|
|
</p>
|
|
</div>
|
|
|
|
<div className="bg-[var(--bg-secondary)] rounded-2xl p-6 border border-[var(--border-primary)] text-center">
|
|
<Lightbulb className="w-12 h-12 text-[var(--color-primary)] mx-auto mb-4" />
|
|
<h3 className="text-lg font-bold text-[var(--text-primary)] mb-2">
|
|
Tus Ideas Nos Inspiran
|
|
</h3>
|
|
<p className="text-sm text-[var(--text-secondary)]">
|
|
Las mejores funcionalidades vienen directamente de nuestros usuarios
|
|
</p>
|
|
</div>
|
|
|
|
<div className="bg-[var(--bg-secondary)] rounded-2xl p-6 border border-[var(--border-primary)] text-center">
|
|
<Heart className="w-12 h-12 text-[var(--color-primary)] mx-auto mb-4" />
|
|
<h3 className="text-lg font-bold text-[var(--text-primary)] mb-2">
|
|
Comunidad Activa
|
|
</h3>
|
|
<p className="text-sm text-[var(--text-secondary)]">
|
|
Contribuyes a crear una herramienta que toda la comunidad panadera disfrutará
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
{/* Feedback 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">
|
|
Comparte Tu Feedback
|
|
</h2>
|
|
<p className="text-xl text-[var(--text-secondary)]">
|
|
Queremos escucharte
|
|
</p>
|
|
</div>
|
|
|
|
{/* Category Selection */}
|
|
<div className="mb-8">
|
|
<h3 className="text-lg font-bold text-[var(--text-primary)] mb-4 text-center">
|
|
¿Qué tipo de feedback quieres compartir?
|
|
</h3>
|
|
<div className="grid sm:grid-cols-2 lg:grid-cols-3 gap-4">
|
|
{categories.map((category) => {
|
|
const CategoryIcon = category.icon;
|
|
const isSelected = formState.category === category.id;
|
|
return (
|
|
<button
|
|
key={category.id}
|
|
type="button"
|
|
onClick={() => setFormState({ ...formState, category: category.id as any })}
|
|
className={`bg-gradient-to-br ${getCategoryColor(category.color)} rounded-xl p-6 border-2 transition-all text-left ${
|
|
isSelected
|
|
? 'border-[var(--color-primary)] shadow-lg'
|
|
: 'border-transparent hover:border-[var(--color-primary)]/30'
|
|
}`}
|
|
>
|
|
<CategoryIcon className={`w-8 h-8 ${getCategoryIconColor(category.color)} mb-3`} />
|
|
<h4 className="font-bold text-[var(--text-primary)] mb-1">{category.title}</h4>
|
|
<p className="text-xs text-[var(--text-secondary)]">{category.description}</p>
|
|
</button>
|
|
);
|
|
})}
|
|
</div>
|
|
</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>¡Gracias por tu feedback!</strong> Lo revisaremos 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">
|
|
Tu Nombre <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>
|
|
</div>
|
|
|
|
{/* Rating */}
|
|
{formState.category === 'praise' && (
|
|
<div className="mt-6">
|
|
<label className="block text-sm font-medium text-[var(--text-primary)] mb-3">
|
|
¿Cómo calificarías tu experiencia? <span className="text-red-500">*</span>
|
|
</label>
|
|
<div className="flex gap-2">
|
|
{[1, 2, 3, 4, 5].map((rating) => (
|
|
<button
|
|
key={rating}
|
|
type="button"
|
|
onClick={() => handleRatingChange(rating)}
|
|
className="transition-transform hover:scale-110"
|
|
>
|
|
<Star
|
|
className={`w-10 h-10 ${
|
|
rating <= formState.rating
|
|
? 'fill-yellow-500 text-yellow-500'
|
|
: 'text-[var(--border-primary)]'
|
|
}`}
|
|
/>
|
|
</button>
|
|
))}
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{/* Title */}
|
|
<div className="mt-6">
|
|
<label htmlFor="title" className="block text-sm font-medium text-[var(--text-primary)] mb-2">
|
|
Título <span className="text-red-500">*</span>
|
|
</label>
|
|
<input
|
|
type="text"
|
|
id="title"
|
|
name="title"
|
|
value={formState.title}
|
|
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="Resumen en una línea"
|
|
/>
|
|
</div>
|
|
|
|
{/* Description */}
|
|
<div className="mt-6">
|
|
<label htmlFor="description" className="block text-sm font-medium text-[var(--text-primary)] mb-2">
|
|
Descripción Detallada <span className="text-red-500">*</span>
|
|
</label>
|
|
<textarea
|
|
id="description"
|
|
name="description"
|
|
value={formState.description}
|
|
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={
|
|
formState.category === 'bug'
|
|
? 'Describe el problema: ¿qué esperabas que pasara? ¿qué pasó en su lugar? ¿cómo podemos reproducirlo?'
|
|
: formState.category === 'feature'
|
|
? 'Describe la funcionalidad que te gustaría ver: ¿qué problema resolvería? ¿cómo lo imaginas?'
|
|
: 'Cuéntanos más sobre tu feedback...'
|
|
}
|
|
/>
|
|
</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 Feedback</span>
|
|
</>
|
|
)}
|
|
</button>
|
|
</div>
|
|
|
|
<p className="text-xs text-[var(--text-tertiary)] text-center mt-4">
|
|
Tu feedback nos ayuda a mejorar. ¡Gracias por tomarte el tiempo!
|
|
</p>
|
|
</form>
|
|
</div>
|
|
</section>
|
|
|
|
{/* Other Ways to Connect */}
|
|
<section className="py-20 bg-[var(--bg-primary)]">
|
|
<div className="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8">
|
|
<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 text-center">
|
|
<h3 className="text-2xl font-bold text-[var(--text-primary)] mb-4">
|
|
¿Prefieres Hablar Directamente?
|
|
</h3>
|
|
<p className="text-[var(--text-secondary)] mb-6">
|
|
Estamos disponibles por múltiples canales
|
|
</p>
|
|
<div className="flex flex-wrap justify-center gap-4">
|
|
<a
|
|
href="/help/support"
|
|
className="inline-flex items-center gap-2 px-6 py-3 bg-[var(--color-primary)] text-white rounded-xl font-bold hover:shadow-xl transition-all hover:scale-105"
|
|
>
|
|
<MessageSquare className="w-5 h-5" />
|
|
<span>Contactar Soporte</span>
|
|
</a>
|
|
<a
|
|
href="/help"
|
|
className="inline-flex items-center gap-2 px-6 py-3 border-2 border-[var(--border-primary)] text-[var(--text-primary)] rounded-xl font-medium hover:border-[var(--color-primary)] transition-all"
|
|
>
|
|
Centro de Ayuda
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
</PublicLayout>
|
|
);
|
|
};
|
|
|
|
export default FeedbackPage;
|