feat(engagement): add Interactive Custom Studio Mood Board, 5-Star Client Reviews showcase, and smart booking prefill

This commit is contained in:
2026-08-30 09:10:18 -07:00
parent 90fa128139
commit e1f03401a8
5 changed files with 719 additions and 21 deletions
+464
View File
@@ -0,0 +1,464 @@
'use client';
import React, { useState } from 'react';
interface PaletteOption {
id: string;
name: string;
colors: string[];
desc: string;
}
interface EventTypeOption {
id: string;
label: string;
icon: string;
}
interface ElementOption {
id: string;
label: string;
icon: string;
tag: string;
}
const eventTypes: EventTypeOption[] = [
{ id: 'wedding', label: 'Wedding & Engagement', icon: '💍' },
{ id: 'milestone_birthday', label: 'Milestone Birthday (30th/50th)', icon: '🎂' },
{ id: 'baby_shower', label: 'Baby Shower / Gender Reveal', icon: '👶' },
{ id: 'themed_party', label: 'Themed / 1st Birthday', icon: '🍓' },
{ id: 'corporate_gala', label: 'Corporate Gala & Brand Launch', icon: '🥂' },
];
const palettes: PaletteOption[] = [
{
id: 'royal_noir',
name: 'Royal Noir Gala',
colors: ['#000000', '#D4AF37', '#990000'],
desc: 'Onyx black marble, reflective chrome gold, and deep crimson.',
},
{
id: 'sweet_berry',
name: 'Berry Sweet Pastel',
colors: ['#E0006F', '#FFB6C1', '#002244'],
desc: 'Strawberry red, blush baby pink, and deep midnight navy.',
},
{
id: 'tuscan_garden',
name: 'Tuscan Sunshine & Flora',
colors: ['#F5C518', '#FFFFFF', '#4A5D4E'],
desc: 'Warm sun yellow, crisp bridal white, and silk eucalyptus greenery.',
},
{
id: 'tropical_neon',
name: 'Tropical Luxe Brights',
colors: ['#FF007F', '#FF6600', '#00F0FF'],
desc: 'Vibrant hot pink, electric orange, and bright turquoise.',
},
{
id: 'celestial_moon',
name: 'Midnight & Gold Moon',
colors: ['#0B1B3D', '#C8A2C8', '#D4AF37'],
desc: 'Royal navy, soft lavender drape, and gold starburst accents.',
},
];
const decorElements: ElementOption[] = [
{ id: 'balloon_cascade', label: 'Organic Balloon Cloud Cascade', icon: '🎈', tag: 'Most Popular' },
{ id: 'arch_panels', label: 'Custom Multi-Arch Wood Panels', icon: '🏛️', tag: 'Signature' },
{ id: 'mosaic_letters', label: 'Giant 3D Balloon Mosaic Letters', icon: '🔤', tag: 'High Impact' },
{ id: 'beverage_station', label: 'Arched Drink & Treat Station', icon: '🍸', tag: 'Interactive' },
{ id: 'floral_accents', label: 'Silk Florals & Botanical Sprigs', icon: '🌹', tag: 'Luxury' },
{ id: 'custom_lettering', label: '3D Gold Script Custom Name Sign', icon: '✨', tag: 'Personalized' },
];
export default function MoodBoardEstimator() {
const [selectedEventType, setSelectedEventType] = useState<string>('milestone_birthday');
const [selectedPalette, setSelectedPalette] = useState<string>('royal_noir');
const [selectedElements, setSelectedElements] = useState<string[]>([
'balloon_cascade',
'arch_panels',
'custom_lettering',
]);
const toggleElement = (id: string) => {
setSelectedElements((prev) =>
prev.includes(id) ? prev.filter((el) => el !== id) : [...prev, id]
);
};
const currentEventType = eventTypes.find((e) => e.id === selectedEventType)?.label || '';
const currentPalette = palettes.find((p) => p.id === selectedPalette);
const selectedElementLabels = decorElements
.filter((e) => selectedElements.includes(e.id))
.map((e) => e.label);
const handleApplyToBooking = () => {
const visionString = `Custom Design Studio Selection:
• Celebration: ${currentEventType}
• Color Palette: ${currentPalette?.name} (${currentPalette?.desc})
• Core Decor Elements: ${selectedElementLabels.join(', ')}`;
// Dispatch custom event to prefill booking form
window.dispatchEvent(
new CustomEvent('prefillWiseHeartedBooking', {
detail: {
eventType: currentEventType,
vision: visionString,
services: selectedElementLabels,
},
})
);
// Smoothly scroll to contact form
const contactSection = document.getElementById('contact');
if (contactSection) {
contactSection.scrollIntoView({ behavior: 'smooth' });
}
};
return (
<section
id="custom-studio"
style={{
padding: 'clamp(70px, 10vw, 110px) 0',
backgroundColor: '#050505',
position: 'relative',
borderBottom: '1px solid var(--border-gold)',
}}
>
<div className="container" style={{ maxWidth: '1120px' }}>
{/* Section Header */}
<div style={{ textAlign: 'center', marginBottom: '45px', padding: '0 8px' }}>
<span
style={{
fontSize: '0.85rem',
fontWeight: 800,
color: 'var(--color-pink-light)',
letterSpacing: '0.15em',
textTransform: 'uppercase',
display: 'block',
marginBottom: '10px',
}}
>
🎨 Interactive Custom Studio
</span>
<h2
style={{
fontSize: 'clamp(2rem, 3.8vw, 2.8rem)',
fontWeight: 800,
marginBottom: '16px',
}}
>
Design Your <span className="gold-gradient-text">Dream Setup</span>
</h2>
<p
style={{
fontSize: '1.05rem',
color: '#94A3B8',
maxWidth: '700px',
margin: '0 auto',
lineHeight: 1.6,
}}
>
Select your celebration type, color palette, and preferred backdrop elements below to generate your personalized setup preview in real time!
</p>
</div>
{/* 3-Step Interactive Grid */}
<div
style={{
display: 'grid',
gridTemplateColumns: 'repeat(auto-fit, minmax(300px, 1fr))',
gap: '24px',
marginBottom: '40px',
}}
>
{/* STEP 1: Celebration Type */}
<div
className="glass-panel"
style={{
padding: '24px 20px',
border: '1px solid rgba(212, 175, 55, 0.3)',
borderRadius: '20px',
}}
>
<div style={{ display: 'flex', alignItems: 'center', gap: '10px', marginBottom: '16px' }}>
<span
style={{
background: 'var(--color-gold-gradient)',
color: '#000',
width: '26px',
height: '26px',
borderRadius: '50%',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
fontWeight: 800,
fontSize: '0.85rem',
}}
>
1
</span>
<h3 style={{ fontSize: '1.15rem', fontWeight: 800, color: '#FFFFFF', margin: 0 }}>
Celebration Type
</h3>
</div>
<div style={{ display: 'flex', flexDirection: 'column', gap: '8px' }}>
{eventTypes.map((et) => {
const isSelected = selectedEventType === et.id;
return (
<button
key={et.id}
type="button"
onClick={() => setSelectedEventType(et.id)}
style={{
display: 'flex',
alignItems: 'center',
gap: '12px',
padding: '12px 14px',
borderRadius: '12px',
background: isSelected ? 'rgba(212, 175, 55, 0.15)' : 'rgba(255, 255, 255, 0.03)',
border: isSelected ? '1.5px solid var(--color-gold)' : '1px solid rgba(255, 255, 255, 0.08)',
color: isSelected ? '#FFFFFF' : '#CBD5E1',
fontSize: '0.92rem',
fontWeight: isSelected ? 700 : 500,
cursor: 'pointer',
textAlign: 'left',
transition: 'all 0.2s ease',
}}
>
<span style={{ fontSize: '1.25rem' }}>{et.icon}</span>
<span>{et.label}</span>
</button>
);
})}
</div>
</div>
{/* STEP 2: Color Palette */}
<div
className="glass-panel"
style={{
padding: '24px 20px',
border: '1px solid rgba(224, 0, 111, 0.3)',
borderRadius: '20px',
}}
>
<div style={{ display: 'flex', alignItems: 'center', gap: '10px', marginBottom: '16px' }}>
<span
style={{
background: 'var(--color-pink-gradient)',
color: '#FFF',
width: '26px',
height: '26px',
borderRadius: '50%',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
fontWeight: 800,
fontSize: '0.85rem',
}}
>
2
</span>
<h3 style={{ fontSize: '1.15rem', fontWeight: 800, color: '#FFFFFF', margin: 0 }}>
Signature Palette
</h3>
</div>
<div style={{ display: 'flex', flexDirection: 'column', gap: '8px' }}>
{palettes.map((p) => {
const isSelected = selectedPalette === p.id;
return (
<button
key={p.id}
type="button"
onClick={() => setSelectedPalette(p.id)}
style={{
display: 'flex',
flexDirection: 'column',
gap: '4px',
padding: '12px 14px',
borderRadius: '12px',
background: isSelected ? 'rgba(224, 0, 111, 0.15)' : 'rgba(255, 255, 255, 0.03)',
border: isSelected ? '1.5px solid var(--color-pink-light)' : '1px solid rgba(255, 255, 255, 0.08)',
cursor: 'pointer',
textAlign: 'left',
transition: 'all 0.2s ease',
}}
>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', width: '100%' }}>
<span style={{ fontSize: '0.92rem', fontWeight: isSelected ? 700 : 600, color: isSelected ? '#FFFFFF' : '#CBD5E1' }}>
{p.name}
</span>
<div style={{ display: 'flex', gap: '4px' }}>
{p.colors.map((c, i) => (
<div
key={i}
style={{
width: '14px',
height: '14px',
borderRadius: '50%',
backgroundColor: c,
border: '1px solid rgba(255,255,255,0.4)',
}}
/>
))}
</div>
</div>
<span style={{ fontSize: '0.78rem', color: '#94A3B8' }}>{p.desc}</span>
</button>
);
})}
</div>
</div>
{/* STEP 3: Decor Elements */}
<div
className="glass-panel"
style={{
padding: '24px 20px',
border: '1px solid rgba(212, 175, 55, 0.3)',
borderRadius: '20px',
}}
>
<div style={{ display: 'flex', alignItems: 'center', gap: '10px', marginBottom: '16px' }}>
<span
style={{
background: 'var(--color-gold-gradient)',
color: '#000',
width: '26px',
height: '26px',
borderRadius: '50%',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
fontWeight: 800,
fontSize: '0.85rem',
}}
>
3
</span>
<h3 style={{ fontSize: '1.15rem', fontWeight: 800, color: '#FFFFFF', margin: 0 }}>
Backdrop & Elements
</h3>
</div>
<div style={{ display: 'flex', flexDirection: 'column', gap: '8px' }}>
{decorElements.map((el) => {
const isSelected = selectedElements.includes(el.id);
return (
<button
key={el.id}
type="button"
onClick={() => toggleElement(el.id)}
style={{
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
padding: '10px 12px',
borderRadius: '12px',
background: isSelected ? 'rgba(212, 175, 55, 0.15)' : 'rgba(255, 255, 255, 0.03)',
border: isSelected ? '1.5px solid var(--color-gold)' : '1px solid rgba(255, 255, 255, 0.08)',
color: isSelected ? '#FFFFFF' : '#CBD5E1',
fontSize: '0.88rem',
fontWeight: isSelected ? 700 : 500,
cursor: 'pointer',
textAlign: 'left',
transition: 'all 0.2s ease',
}}
>
<div style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>
<span>{el.icon}</span>
<span>{el.label}</span>
</div>
<span
style={{
fontSize: '0.72rem',
fontWeight: 700,
color: isSelected ? 'var(--color-gold-light)' : '#64748B',
background: isSelected ? 'rgba(212, 175, 55, 0.2)' : 'transparent',
padding: '2px 6px',
borderRadius: '6px',
}}
>
{isSelected ? '✓ Added' : '+ Add'}
</span>
</button>
);
})}
</div>
</div>
</div>
{/* Live Customized Preview Banner & CTA */}
<div
style={{
background: 'linear-gradient(135deg, rgba(20, 20, 20, 0.95) 0%, rgba(10, 10, 10, 0.98) 100%)',
border: '2px solid var(--color-gold)',
borderRadius: '24px',
padding: '24px 28px',
boxShadow: '0 20px 50px rgba(0,0,0,0.8), 0 0 30px rgba(212, 175, 55, 0.2)',
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
flexWrap: 'wrap',
gap: '20px',
}}
>
<div style={{ flex: '1 1 400px' }}>
<span
style={{
fontSize: '0.8rem',
fontWeight: 800,
color: 'var(--color-pink-light)',
letterSpacing: '0.12em',
textTransform: 'uppercase',
display: 'block',
marginBottom: '6px',
}}
>
YOUR CUSTOM EVENT SPECIFICATION
</span>
<div style={{ fontSize: '1.25rem', fontWeight: 800, color: '#FFFFFF', marginBottom: '8px' }}>
{currentEventType} <span style={{ color: 'var(--color-gold)' }}></span> {currentPalette?.name}
</div>
<div style={{ display: 'flex', flexWrap: 'wrap', gap: '6px', marginTop: '4px' }}>
{selectedElementLabels.map((lbl, idx) => (
<span
key={idx}
style={{
fontSize: '0.78rem',
background: 'rgba(255, 255, 255, 0.08)',
border: '1px solid rgba(255, 255, 255, 0.15)',
padding: '3px 8px',
borderRadius: '8px',
color: '#E2E8F0',
}}
>
{lbl}
</span>
))}
</div>
</div>
<div>
<button
type="button"
onClick={handleApplyToBooking}
className="btn-gold"
style={{
padding: '16px 32px',
fontSize: '1rem',
fontWeight: 800,
cursor: 'pointer',
boxShadow: '0 6px 25px rgba(212, 175, 55, 0.5)',
}}
>
<span></span> Lock In Design & Get Quote
</button>
</div>
</div>
</div>
</section>
);
}