Content is user-generated and unverified.

S.KIM Meal Prep - Complete Wix Implementation Guide

Email: dine@skimprivatedining.com

🎯 What This Does

  • Multi-step modal form for meal prep consultations
  • Automatically sends emails to both the customer AND dine@skimprivatedining.com
  • Creates calendar invites for phone/Zoom calls
  • Stores all data in Wix database
  • Professional design that matches your brand

⚡ 15-Minute Setup Guide

Step 1: Create the Database (2 minutes)

  1. Go to Wix Dashboard → Content Manager
  2. Click Create New Collection
  3. Name it: MealPrepConsultations
  4. Click Start Creating Fields
  5. Add these fields EXACTLY (copy/paste each one):
firstName (Text)
lastName (Text)
email (Text)
phone (Text)
streetAddress (Text)
city (Text)
state (Text)
zipCode (Text)
unit (Text)
householdSize (Number)
location (Text)
mealPlan (Text)
serviceDay (Text)
preparationMethod (Text)
containerPreference (Text)
favoriteRestaurants (Long Text)
favoriteDishes (Long Text)
avoidFoods (Long Text)
dietaryPreferences (Tags)
additionalInfo (Long Text)
enhancements (Tags)
consultationDate (Date)
consultationTime (Text)
communicationMethod (Text)
weeklyMeals (Text)
  1. Set Permissions: Site Member → Create Content

Step 2: Create Backend Code (3 minutes)

  1. In Wix Editor, open Dev Mode (if not already on)
  2. Click { } Code Files in left sidebar
  3. Under Backend, click + New Web Module
  4. Name it: consultationHandler.jsw
  5. Delete any existing code and paste this ENTIRE code:
javascript
// backend/consultationHandler.jsw
import wixData from 'wix-data';
import { sendEmail } from 'wix-email-backend';

// MAIN FUNCTION - Processes the entire consultation form
export async function processConsultation(formData) {
    try {
        // Save to database
        const consultation = await wixData.insert('MealPrepConsultations', {
            ...formData,
            createdDate: new Date(),
            status: 'pending'
        });
        
        // Send emails
        const emailsSent = await sendConsultationEmails(formData);
        
        return {
            success: true,
            consultationId: consultation._id,
            emailsSent: emailsSent
        };
        
    } catch (error) {
        console.error('Consultation processing error:', error);
        return {
            success: false,
            error: error.message
        };
    }
}

