import React, { useState, useEffect, createContext, useContext } from 'react'; import { initializeApp } from 'firebase/app'; import { getAuth, signInWithCustomToken, signInAnonymously, onAuthStateChanged } from 'firebase/auth'; import { getFirestore, collection, query, onSnapshot, addDoc, updateDoc, deleteDoc, doc } from 'firebase/firestore'; import { X } from 'lucide-react'; // --- Shadcn/ui Component Reimplementations --- // Since shadcn/ui imports don't work directly, we'll create our own simple // versions of the necessary components using Tailwind CSS. // Button component const Button = React.forwardRef(({ className, variant, size, ...props }, ref) => { const baseClass = 'inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:opacity-50 disabled:pointer-events-none'; const variantClass = variant === 'ghost' ? 'hover:bg-gray-100 hover:text-gray-900' : 'bg-blue-600 text-white hover:bg-blue-700'; const sizeClass = size === 'icon' ? 'h-10 w-10 p-0' : 'h-10 py-2 px-4'; const finalClassName = `${baseClass} ${variantClass} ${sizeClass} ${className || ''}`.trim(); return ( )); Checkbox.displayName = 'Checkbox'; // Toast Context for notifications const ToastContext = createContext(null); const useToast = () => { const context = useContext(ToastContext); if (!context) { throw new Error('useToast must be used within a ToastProvider'); } return context; }; // Simple Toaster and Toast component const Toaster = ({ position = 'top-center' }) => { const { toasts, removeToast } = useContext(ToastContext); const getPositionClasses = (pos) => { switch (pos) { case 'top-center': default: return 'top-4 left-1/2 -translate-x-1/2'; } }; return (
{toasts.map((toast) => (

{toast.title}

{toast.description &&

{toast.description}

}
))}
); }; const ToastProvider = ({ children }) => { const [toasts, setToasts] = useState([]); const toast = ({ title, description }) => { const id = Date.now(); setToasts((prevToasts) => [...prevToasts, { id, title, description }]); setTimeout(() => removeToast(id), 5000); }; const removeToast = (id) => { setToasts((prevToasts) => prevToasts.filter((t) => t.id !== id)); }; return ( {children} ); }; // The main application component that contains all the UI and logic. // We've renamed it to ToDoListApp to allow the App component to be a wrapper. function ToDoListApp() { const [todos, setTodos] = useState([]); const [newTodoText, setNewTodoText] = useState(''); const [isLoading, setIsLoading] = useState(true); const [db, setDb] = useState(null); const [userId, setUserId] = useState(null); const { toast } = useToast(); // Initialize Firebase and set up the real-time listener for the todos. useEffect(() => { // These global variables are provided by the Canvas environment. const firebaseConfig = typeof __firebase_config !== 'undefined' ? JSON.parse(__firebase_config) : {}; const initialAuthToken = typeof __initial_auth_token !== 'undefined' ? __initial_auth_token : null; // Initialize Firebase app const app = initializeApp(firebaseConfig); const firestore = getFirestore(app); const firebaseAuth = getAuth(app); setDb(firestore); // Sign in the user using the custom token or anonymously if the token is not available. const signIn = async () => { try { if (initialAuthToken) { await signInWithCustomToken(firebaseAuth, initialAuthToken); } else { await signInAnonymously(firebaseAuth); } } catch (error) { console.error("Firebase authentication error:", error); } }; signIn(); // Set up a listener for authentication state changes. // This ensures we have a valid user ID before attempting to fetch data. const unsubscribeAuth = onAuthStateChanged(firebaseAuth, (user) => { if (user) { setUserId(user.uid); } else { setUserId(null); } }); // Clean up the auth listener when the component unmounts. return () => unsubscribeAuth(); }, []); // Set up the Firestore listener to get real-time updates. useEffect(() => { if (!db || !userId) { // Don't proceed until Firebase is initialized and the user is authenticated. return; } setIsLoading(true); // This is the path for the public data collection shared among users. const collectionPath = `artifacts/${typeof __app_id !== 'undefined' ? __app_id : 'default-app-id'}/public/data/todos`; const q = query(collection(db, collectionPath)); // The onSnapshot listener provides real-time updates. // It's the most efficient way to handle collaborative data. const unsubscribeSnapshot = onSnapshot(q, (querySnapshot) => { try { const todosArray = []; querySnapshot.forEach((doc) => { todosArray.push({ id: doc.id, ...doc.data() }); }); // Sort the todos by their timestamp to maintain a consistent order. todosArray.sort((a, b) => (a.timestamp?.seconds || 0) - (b.timestamp?.seconds || 0)); setTodos(todosArray); } catch (error) { console.error("Error fetching todos:", error); } finally { setIsLoading(false); } }, (error) => { console.error("Firestore onSnapshot error:", error); setIsLoading(false); }); // Clean up the Firestore listener when the component unmounts. return () => unsubscribeSnapshot(); }, [db, userId]); // Re-run this effect if the database or user ID changes. // Function to add a new to-do item to Firestore. const addTodo = async () => { if (newTodoText.trim() === '' || !db) { toast({ title: "Input is empty!", description: "Please enter a to-do item.", }); return; } try { // Add a new document to the todos collection. await addDoc(collection(db, `artifacts/${typeof __app_id !== 'undefined' ? __app_id : 'default-app-id'}/public/data/todos`), { text: newTodoText, completed: false, timestamp: new Date(), }); setNewTodoText(''); } catch (error) { console.error("Error adding document:", error); } }; // Function to toggle the 'completed' status of a to-do item. const toggleTodoCompletion = async (id, completed) => { if (!db) return; try { const todoDocRef = doc(db, `artifacts/${typeof __app_id !== 'undefined' ? __app_id : 'default-app-id'}/public/data/todos`, id); // Update the document with the new 'completed' status. await updateDoc(todoDocRef, { completed: !completed, }); } catch (error) { console.error("Error updating document:", error); } }; // Function to delete a to-do item from Firestore. const deleteTodo = async (id) => { if (!db) return; try { const todoDocRef = doc(db, `artifacts/${typeof __app_id !== 'undefined' ? __app_id : 'default-app-id'}/public/data/todos`, id); // Delete the document. await deleteDoc(todoDocRef); } catch (error) { console.error("Error deleting document:", error); } }; return (

Family To-Do List

setNewTodoText(e.target.value)} onKeyDown={(e) => e.key === 'Enter' && addTodo()} className="flex-1 rounded-lg border-gray-300 focus:ring-2 focus:ring-blue-500 focus:border-transparent transition-all" />
{todos.filter(todo => !todo.completed).length} items remaining User ID: {userId || 'Loading...'}
{isLoading ? (
) : (
{todos.map((todo) => (
toggleTodoCompletion(todo.id, todo.completed)} id={`todo-${todo.id}`} className="peer h-5 w-5 rounded-sm border-2 border-gray-300 data-[state=checked]:bg-blue-600 data-[state=checked]:border-blue-600 transition-colors" />
))} {todos.length === 0 && (
You have no tasks! Start by adding one above.
)}
)}
); } // The root component that wraps the main application in a context provider. export default function App() { return ( ); }