Always original
art + design

since 2003

VISIT (6).gif
VISIT (9).gif
VISIT (7).gif
 
 
 

Melbourne’s home

OF ALL THINGS HANDMADE

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur lobortis, neque sit amet dapibus varius, risus ipsum sagittis elit, a venenatis enim metus eu neque. Proin vel ante placerat velit eleifend dignissim blandit nec tortor. Mauris ut tellus lobortis, mattis leo non, laoreet arcu. Nunc nec mi vitae nisi rutrum pretium. Phasellus tincidunt urna augue, imperdiet fermentum erat auctor quis. Etiam ullamcorper nec augue ac tempor. Morbi sed quam vitae justo pharetra tempus.

 
 
workshops-shibori.jpg

SHIBORI WORKSHOP: JAPANESE DYE TECHNIQUES

workshops-photo.jpg

MAKE YOUR OWN LIP BALM

DIY BUSINESS TIPS WITH UNION WINE CO.

LEATHER CARE

 
 
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

{/* 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 }) => (
{listing.title} { e.target.onerror = null; e.target.src = `https://placehold.co/400x200/e2e8f0/64748b?text=No+Image`; }} />

{listing.title}

{listing.description}

by {listing.vendorName}

{listing.links.map((link, index) => ( {link.name} ))}
); // 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

{loading ? (
) : listings.length > 0 ? (
{listings.map(listing => ( ))}
Title Description Actions
{listing.title} {listing.description}
) : (
You don't have any listings yet.
)} )} {/* Custom Modal for alerts */} {showModal && (

Notification

{modalMessage}

)}
); }; // 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...'}

setVendorName(e.target.value)} className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500 transition-colors" required />
); }; // 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'}

setTitle(e.target.value)} className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500" required />
setImageUrl(e.target.value)} className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500" placeholder="e.g., https://example.com/image.jpg" />

Links (Website/Socials)

{links.map((link, index) => (
handleLinkChange(index, 'name', e.target.value)} className="w-1/3 px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500" /> handleLinkChange(index, 'url', e.target.value)} className="w-2/3 px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500" />
))}
{/* Custom Modal for alerts */} {showModal && (

Notification

{modalMessage}

)}
); }; export default App;

Features Overview

 
 

Feature 1

Mauris egestas at nibh nec finibus. Phasellus sodales massa malesuada tellus fringilla, nec bibendum tellus blandit. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae.

Feature 2

In sit amet felis malesuada, feugiat purus eget, varius mi. Quisque congue porttitor ullamcorper. Suspendisse nec congue purus.

Feature 3

Nulla eu pretium massa. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas non leo laoreet, condimentum lorem nec, vulputate massa.