// SEND EMAILS TO BOTH CUSTOMER AND CHEF
async function sendConsultationEmails(formData) {
    const chefEmail = 'dine@skimprivatedining.com';
    
    // Format the date and time nicely
    const consultDate = new Date(formData.consultationDate);
    const formattedDate = consultDate.toLocaleDateString('en-US', {
        weekday: 'long',
        year: 'numeric',
        month: 'long',
        day: 'numeric'
    });
    
    // Generate calendar files
    const icsContent = generateCalendarInvite(formData, consultDate);
    
    try {
        // EMAIL TO CUSTOMER
        const customerSubject = `Confirmed: Your S.KIM Meal Prep Consultation - ${formattedDate}`;
        const customerBody = `
<!DOCTYPE html>
<html>
<head>
    <style>
        body { font-family: 'Helvetica Neue', Arial, sans-serif; line-height: 1.6; color: #333; margin: 0; padding: 0; }
        .email-container { max-width: 600px; margin: 0 auto; }
        .header { background: linear-gradient(135deg, #627a6a 0%, #4f6357 100%); color: white; padding: 40px 30px; text-align: center; }
        .header h1 { margin: 0; font-size: 28px; font-weight: 300; letter-spacing: 1px; }
        .content { padding: 40px 30px; background: #ffffff; }
        .greeting { font-size: 20px; color: #627a6a; margin-bottom: 20px; }
        .details-box { background: #f8f9fa; border-left: 4px solid #627a6a; padding: 25px; margin: 30px 0; }
        .details-box h3 { margin-top: 0; color: #627a6a; }
        .detail-item { margin: 12px 0; }
        .detail-label { font-weight: 600; color: #555; }
        .button { display: inline-block; padding: 14px 30px; background: #627a6a; color: white; text-decoration: none; margin: 20px 0; }
        .next-steps { background: #f0f8f0; padding: 25px; margin: 30px 0; border-radius: 5px; }
        .footer { text-align: center; padding: 30px; color: #888; font-size: 14px; background: #f8f9fa; }
    </style>
</head>
<body>
    <div class="email-container">
        <div class="header">
            <h1>Your Consultation is Confirmed!</h1>
        </div>
        
        <div class="content">
            <div class="greeting">Hi ${formData.firstName},</div>
            
            <p>Thank you for choosing S.KIM Meal Prep! I'm looking forward to our conversation about creating your perfect weekly meal plan.</p>
            
            <div class="details-box">
                <h3>📅 Your Consultation Details</h3>
                <div class="detail-item">
                    <span class="detail-label">Date:</span> ${formattedDate}
                </div>
                <div class="detail-item">
                    <span class="detail-label">Time:</span> ${formData.consultationTime} PT
                </div>
                <div class="detail-item">
                    <span class="detail-label">Duration:</span> 15-20 minutes
                </div>
                <div class="detail-item">
                    <span class="detail-label">Method:</span> ${formData.communicationMethod === 'zoom' ? '💻 Zoom Video Call' : '📞 Phone Call'}
                </div>
                ${formData.communicationMethod === 'phone' ? `
                <div class="detail-item">
                    <span class="detail-label">Phone:</span> I'll call you at ${formData.phone}
                </div>
                ` : ''}
            </div>
            
            <div class="next-steps">
                <h3>What Happens Next?</h3>
                <ol>
                    <li><strong>Our Call:</strong> We'll discuss your preferences, dietary needs, and weekly schedule</li>
                    <li><strong>Custom Proposal:</strong> Within 2 hours after our call, you'll receive a detailed proposal</li>
                    <li><strong>Your First Week:</strong> Once approved, we'll schedule your first week of meal prep!</li>
                </ol>
            </div>
            
            <p><strong>Your Selections:</strong></p>
            <ul>
                <li>Household size: ${formData.householdSize} ${formData.householdSize === 1 ? 'person' : 'people'}</li>
                <li>Meal plan: ${formData.weeklyMeals}</li>
                <li>Preferred service day: ${formData.serviceDay}</li>
            </ul>
            
            <p style="margin-top: 30px;">If you need to reschedule, please reply to this email at least 2 hours before our scheduled time.</p>
            
            <p>Looking forward to creating delicious, healthy meals for you!</p>
            
            <p>Best,<br>
            <strong>Chef S.KIM</strong></p>
        </div>
        
        <div class="footer">
            S.KIM Private Dining & Meal Prep<br>
            dine@skimprivatedining.com<br>
            © ${new Date().getFullYear()} All rights reserved
        </div>
    </div>
</body>
</html>
        `;
        
        // EMAIL TO CHEF
        const chefSubject = `NEW Consultation: ${formData.firstName} ${formData.lastName} - ${formattedDate} at ${formData.consultationTime}`;
        const chefBody = `
<!DOCTYPE html>
<html>
<head>
    <style>
        body { font-family: Arial, sans-serif; line-height: 1.5; color: #333; }
        .container { max-width: 700px; margin: 0 auto; padding: 20px; }
        .header { background: #2c3e50; color: white; padding: 20px; margin: -20px -20px 20px; }
        .section { background: #f5f5f5; padding: 20px; margin: 20px 0; border-left: 4px solid #3498db; }
        .label { font-weight: bold; color: #2c3e50; display: inline-block; width: 150px; }
        .value { color: #555; }
        .highlight { background: #fffacd; padding: 15px; margin: 20px 0; border: 1px solid #f0e68c; }
        table { width: 100%; border-collapse: collapse; margin: 20px 0; }
        td { padding: 8px; border-bottom: 1px solid #ddd; }
        .important { color: #e74c3c; font-weight: bold; }
    </style>
</head>
<body>
    <div class="container">
        <div class="header">
            <h2 style="margin: 0;">New Meal Prep Consultation Scheduled</h2>
        </div>
        
        <div class="highlight">
            <h3 style="margin-top: 0;">📞 Consultation Details</h3>
            <table>
                <tr><td class="label">Date:</td><td class="important">${formattedDate}</td></tr>
                <tr><td class="label">Time:</td><td class="important">${formData.consultationTime} PT</td></tr>
                <tr><td class="label">Method:</td><td>${formData.communicationMethod === 'zoom' ? 'Zoom Call' : 'Phone Call'}</td></tr>
                <tr><td class="label">Phone:</td><td>${formData.phone}</td></tr>
            </table>
        </div>
        
        <div class="section">
            <h3>👤 Client Information</h3>
            <table>
                <tr><td class="label">Name:</td><td>${formData.firstName} ${formData.lastName}</td></tr>
                <tr><td class="label">Email:</td><td><a href="mailto:${formData.email}">${formData.email}</a></td></tr>
                <tr><td class="label">Phone:</td><td>${formData.phone}</td></tr>
                <tr><td class="label">Address:</td><td>${formData.streetAddress} ${formData.unit || ''}<br>${formData.city}, ${formData.state} ${formData.zipCode}</td></tr>
            </table>
        </div>
        
        <div class="section">
            <h3>🍽️ Service Preferences</h3>
            <table>
                <tr><td class="label">Household Size:</td><td><strong>${formData.householdSize} ${formData.householdSize === 1 ? 'person' : 'people'}</strong></td></tr>
                <tr><td class="label">Location:</td><td>${formData.location}</td></tr>
                <tr><td class="label">Meal Plan:</td><td><strong>${formData.weeklyMeals}</strong></td></tr>
                <tr><td class="label">Service Day:</td><td>${formData.serviceDay}</td></tr>
                <tr><td class="label">Preparation:</td><td>${formData.preparationMethod}</td></tr>
                <tr><td class="label">Containers:</td><td>${formData.containerPreference}</td></tr>
            </table>
        </div>
        
        <div class="section">
            <h3>🥗 Dietary Information</h3>
            <table>
                <tr><td class="label">Dietary Needs:</td><td>${formData.dietaryPreferences.length > 0 ? formData.dietaryPreferences.join(', ') : 'None specified'}</td></tr>
                <tr><td class="label">Favorite Restaurants:</td><td>${formData.favoriteRestaurants || 'Not specified'}</td></tr>
                <tr><td class="label">Favorite Dishes:</td><td>${formData.favoriteDishes || 'Not specified'}</td></tr>
                <tr><td class="label">Avoid:</td><td class="important">${formData.avoidFoods || 'Nothing specified'}</td></tr>
                <tr><td class="label">Additional Info:</td><td>${formData.additionalInfo || 'None'}</td></tr>
            </table>
        </div>
        
        ${formData.enhancements.length > 0 ? `
        <div class="section">
            <h3>✨ Enhancement Interests</h3>
            <ul>
                ${formData.enhancements.map(e => `<li>${e}</li>`).join('')}
            </ul>
        </div>
        ` : ''}
        
        <div style="background: #e8f5e9; padding: 20px; margin: 20px 0; text-align: center;">
            <p style="margin: 0;"><strong>Calendar invite attached</strong> - Add to your calendar now!</p>
        </div>
    </div>
</body>
</html>
        `;
        
        // Send both emails
        await sendEmail('dine@skimprivatedining.com', {
            subject: customerSubject,
            body: customerBody,
            attachments: [{
                fileName: 'consultation.ics',
                content: Buffer.from(icsContent).toString('base64')
            }]
        });
        
        await sendEmail(chefEmail, {
            subject: chefSubject,
            body: chefBody,
            attachments: [{
                fileName: 'consultation.ics',
                content: Buffer.from(icsContent).toString('base64')
            }]
        });
        
        return true;
    } catch (error) {
        console.error('Email sending error:', error);
        return false;
    }
}

