update lang

This commit is contained in:
Your Name committed 2026-06-14 19:51:19 -04:00
1 parent 84a9b5c603
commit f0f714e8e2
8 files changed
+357 -25

No files matched your search

@@ -12,6 +12,9 @@ import DynamicHeroArticle from './DynamicHeroArticle';
import DynamicScentNotesBreakdown from './DynamicScentNotesBreakdown';
import DynamicQuoteCallout from './DynamicQuoteCallout';
import DynamicProductCarousel from './DynamicProductCarousel';
import AccordionSection from './sections/AccordionSection';
import ContactInfoSection from './sections/ContactInfoSection';
import ContactFormSection from './sections/ContactFormSection';
interface DynamicPageRendererProps {
sections: PageSection[];
@@ -63,6 +66,12 @@ export default function DynamicPageRenderer({
return <DynamicQuoteCallout key={section.id} payload={section.payload} />;
case 'PRODUCT_CAROUSEL':
return <DynamicProductCarousel key={section.id} payload={section.payload} />;
case 'ACCORDION':
return <AccordionSection key={section.id} payload={section.payload as any} />;
case 'CONTACT_INFO':
return <ContactInfoSection key={section.id} payload={section.payload as any} />;
case 'CONTACT_FORM':
return <ContactFormSection key={section.id} payload={section.payload as any} />;
default:
console.warn(`[DynamicPageRenderer] Unknown block type: ${section.type}`);
return null;
@@ -0,0 +1,64 @@
import React, { useState } from 'react';
import type { AccordionSectionPayload } from '@homaroma/types';
import { ChevronDown, ChevronUp } from 'lucide-react';
interface AccordionSectionProps {
payload: AccordionSectionPayload;
}
export default function AccordionSection({ payload }: AccordionSectionProps) {
const [openIndex, setOpenIndex] = useState<string | null>(null);
const toggleAccordion = (index: string) => {
setOpenIndex(openIndex === index ? null : index);
};
return (
<section className="w-full py-16 px-4 md:px-8 bg-spa-sand/20">
<div className="max-w-4xl mx-auto">
{payload.categories.map((category, catIndex) => (
<div key={catIndex} className="mb-12 last:mb-0">
<h3 className="text-2xl font-serif text-spa-charcoal mb-6 flex items-center gap-3">
{category.icon && <span className="text-spa-sage">{category.icon}</span>}
{category.name}
</h3>
<div className="flex flex-col gap-4">
{category.questions.map((item, qIndex) => {
const indexKey = `${catIndex}-${qIndex}`;
const isOpen = openIndex === indexKey;
return (
<div
key={qIndex}
className="border border-spa-sage/30 rounded-xl bg-white overflow-hidden transition-all duration-300"
>
<button
type="button"
className="w-full flex items-center justify-between p-5 text-left focus:outline-none focus:ring-2 focus:ring-spa-sage/50"
onClick={() => toggleAccordion(indexKey)}
>
<span className="text-lg font-medium text-spa-charcoal pr-4">
{item.q}
</span>
<span className="text-spa-sage flex-shrink-0">
{isOpen ? <ChevronUp size={20} /> : <ChevronDown size={20} />}
</span>
</button>
<div
className={`px-5 overflow-hidden transition-all duration-300 ${
isOpen ? 'max-h-96 pb-5 opacity-100' : 'max-h-0 opacity-0'
}`}
>
<p className="text-spa-charcoal/80 leading-relaxed whitespace-pre-wrap">
{item.a}
</p>
</div>
</div>
);
})}
</div>
</div>
))}
</div>
</section>
);
}
@@ -0,0 +1,130 @@
import React, { useState } from 'react';
import type { ContactFormSectionPayload } from '@homaroma/types';
import { Send } from 'lucide-react';
interface ContactFormSectionProps {
payload: ContactFormSectionPayload;
}
export default function ContactFormSection({ payload }: ContactFormSectionProps) {
const [formData, setFormData] = useState({
name: '',
email: '',
message: ''
});
const [isSubmitting, setIsSubmitting] = useState(false);
const [isSuccess, setIsSuccess] = useState(false);
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
setIsSubmitting(true);
// Simulate API call
setTimeout(() => {
setIsSubmitting(false);
setIsSuccess(true);
setFormData({ name: '', email: '', message: '' });
// Reset success message after 3 seconds
setTimeout(() => setIsSuccess(false), 3000);
}, 1000);
};
const handleChange = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
const { name, value } = e.target;
setFormData(prev => ({ ...prev, [name]: value }));
};
return (
<section className="w-full py-20 px-4 md:px-8 bg-spa-sand/30">
<div className="max-w-3xl mx-auto">
<div className="text-center mb-12">
<h2 className="text-3xl md:text-4xl font-serif text-spa-charcoal mb-4">
{payload.title || "ติดต่อเรา"}
</h2>
<p className="text-lg text-spa-charcoal/70 max-w-xl mx-auto">
{payload.subtitle || "ส่งข้อความถึงทีมงานของเรา เรายินดีให้บริการและพร้อมตอบทุกข้อสงสัย"}
</p>
</div>
<div className="bg-white rounded-3xl shadow-xl shadow-spa-sand/50 p-8 md:p-12 border border-spa-sage/10">
{isSuccess ? (
<div className="text-center py-12">
<div className="w-16 h-16 bg-green-100 text-green-600 rounded-full flex items-center justify-center mx-auto mb-6">
<Send size={32} />
</div>
<h3 className="text-2xl font-serif text-spa-charcoal mb-2">ส่งข้อความสำเร็จ</h3>
<p className="text-spa-charcoal/70">ขอบคุณที่ติดต่อเรา ทีมงานจะรีบตอบกลับโดยเร็วที่สุด</p>
</div>
) : (
<form onSubmit={handleSubmit} className="space-y-6">
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<div className="space-y-2">
<label htmlFor="name" className="block text-sm font-medium text-spa-charcoal/80">
ชื่อ-นามสกุล
</label>
<input
type="text"
id="name"
name="name"
required
value={formData.name}
onChange={handleChange}
className="w-full px-4 py-3 rounded-xl border border-spa-sage/30 focus:outline-none focus:ring-2 focus:ring-spa-sage/50 bg-spa-sand/5 transition-all"
placeholder="กรอกชื่อของคุณ"
/>
</div>
<div className="space-y-2">
<label htmlFor="email" className="block text-sm font-medium text-spa-charcoal/80">
อีเมล
</label>
<input
type="email"
id="email"
name="email"
required
value={formData.email}
onChange={handleChange}
className="w-full px-4 py-3 rounded-xl border border-spa-sage/30 focus:outline-none focus:ring-2 focus:ring-spa-sage/50 bg-spa-sand/5 transition-all"
placeholder="example@email.com"
/>
</div>
</div>
<div className="space-y-2">
<label htmlFor="message" className="block text-sm font-medium text-spa-charcoal/80">
ข้อความ
</label>
<textarea
id="message"
name="message"
required
value={formData.message}
onChange={handleChange}
rows={5}
className="w-full px-4 py-3 rounded-xl border border-spa-sage/30 focus:outline-none focus:ring-2 focus:ring-spa-sage/50 bg-spa-sand/5 transition-all resize-none"
placeholder="พิมพ์ข้อความของคุณที่นี่..."
/>
</div>
<button
type="submit"
disabled={isSubmitting}
className="w-full py-4 px-6 bg-spa-sage hover:bg-spa-sage-dark text-white rounded-xl font-medium flex items-center justify-center gap-2 transition-colors disabled:opacity-70 disabled:cursor-not-allowed"
>
{isSubmitting ? (
<span className="w-6 h-6 border-2 border-white/30 border-t-white rounded-full animate-spin" />
) : (
<>
<Send size={18} />
ส่งข้อความ
</>
)}
</button>
</form>
)}
</div>
</div>
</section>
);
}
@@ -0,0 +1,59 @@
import React from 'react';
import type { ContactInfoSectionPayload } from '@homaroma/types';
import { MapPin, Mail, MessageCircle, Clock } from 'lucide-react';
interface ContactInfoSectionProps {
payload: ContactInfoSectionPayload;
}
export default function ContactInfoSection({ payload }: ContactInfoSectionProps) {
return (
<section className="w-full py-16 px-4 md:px-8 bg-white">
<div className="max-w-6xl mx-auto">
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-8">
<div className="flex flex-col items-center text-center p-6 bg-spa-sand/10 rounded-2xl border border-spa-sage/20 hover:border-spa-sage transition-colors duration-300">
<div className="w-12 h-12 bg-spa-sage/10 rounded-full flex items-center justify-center text-spa-sage mb-4">
<MapPin size={24} />
</div>
<h4 className="text-lg font-serif text-spa-charcoal mb-2">ที่อยู่</h4>
<p className="text-spa-charcoal/70 leading-relaxed whitespace-pre-wrap">
{payload.address}
</p>
</div>
<div className="flex flex-col items-center text-center p-6 bg-spa-sand/10 rounded-2xl border border-spa-sage/20 hover:border-spa-sage transition-colors duration-300">
<div className="w-12 h-12 bg-spa-sage/10 rounded-full flex items-center justify-center text-spa-sage mb-4">
<Mail size={24} />
</div>
<h4 className="text-lg font-serif text-spa-charcoal mb-2">อีเมล</h4>
<a href={`mailto:${payload.email}`} className="text-spa-charcoal/70 hover:text-spa-sage transition-colors">
{payload.email}
</a>
</div>
<div className="flex flex-col items-center text-center p-6 bg-spa-sand/10 rounded-2xl border border-spa-sage/20 hover:border-spa-sage transition-colors duration-300">
<div className="w-12 h-12 bg-spa-sage/10 rounded-full flex items-center justify-center text-spa-sage mb-4">
<MessageCircle size={24} />
</div>
<h4 className="text-lg font-serif text-spa-charcoal mb-2">Line</h4>
<p className="text-spa-charcoal/70">
{payload.line}
</p>
</div>
<div className="flex flex-col items-center text-center p-6 bg-spa-sand/10 rounded-2xl border border-spa-sage/20 hover:border-spa-sage transition-colors duration-300">
<div className="w-12 h-12 bg-spa-sage/10 rounded-full flex items-center justify-center text-spa-sage mb-4">
<Clock size={24} />
</div>
<h4 className="text-lg font-serif text-spa-charcoal mb-2">เวลาทำการ</h4>
<p className="text-spa-charcoal/70 whitespace-pre-wrap">
{payload.hours}
</p>
</div>
</div>
</div>
</section>
);
}