import React, { useState, useEffect } from 'react';
import { Calendar, User, CheckCircle2, Utensils, Plus, X, ChevronRight, Info, Loader2 } from 'lucide-react';
import { initializeApp } from 'firebase/app';
import { getAuth, signInWithCustomToken, signInAnonymously, onAuthStateChanged } from 'firebase/auth';
import { getFirestore, doc, setDoc, onSnapshot } from 'firebase/firestore';
const initialEvents = [
{
id: 'may',
date: 'May 3rd',
theme: 'Taco Bar',
details: {
main: 'Ground beef in crockpot',
sides: '12 hard shells, 12 flour tortillas, shredded cheese, lettuce, tomatoes, red onion, salsa, tortilla chips',
dessert: 'Frozen ice cream bars'
},
slots: [
{ id: 'may-1', category: 'Main: Ground Beef', needed: true, volunteer: '', details: '' },
{ id: 'may-2', category: 'Shells: 12 Hard & 12 Flour', needed: true, volunteer: '', details: '' },
{ id: 'may-3', category: 'Toppings: Cheese & Lettuce', needed: true, volunteer: '', details: '' },
{ id: 'may-4', category: 'Toppings: Tomatoes, Onion, Salsa', needed: true, volunteer: '', details: '' },
{ id: 'may-5', category: 'Sides: Tortilla Chips', needed: true, volunteer: '', details: '' },
{ id: 'may-6', category: 'Dessert: Frozen Ice Cream Bars', needed: true, volunteer: '', details: '' },
{ id: 'may-7', category: 'Drinks & Paper Goods', needed: true, volunteer: '', details: '' },
]
},
{
id: 'june',
date: 'June 7th',
theme: 'Build-a-Sandwich',
details: {
main: 'Build-a-Sandwich',
sides: 'Tuna salad, ham, cheese, mayo, mustard, lettuce, tomatoes, 6 white buns, 12 wheat buns',
dessert: 'Sundae Bar: Vanilla ice cream, chocolate/caramel sauce, strawberries, bananas, sprinkles'
},
slots: [
{ id: 'june-1', category: 'Protein: Tuna Salad & Ham', needed: true, volunteer: '', details: '' },
{ id: 'june-2', category: 'Cheese, Mayo & Mustard', needed: true, volunteer: '', details: '' },
{ id: 'june-3', category: 'Veggies: Lettuce & Tomatoes', needed: true, volunteer: '', details: '' },
{ id: 'june-4', category: 'Buns: 6 White, 12 Wheat', needed: true, volunteer: '', details: '' },
{ id: 'june-5', category: 'Sundae Bar: Ice Cream', needed: true, volunteer: '', details: '' },
{ id: 'june-6', category: 'Sundae Bar: Toppings & Sauces', needed: true, volunteer: '', details: '' },
]
},
{
id: 'july',
date: 'July 5th',
theme: 'Summer BBQ Style',
details: {
main: 'Summer BBQ Style',
sides: 'Potato salad, pasta salad, baked beans, 18 quality hot dogs, buns, relish, mustard, onions, pickles, chips',
dessert: 'Watermelon'
},
slots: [
{ id: 'july-1', category: 'Main: 18 Hot Dogs & Buns', needed: true, volunteer: '', details: '' },
{ id: 'july-2', category: 'Side: Potato Salad', needed: true, volunteer: '', details: '' },
{ id: 'july-3', category: 'Side: Pasta Salad', needed: true, volunteer: '', details: '' },
{ id: 'july-4', category: 'Side: Baked Beans', needed: true, volunteer: '', details: '' },
{ id: 'july-5', category: 'Toppings: Relish, Mustard, Onions, Pickles', needed: true, volunteer: '', details: '' },
{ id: 'july-6', category: 'Side: Chips', needed: true, volunteer: '', details: '' },
{ id: 'july-7', category: 'Dessert: Watermelon', needed: true, volunteer: '', details: '' },
]
},
{
id: 'aug',
date: 'August 2nd',
theme: 'NO POTLUCK',
details: {
main: 'Long Weekend Break',
sides: 'N/A',
dessert: 'N/A'
},
slots: []
},
{
id: 'sept',
date: 'September 6th',
theme: 'Traditional Potluck',
details: {
main: 'Bring whatever you want!',
sides: 'General Categories',
dessert: 'General Categories'
},
slots: [
{ id: 'sept-1', category: 'Main Dish (Protein)', needed: true, volunteer: '', details: '' },
{ id: 'sept-2', category: 'Main Dish (Vegetarian)', needed: true, volunteer: '', details: '' },
{ id: 'sept-3', category: 'Salad / Greens', needed: true, volunteer: '', details: '' },
{ id: 'sept-4', category: 'Side Dish / Carbs', needed: true, volunteer: '', details: '' },
{ id: 'sept-5', category: 'Appetizer / Snacks', needed: true, volunteer: '', details: '' },
{ id: 'sept-6', category: 'Dessert', needed: true, volunteer: '', details: '' },
{ id: 'sept-7', category: 'Drinks', needed: true, volunteer: '', details: '' },
]
},
{
id: 'oct',
date: 'October 4th',
theme: 'Pizza',
details: {
main: 'Pizza',
sides: 'Small veggie platter, small fruit platter',
dessert: 'Ice cream with chocolate or caramel sauce (leftover from June)'
},
slots: [
{ id: 'oct-1', category: 'Main: Pizza 1', needed: true, volunteer: '', details: '' },
{ id: 'oct-2', category: 'Main: Pizza 2', needed: true, volunteer: '', details: '' },
{ id: 'oct-3', category: 'Main: Pizza 3', needed: true, volunteer: '', details: '' },
{ id: 'oct-4', category: 'Side: Small Veggie Platter', needed: true, volunteer: '', details: '' },
{ id: 'oct-5', category: 'Side: Small Fruit Platter', needed: true, volunteer: '', details: '' },
{ id: 'oct-6', category: 'Dessert: Ice Cream (From June)', needed: true, volunteer: 'Church Kitchen', details: 'Using leftovers' },
]
},
{
id: 'nov',
date: 'November 1st',
theme: 'Chili',
details: {
main: 'Chili',
sides: 'Assorted rolls, shredded cheddar',
dessert: 'Apple-themed dessert (crisp, pie, or cake) with ice cream'
},
slots: [
{ id: 'nov-1', category: 'Main: Chili (Pot 1)', needed: true, volunteer: '', details: '' },
{ id: 'nov-2', category: 'Main: Chili (Pot 2)', needed: true, volunteer: '', details: '' },
{ id: 'nov-3', category: 'Side: Assorted Rolls', needed: true, volunteer: '', details: '' },
{ id: 'nov-4', category: 'Topping: Shredded Cheddar', needed: true, volunteer: '', details: '' },
{ id: 'nov-5', category: 'Dessert: Apple Dessert', needed: true, volunteer: '', details: '' },
{ id: 'nov-6', category: 'Dessert: Ice Cream', needed: true, volunteer: '', details: '' },
]
},
{
id: 'dec',
date: 'December 6th',
theme: 'Casseroles & Lasagna',
details: {
main: 'Casseroles & Lasagna',
sides: 'Tossed or Caesar salad, garlic bread',
dessert: 'Christmas-themed dessert'
},
slots: [
{ id: 'dec-1', category: 'Main: Casserole', needed: true, volunteer: '', details: '' },
{ id: 'dec-2', category: 'Main: Lasagna', needed: true, volunteer: '', details: '' },
{ id: 'dec-3', category: 'Side: Tossed or Caesar Salad', needed: true, volunteer: '', details: '' },
{ id: 'dec-4', category: 'Side: Garlic Bread', needed: true, volunteer: '', details: '' },
{ id: 'dec-5', category: 'Dessert: Christmas Themed', needed: true, volunteer: '', details: '' },
]
}
];
const firebaseConfig = typeof __firebase_config !== 'undefined' ? JSON.parse(__firebase_config) : {};
const app = initializeApp(firebaseConfig);
const auth = getAuth(app);
const db = getFirestore(app);
const appId = typeof __app_id !== 'undefined' ? __app_id : 'default-app-id';
export default function App() {
const [events, setEvents] = useState(initialEvents);
const [activeModal, setActiveModal] = useState(null); // { eventId, slotId }
const [formData, setFormData] = useState({ name: '', details: '' });
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
const [saving, setSaving] = useState(false);
const [error, setError] = useState(null);
useEffect(() => {
const initAuth = async () => {
try {
if (typeof __initial_auth_token !== 'undefined' && __initial_auth_token) {
await signInWithCustomToken(auth, __initial_auth_token);
} else {
await signInAnonymously(auth);
}
} catch (err) {
console.error('Auth error:', err);
setError('Failed to authenticate.');
setLoading(false);
}
};
initAuth();
const unsubscribe = onAuthStateChanged(auth, setUser);
return () => unsubscribe();
}, []);
useEffect(() => {
if (!user) return;
const eventsDocRef = doc(db, 'artifacts', appId, 'public', 'data', 'potlucks', 'schedule');
const unsubscribe = onSnapshot(
eventsDocRef,
(docSnap) => {
if (docSnap.exists()) {
setEvents(docSnap.data().events);
} else {
// Initialize document if it doesn't exist yet
setDoc(eventsDocRef, { events: initialEvents }).catch(console.error);
}
setLoading(false);
},
(err) => {
console.error('Firestore error:', err);
setError('Failed to load data. Please refresh.');
setLoading(false);
}
);
return () => unsubscribe();
}, [user]);
const handleOpenModal = (eventId, slotId) => {
setActiveModal({ eventId, slotId });
setFormData({ name: '', details: '' });
setError(null);
};
const handleCloseModal = () => {
setActiveModal(null);
setError(null);
};
const handleSubmit = async (e) => {
e.preventDefault();
if (!formData.name.trim() || !user) return;
setSaving(true);
setError(null);
const updatedEvents = events.map(event => {
if (event.id === activeModal.eventId) {
return {
...event,
slots: event.slots.map(slot => {
if (slot.id === activeModal.slotId) {
return {
...slot,
needed: false,
volunteer: formData.name,
details: formData.details
};
}
return slot;
})
};
}
return event;
});
try {
const eventsDocRef = doc(db, 'artifacts', appId, 'public', 'data', 'potlucks', 'schedule');
await setDoc(eventsDocRef, { events: updatedEvents }, { merge: true });
handleCloseModal();
} catch (err) {
console.error("Save error:", err);
setError("Failed to save your sign-up. Please try again.");
} finally {
setSaving(false);
}
};
const getSlotToEdit = () => {
if (!activeModal) return null;
const event = events.find(e => e.id === activeModal.eventId);
return event.slots.find(s => s.id === activeModal.slotId);
};
return (
{/* Header */}
{error && !activeModal && (
{/* Main Content */}
{loading ? (
) : (
{/* Sign Up Modal */}
{activeModal && (
)}
);
}Church Potluck Sign-Up
Pick a date, claim an item, and let us know what you're bringing!
{error}
)}
Loading potluck schedule...
{events.map((event) => (
{/* Event Header */}
{event.date}
{event.id !== 'aug' && (
)}
{/* Event Slots */}
{event.slots.length > 0 ? (
))}
{event.theme}
Main: {event.details.main}
Sides: {event.details.sides}
Dessert: {event.details.dessert}
{event.slots.map((slot) => (
))}
) : (
{slot.category}
{!slot.needed && slot.details && (
{slot.needed ? (
) : (
Claimed by {slot.volunteer}
)}
Enjoy your long weekend! No potluck this month.
)}