// GENERATE CALENDAR INVITE FILE
function generateCalendarInvite(formData, consultDate) {
    // Parse time
    const [time, period] = formData.consultationTime.split(' ');
    const [hours, minutes] = time.split(':');
    let hour = parseInt(hours);
    
    if (period === 'PM' && hour !== 12) hour += 12;
    if (period === 'AM' && hour === 12) hour = 0;
    
    // Set start time
    const startDate = new Date(consultDate);
    startDate.setHours(hour, parseInt(minutes), 0, 0);
    
    // Set end time (20 minutes later)
    const endDate = new Date(startDate);
    endDate.setMinutes(endDate.getMinutes() + 20);
    
    // Format dates for ICS
    const formatDate = (date) => {
        return date.toISOString().replace(/[-:]/g, '').replace(/\.\d{3}/, '');
    };
    
    const method = formData.communicationMethod === 'zoom' ? 'Zoom Call' : `Phone Call at ${formData.phone}`;
    
    return `BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//S.KIM Meal Prep//EN
METHOD:REQUEST
BEGIN:VEVENT
UID:${Date.now()}@skimprivatedining.com
DTSTAMP:${formatDate(new Date())}
DTSTART:${formatDate(startDate)}
DTEND:${formatDate(endDate)}
SUMMARY:S.KIM Meal Prep Consultation - ${formData.firstName} ${formData.lastName}
DESCRIPTION:Meal prep consultation to discuss preferences and create custom proposal.\\n\\nClient: ${formData.firstName} ${formData.lastName}\\nPhone: ${formData.phone}\\nEmail: ${formData.email}\\nHousehold: ${formData.householdSize} people\\nMeal Plan: ${formData.weeklyMeals}
LOCATION:${method}
ORGANIZER;CN=S.KIM Meal Prep:mailto:dine@skimprivatedining.com
ATTENDEE;CN=${formData.firstName} ${formData.lastName};RSVP=TRUE:mailto:${formData.email}
STATUS:CONFIRMED
END:VEVENT
END:VCALENDAR`;
}

