import React, { useState, useEffect } from 'react';
import { initializeApp } from 'firebase/app';
import { getAuth, signInWithCustomToken, signInAnonymously } from 'firebase/auth';
import { getFirestore, collection, addDoc, onSnapshot, doc, updateDoc, deleteDoc } from 'firebase/firestore';
// Tailwind CSS is assumed to be available
// For lucide-react icons, we'll use inline SVGs as a fallback for the immersive environment.
const EditIcon = () => (
);
const TrashIcon = () => (
);
const AddIcon = () => (
);
// Global variables provided by the Canvas environment for Firebase
const appId = typeof __app_id !== 'undefined' ? __app_id : 'default-app-id';
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, 'multivendor-app');
const db = getFirestore(app);
const auth = getAuth(app);
// Context for managing state
const AppContext = React.createContext();
// The main application component
const App = () => {
const [activeView, setActiveView] = useState('marketplace'); // 'marketplace' or 'dashboard'
const [userId, setUserId] = useState(null);
const [userProfile, setUserProfile] = useState(null);
const [listings, setListings] = useState([]);
const [loading, setLoading] = useState(true);
// One-time initialization and auth listener
useEffect(() => {
const initFirebase = async () => {
try {
if (initialAuthToken) {
await signInWithCustomToken(auth, initialAuthToken);
} else {
await signInAnonymously(auth);
}
} catch (error) {
console.error('Firebase authentication failed:', error);
}
// Set up auth state change listener
const unsubscribeAuth = auth.onAuthStateChanged(async (user) => {
if (user) {
const currentUserId = user.uid;
setUserId(currentUserId);
console.log(`User authenticated with ID: ${currentUserId}`);
} else {
console.log('User is not authenticated.');
setUserId(null);
}
});
return () => unsubscribeAuth();
};
initFirebase();
}, []);
// Fetch data with onSnapshot listener
useEffect(() => {
// Only proceed if auth is ready
if (!userId) return;
// Fetch all listings for the marketplace view
const listingsCollectionRef = collection(db, `/artifacts/${appId}/public/data/listings`);
const unsubscribeListings = onSnapshot(listingsCollectionRef, (snapshot) => {
const allListings = snapshot.docs.map(doc => ({ id: doc.id, ...doc.data() }));
setListings(allListings);
setLoading(false);
}, (error) => {
console.error('Error fetching listings:', error);
setLoading(false);
});
// Fetch user profile for the dashboard view
const userProfileCollectionRef = collection(db, `/artifacts/${appId}/users/${userId}/profile`);
const unsubscribeProfile = onSnapshot(userProfileCollectionRef, (snapshot) => {
if (snapshot.docs.length > 0) {
setUserProfile({ id: snapshot.docs[0].id, ...snapshot.docs[0].data() });
} else {
setUserProfile(null);
}
}, (error) => {
console.error('Error fetching user profile:', error);
});
return () => {
unsubscribeListings();
unsubscribeProfile();
};
}, [userId]); // Re-run when userId changes
// Function to switch between views
const renderView = () => {
switch (activeView) {
case 'marketplace':
return
;
case 'dashboard':
return
;
default:
return
;
}
};
return (
{/* Header */}
Multi-Vendor Marketplace
setActiveView('marketplace')}
className={`py-2 px-4 rounded-lg font-medium transition-colors ${
activeView === 'marketplace' ? 'bg-indigo-600 text-white' : 'text-gray-700 hover:bg-indigo-50'
}`}
>
Marketplace
setActiveView('dashboard')}
className={`py-2 px-4 rounded-lg font-medium transition-colors ${
activeView === 'dashboard' ? 'bg-indigo-600 text-white' : 'text-gray-700 hover:bg-indigo-50'
}`}
>
Seller Dashboard
{/* Main Content */}
{renderView()}
);
};
// Marketplace View Component
const Marketplace = ({ listings, loading }) => {
if (loading) {
return (
);
}
return (
All Listings
{listings.length > 0 ? (
listings.map(listing => (
))
) : (
No listings found.
)}
);
};
// Single Listing Card Component
const ListingCard = ({ listing }) => (
{ e.target.onerror = null; e.target.src = `https://placehold.co/400x200/e2e8f0/64748b?text=No+Image`; }}
/>
{listing.title}
{listing.description}
by {listing.vendorName}
);
// Dashboard View Component
const Dashboard = ({ userId, userProfile }) => {
const [listings, setListings] = useState([]);
const [isEditing, setIsEditing] = useState(false);
const [currentListing, setCurrentListing] = useState(null);
const [showModal, setShowModal] = useState(false);
const [modalMessage, setModalMessage] = useState('');
const [loading, setLoading] = useState(true);
const { db, appId } = React.useContext(AppContext);
// Fetch listings for the current user
useEffect(() => {
if (!userId || !db) return;
const listingsCollectionRef = collection(db, `/artifacts/${appId}/users/${userId}/listings`);
const unsubscribe = onSnapshot(listingsCollectionRef, (snapshot) => {
const userListings = snapshot.docs.map(doc => ({ id: doc.id, ...doc.data() }));
setListings(userListings);
setLoading(false);
}, (error) => {
console.error('Error fetching user listings:', error);
setLoading(false);
});
return () => unsubscribe();
}, [userId, db, appId]);
const handleEdit = (listing) => {
setCurrentListing(listing);
setIsEditing(true);
};
const handleDelete = async (listingId) => {
if (window.confirm('Are you sure you want to delete this listing?')) {
try {
await deleteDoc(doc(db, `/artifacts/${appId}/users/${userId}/listings`, listingId));
await deleteDoc(doc(db, `/artifacts/${appId}/public/data/listings`, listingId));
setModalMessage('Listing deleted successfully!');
setShowModal(true);
} catch (e) {
console.error('Error deleting listing:', e);
setModalMessage('Error deleting listing.');
setShowModal(true);
}
}
};
const handleAddListing = () => {
setCurrentListing(null);
setIsEditing(true);
};
if (!userProfile) {
return (
)
}
return (
Seller Dashboard
Welcome, {userProfile?.vendorName || 'Seller'}!
Your User ID: {userId || 'Loading...'}
This is your private space to manage your listings.
{isEditing ? (
) : (
<>
Your Listings
Add New Listing
{loading ? (
) : listings.length > 0 ? (
Title
Description
Actions
{listings.map(listing => (
{listing.title}
{listing.description}
handleEdit(listing)}
className="text-indigo-600 hover:text-indigo-900 transition-colors"
title="Edit"
>
handleDelete(listing.id)}
className="text-red-600 hover:text-red-900 transition-colors"
title="Delete"
>
))}
) : (
You don't have any listings yet.
)}
>
)}
{/* Custom Modal for alerts */}
{showModal && (
Notification
{modalMessage}
setShowModal(false)}
className="bg-indigo-600 text-white py-2 px-6 rounded-lg hover:bg-indigo-700 transition-colors"
>
OK
)}
);
};
// Form for creating and editing a user's profile
const UserProfileForm = () => {
const [vendorName, setVendorName] = useState('');
const { db, appId, userId } = React.useContext(AppContext);
const handleSubmit = async (e) => {
e.preventDefault();
if (!userId || !vendorName.trim()) {
alert('Please enter a vendor name.');
return;
}
try {
// Create a user profile with the seller's info
const userProfileCollectionRef = collection(db, `/artifacts/${appId}/users/${userId}/profile`);
await addDoc(userProfileCollectionRef, {
vendorName: vendorName,
userId: userId
});
alert('Profile created successfully!');
} catch (e) {
console.error('Error adding user profile:', e);
alert('Error creating profile. Check console for details.');
}
};
return (
Create Your Vendor Profile
Before you can add listings, you need to create a profile. Your User ID will be associated with this profile: {userId || 'Loading...'}
);
};
// Form for creating and editing listings
const ListingForm = ({ listing, setIsEditing, userProfile }) => {
const [title, setTitle] = useState(listing?.title || '');
const [description, setDescription] = useState(listing?.description || '');
const [imageUrl, setImageUrl] = useState(listing?.imageUrl || '');
const [links, setLinks] = useState(listing?.links || [{ name: '', url: '' }]);
const { db, appId, userId } = React.useContext(AppContext);
const [showModal, setShowModal] = useState(false);
const [modalMessage, setModalMessage] = useState('');
const handleLinkChange = (index, field, value) => {
const newLinks = [...links];
newLinks[index][field] = value;
setLinks(newLinks);
};
const handleAddLink = () => {
setLinks([...links, { name: '', url: '' }]);
};
const handleRemoveLink = (index) => {
const newLinks = links.filter((_, i) => i !== index);
setLinks(newLinks);
};
const handleSubmit = async (e) => {
e.preventDefault();
if (!title.trim() || !description.trim()) {
setModalMessage('Please fill out all required fields.');
setShowModal(true);
return;
}
const listingData = {
title,
description,
imageUrl,
links,
vendorName: userProfile.vendorName,
vendorId: userId
};
try {
if (listing) {
// Update existing listing
const userDocRef = doc(db, `/artifacts/${appId}/users/${userId}/listings`, listing.id);
const publicDocRef = doc(db, `/artifacts/${appId}/public/data/listings`, listing.id);
await updateDoc(userDocRef, listingData);
await updateDoc(publicDocRef, listingData);
setModalMessage('Listing updated successfully!');
} else {
// Add new listing
const userDocRef = collection(db, `/artifacts/${appId}/users/${userId}/listings`);
const newListingRef = await addDoc(userDocRef, listingData);
// Add to public collection with the same ID
const publicDocRef = doc(db, `/artifacts/${appId}/public/data/listings`, newListingRef.id);
await updateDoc(publicDocRef, listingData);
setModalMessage('Listing added successfully!');
}
setIsEditing(false);
setShowModal(true);
} catch (e) {
console.error('Error saving listing:', e);
setModalMessage('Error saving listing. Check console for details.');
setShowModal(true);
}
};
return (
{listing ? 'Edit Listing' : 'Add New Listing'}
{/* Custom Modal for alerts */}
{showModal && (
Notification
{modalMessage}
setShowModal(false)}
className="bg-indigo-600 text-white py-2 px-6 rounded-lg hover:bg-indigo-700 transition-colors"
>
OK
)}
);
};
export default App;