Step 3: Create the Lightbox (5 minutes)

  1. In Wix Editor, click Add → Lightbox
  2. Choose Blank Lightbox
  3. Name it: consultationModal
  4. Set these properties:
    • Width: 900px (desktop), 95% (mobile)
    • Height: Auto
    • Overlay color: Black with 80% opacity

Step 4: Add Lightbox Elements

IMPORTANT: Add these elements in ORDER and give them the EXACT IDs listed:

Header Section:

  • Close Button (ID: closeButton) - Top right X button
  • Progress Bar Container (ID: progressBar) - Strip element
    • Inside, add 5 box elements (IDs: progress1, progress2, progress3, progress4, progress5)

Step Containers (Add 5 containers, initially hidden except Step 1):

  • Container (ID: step1Container) - Visible
  • Container (ID: step2Container) - Hidden
  • Container (ID: step3Container) - Hidden
  • Container (ID: step4Container) - Hidden
  • Container (ID: step5Container) - Hidden

Inside Each Step Container:

Step 1 Elements:

  • Text: "Let's Create Your Perfect Week"
  • Repeater (ID: householdRepeater) - For 1-6+ people selection
  • Repeater (ID: locationRepeater) - For Primary/Vacation selection
  • Repeater (ID: mealPlanRepeater) - For meal plan options
  • Text (ID: portionPreview) - Hidden by default

Step 2 Elements:

  • Text: "Your Service Details"
  • Repeater (ID: serviceDayRepeater) - For day selection
  • Repeater (ID: prepMethodRepeater) - For preparation method
  • Repeater (ID: containerRepeater) - For container preference

Step 3 Elements:

  • Text: "Your Culinary Profile"
  • Input (ID: favoriteRestaurants) - Text input
  • Input (ID: favoriteDishes) - Text input
  • Input (ID: avoidFoods) - Text input
  • Checkbox Group (ID: dietaryCheckboxes) with options:
    • Gluten-free
    • Dairy-free
    • Vegetarian
    • Vegan
    • Pescatarian
    • Keto/Low-carb
    • Paleo
    • Low sodium
    • Nut allergies
    • Shellfish allergies
    • Other allergies
  • Text Input (ID: additionalInfo) - Multi-line

Step 4 Elements:

  • Text: "Enhance Your Week"
  • Checkbox Group (ID: enhancementCheckboxes) with options:
    • Sweet Endings
    • Snack Attack
    • Market Sides
    • Dip Bar
    • Wellness Boost
    • Fresh Baked

Step 5 Elements:

  • Text: "Let's Connect"
  • Input (ID: firstName) - Required
  • Input (ID: lastName) - Required
  • Input (ID: emailInput) - Required, Email type
  • Input (ID: phoneInput) - Required, Phone type
  • Input (ID: streetAddress) - Required
  • Input (ID: cityInput) - Required
  • Dropdown (ID: stateDropdown) - Default: CA
  • Input (ID: zipCode) - Required
  • Input (ID: unitInput) - Optional
  • Date Picker (ID: consultationDate) - Required
  • Repeater (ID: timeSlotRepeater) - Time slots
  • Button Group (ID: communicationButtons) - Phone/Zoom selection

Footer Elements:

  • Button (ID: backButton) - Initially hidden
  • Button (ID: nextButton) - Text: "Continue"

Step 5: Add the Lightbox Code (5 minutes)

  1. Select the lightbox
  2. Click Code panel
  3. Delete any existing code
  4. Paste this ENTIRE code:
javascript
// Lightbox Code - consultationModal
import wixWindow from 'wix-window';
import { processConsultation } from 'backend/consultationHandler';

// Form data storage
let currentStep = 1;
const totalSteps = 5;
let formData = {
    householdSize: 0,
    location: '',
    mealPlan: '',
    serviceDay: '',
    preparationMethod: '',
    containerPreference: '',
    favoriteRestaurants: '',
    favoriteDishes: '',
    avoidFoods: '',
    dietaryPreferences: [],
    additionalInfo: '',
    enhancements: [],
    firstName: '',
    lastName: '',
    email: '',
    phone: '',
    streetAddress: '',
    city: '',
    state: 'CA',
    zipCode: '',
    unit: '',
    consultationDate: null,
    consultationTime: '',
    communicationMethod: '',
    weeklyMeals: ''
};

$w.onReady(function () {
    setupModal();
    setupEventHandlers();
    updateUI();
});

function setupModal() {
    // Configure date picker
    const tomorrow = new Date();
    tomorrow.setDate(tomorrow.getDate() + 1);
    $w('#consultationDate').minDate = tomorrow;
    
    const maxDate = new Date();
    maxDate.setDate(maxDate.getDate() + 14);
    $w('#consultationDate').maxDate = maxDate;
    
    // Setup repeater data
    setupRepeaters();
}

function setupRepeaters() {
    // Household size options
    $w('#householdRepeater').data = [
        { _id: '1', label: '1', sublabel: 'Person', value: 1 },
        { _id: '2', label: '2', sublabel: 'People', value: 2 },
        { _id: '3', label: '3', sublabel: 'People', value: 3 },
        { _id: '4', label: '4', sublabel: 'People', value: 4 },
        { _id: '5', label: '5', sublabel: 'People', value: 5 },
        { _id: '6', label: '6+', sublabel: 'People', value: 6 }
    ];
    
    // Location options
    $w('#locationRepeater').data = [
        { _id: '1', label: 'Primary Residence', value: 'primary' },
        { _id: '2', label: 'Beachfront Vacation Rental', value: 'vacation' }
    ];
    
    // Meal plan options
    $w('#mealPlanRepeater').data = [
        { _id: '1', label: 'Dinner Only', sublabel: '4 different dinners each week', value: 'dinner' },
        { _id: '2', label: 'Lunch & Dinner', sublabel: '4 lunches + 4 dinners each week', value: 'both' },
        { _id: '3', label: 'Lunch Only', sublabel: '4 different lunches each week', value: 'lunch' },
        { _id: '4', label: 'Custom Plan', sublabel: "Let's discuss your needs", value: 'custom' }
    ];
    
    // Service day options
    $w('#serviceDayRepeater').data = [
        { _id: '1', label: 'Sunday', sublabel: 'Start the week prepared', value: 'Sunday' },
        { _id: '2', label: 'Monday', sublabel: 'Fresh week beginning', value: 'Monday' },
        { _id: '3', label: 'Tuesday', sublabel: 'Midweek delivery', value: 'Tuesday' },
        { _id: '4', label: 'Flexible', sublabel: "We'll coordinate weekly", value: 'Flexible' }
    ];
    
    // Preparation method options
    $w('#prepMethodRepeater').data = [
        { _id: '1', label: 'Cook in Your Kitchen', sublabel: 'I prepare everything fresh in your home', value: 'in-home' },
        { _id: '2', label: 'Ready-to-Heat Delivery', sublabel: 'Meals delivered fully prepared', value: 'delivery' },
        { _id: '3', label: "Let's Discuss", sublabel: 'Flexible based on your needs', value: 'flexible' }
    ];
    
    // Container options
    $w('#containerRepeater').data = [
        { _id: '1', label: 'Use Your Containers', sublabel: 'Eco-friendly option', value: 'client-containers' },
        { _id: '2', label: 'I Provide Containers', sublabel: 'BPA-free, reusable containers', value: 'chef-containers' }
    ];
    
    // Time slots
    const timeSlots = [];
    const times = ['7:00 AM', '8:00 AM', '9:00 AM', '10:00 AM', '11:00 AM', '12:00 PM', 
                   '1:00 PM', '2:00 PM', '3:00 PM', '4:00 PM', '5:00 PM', '6:00 PM'];
    times.forEach((time, index) => {
        timeSlots.push({ _id: `time${index}`, label: time, value: time });
    });
    $w('#timeSlotRepeater').data = timeSlots;
}

function setupEventHandlers() {
    // Navigation
    $w('#nextButton').onClick(() => handleNext());
    $w('#backButton').onClick(() => handleBack());
    $w('#closeButton').onClick(() => wixWindow.lightbox.close());
    
    // Step 1 - Household size
    $w('#householdRepeater').onItemReady(($item, itemData) => {
        $item('#optionBox').onClick(() => {
            formData.householdSize = itemData.value;
            selectRepeaterItem('#householdRepeater', itemData._id);
            updatePortionPreview();
        });
    });
    
    // Step 1 - Location
    $w('#locationRepeater').onItemReady(($item, itemData) => {
        $item('#optionBox').onClick(() => {
            formData.location = itemData.value;
            selectRepeaterItem('#locationRepeater', itemData._id);
        });
    });
    
    // Step 1 - Meal plan
    $w('#mealPlanRepeater').onItemReady(($item, itemData) => {
        $item('#optionBox').onClick(() => {
            formData.mealPlan = itemData.value;
            selectRepeaterItem('#mealPlanRepeater', itemData._id);
            updatePortionPreview();
        });
    });
    
    // Step 2 - Service day
    $w('#serviceDayRepeater').onItemReady(($item, itemData) => {
        $item('#optionBox').onClick(() => {
            formData.serviceDay = itemData.value;
            selectRepeaterItem('#serviceDayRepeater', itemData._id);
        });
    });
    
    // Step 2 - Prep method
    $w('#prepMethodRepeater').onItemReady(($item, itemData) => {
        $item('#optionBox').onClick(() => {
            formData.preparationMethod = itemData.value;
            selectRepeaterItem('#prepMethodRepeater', itemData._id);
        });
    });
    
    // Step 2 - Containers
    $w('#containerRepeater').onItemReady(($item, itemData) => {
        $item('#optionBox').onClick(() => {
            formData.containerPreference = itemData.value;
            selectRepeaterItem('#containerRepeater', itemData._id);
        });
    });
    
    // Step 5 - Time slots
    $w('#timeSlotRepeater').onItemReady(($item, itemData) => {
        $item('#timeBox').onClick(() => {
            formData.consultationTime = itemData.value;
            selectRepeaterItem('#timeSlotRepeater', itemData._id);
        });
    });
    
    // Communication method
    if ($w('#phoneButton')) {
        $w('#phoneButton').onClick(() => {
            formData.communicationMethod = 'phone';
            $w('#phoneButton').style.backgroundColor = '#627a6a';
            $w('#zoomButton').style.backgroundColor = '#ffffff';
        });
    }
    
    if ($w('#zoomButton')) {
        $w('#zoomButton').onClick(() => {
            formData.communicationMethod = 'zoom';
            $w('#zoomButton').style.backgroundColor = '#627a6a';
            $w('#phoneButton').style.backgroundColor = '#ffffff';
        });
    }
}

function selectRepeaterItem(repeaterId, selectedId) {
    const repeater = $w(repeaterId);
    repeater.forEachItem(($item, itemData) => {
        if (itemData._id === selectedId) {
            $item('#optionBox').style.backgroundColor = '#f5f9f5';
            $item('#optionBox').style.borderColor = '#627a6a';
        } else {
            $item('#optionBox').style.backgroundColor = '#ffffff';
            $item('#optionBox').style.borderColor = '#e8e8e8';
        }
    });
}

function updatePortionPreview() {
    if (formData.householdSize > 0 && formData.mealPlan) {
        let weeklyMeals = '';
        const size = formData.householdSize;
        
        switch(formData.mealPlan) {
            case 'dinner':
                weeklyMeals = `${size * 4} dinner portions weekly (4 dinners × ${size} ${size === 1 ? 'person' : 'people'})`;
                break;
            case 'both':
                weeklyMeals = `${size * 8} total portions weekly (4 lunches + 4 dinners × ${size} ${size === 1 ? 'person' : 'people'})`;
                break;
            case 'lunch':
                weeklyMeals = `${size * 4} lunch portions weekly (4 lunches × ${size} ${size === 1 ? 'person' : 'people'})`;
                break;
            case 'custom':
                weeklyMeals = 'Custom meal plan - details to be discussed';
                break;
        }
        
        formData.weeklyMeals = weeklyMeals;
        $w('#portionPreview').text = `Your selection: ${weeklyMeals}`;
        $w('#portionPreview').show();
    }
}

async function handleNext() {
    if (currentStep < totalSteps) {
        if (validateCurrentStep()) {
            currentStep++;
            updateUI();
        }
    } else {
        await submitForm();
    }
}

function handleBack() {
    if (currentStep > 1) {
        currentStep--;
        updateUI();
    }
}

function validateCurrentStep() {
    switch(currentStep) {
        case 1:
            if (!formData.householdSize || !formData.location || !formData.mealPlan) {
                showError('Please make all selections before continuing');
                return false;
            }
            break;
        case 2:
            if (!formData.serviceDay || !formData.preparationMethod || !formData.containerPreference) {
                showError('Please make all selections before continuing');
                return false;
            }
            break;
        case 5:
            // Collect form data before validation
            collectStep5Data();
            if (!formData.firstName || !formData.lastName || !formData.email || 
                !formData.phone || !formData.streetAddress || !formData.city || 
                !formData.zipCode || !formData.consultationDate || 
                !formData.consultationTime || !formData.communicationMethod) {
                showError('Please fill in all required fields');
                return false;
            }
            break;
    }
    return true;
}

function collectStep3Data() {
    formData.favoriteRestaurants = $w('#favoriteRestaurants').value || '';
    formData.favoriteDishes = $w('#favoriteDishes').value || '';
    formData.avoidFoods = $w('#avoidFoods').value || '';
    formData.additionalInfo = $w('#additionalInfo').value || '';
    formData.dietaryPreferences = $w('#dietaryCheckboxes').value || [];
}

function collectStep4Data() {
    formData.enhancements = $w('#enhancementCheckboxes').value || [];
}

function collectStep5Data() {
    formData.firstName = $w('#firstName').value;
    formData.lastName = $w('#lastName').value;
    formData.email = $w('#emailInput').value;
    formData.phone = $w('#phoneInput').value;
    formData.streetAddress = $w('#streetAddress').value;
    formData.city = $w('#cityInput').value;
    formData.state = $w('#stateDropdown').value || 'CA';
    formData.zipCode = $w('#zipCode').value;
    formData.unit = $w('#unitInput').value || '';
    formData.consultationDate = $w('#consultationDate').value;
}

async function submitForm() {
    try {
        // Disable submit button
        $w('#nextButton').disable();
        $w('#nextButton').label = 'Scheduling...';
        
        // Collect all remaining data
        collectStep3Data();
        collectStep4Data();
        collectStep5Data();
        
        // Validate final data
        if (!validateCurrentStep()) {
            $w('#nextButton').enable();
            $w('#nextButton').label = 'Schedule Consultation';
            return;
        }
        
        // Process consultation
        const result = await processConsultation(formData);
        
        if (result.success) {
            // Show success message
            $w('#successMessage').show();
            $w('#step5Container').hide();
            $w('#nextButton').hide();
            $w('#backButton').hide();
            
            // Close after 3 seconds
            setTimeout(() => {
                wixWindow.lightbox.close();
            }, 3000);
        } else {
            showError('There was an error scheduling your consultation. Please try again.');
            $w('#nextButton').enable();
            $w('#nextButton').label = 'Schedule Consultation';
        }
        
    } catch (error) {
        console.error('Submission error:', error);
        showError('There was an error. Please try again or contact us directly.');
        $w('#nextButton').enable();
        $w('#nextButton').label = 'Schedule Consultation';
    }
}

function updateUI() {
    // Hide all steps
    for (let i = 1; i <= totalSteps; i++) {
        $w(`#step${i}Container`).hide();
    }
    
    // Show current step
    $w(`#step${currentStep}Container`).show();
    
    // Update progress bar
    for (let i = 1; i <= totalSteps; i++) {
        const progressElement = $w(`#progress${i}`);
        if (i <= currentStep) {
            progressElement.style.backgroundColor = '#627a6a';
        } else {
            progressElement.style.backgroundColor = '#e8e8e8';
        }
    }
    
    // Update buttons
    if (currentStep === 1) {
        $w('#backButton').hide();
    } else {
        $w('#backButton').show();
    }
    
    if (currentStep === totalSteps) {
        $w('#nextButton').label = 'Schedule Consultation';
    } else {
        $w('#nextButton').label = 'Continue';
    }
    
    // Clear any error messages
    if ($w('#errorMessage')) {
        $w('#errorMessage').hide();
    }
}

function showError(message) {
    if ($w('#errorMessage')) {
        $w('#errorMessage').text = message;
        $w('#errorMessage').show();
    } else {
        // If no error element, use console
        console.error(message);
    }
}

Step 6: Add Success Message (30 seconds)

  1. In the lightbox, add a Container (ID: successMessage)
  2. Initially hidden
  3. Inside add:
    • Text: "✅ Consultation Scheduled!"
    • Text: "Check your email for confirmation. We'll speak soon!"

Step 7: Connect Your Button (30 seconds)

On your main page, add this code to open the modal:

javascript
// Page code where your button is
import wixWindow from 'wix-window';

$w.onReady(function () {
    $w('#consultButton').onClick(() => {
        wixWindow.openLightbox('consultationModal');
    });
});

✅ That's It! You're Done!

What Happens Now:

  1. User fills out the form → Data saves to your database
  2. Customer gets a beautiful confirmation email with calendar invite
  3. You get a detailed email at dine@skimprivatedining.com with all their info
  4. Both emails include .ics calendar files that work with all calendar apps

Testing Checklist:

  • Fill out form completely and submit
  • Check both email addresses received emails
  • Verify calendar files open correctly
  • Check database has the submission
  • Test on mobile device

Need Help?

  • Make sure all element IDs match EXACTLY as listed
  • Check browser console for any errors
  • Ensure database permissions allow member creation
  • Verify email sending is enabled in Wix

🎉 Congratulations! Your sophisticated meal prep consultation system is ready!

Content is user-generated and unverified.
    S.KIM Complete Wix Implementation - Ready to Copy & Paste | Claude