Hi hier können alle rein schreiben Namensvorschläge * Leafio * (Leaf + modernes “-io”, beliebt bei Webanwendungen) * Leafit * (Leaf +. it lustig aber ist halt ne Italienische Domain) * Plantsy * (klingt wie Etsy, freundlich, leicht merkbar) * * Florami * (abgeleitet von Flora + „ami“ wie Freund, klingt weich und sympathisch) * * Plantza * (Plants + etwas locker, Startup-Feeling, erinnert an schnelle Plattformen) * Vireo * (lateinisch für eine grüne Pflanze, klingt techy) * * Floru * (Flora + modernes, kurzes Ending) * * Sproot * (von „Sprout“ abgeleitet, klingt sympathisch und wachsend) Idee 1: Idee 2: Idee 3: Cloudinary insdex.db im Browser json server ROUTER 7! Zustand? TODO: welche NPMs? Techniken Arbeitsaufteilung Was brauchen wir? pseudocode? Objektdaten! Welche Variablen/Arrays brauchen wir? MD5 für Log in neeee lieber nicht Router: https://reactrouter.com/start/declarative/routing Bilder ablegen auf Cloudinary ... ebqndymu https://cloudinary.com/documentation/react_integration Index DB API https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API neuester backup: rules_version = '2'; service cloud.firestore { match /databases/{database}/documents { match /allads/{adId} { allow read: if true; allow update: if request.auth != null && ( (request.resource.data.views == resource.data.views + 1) || (request.resource.data.likes == resource.data.likes + 1 || request.resource.data.likes == resource.data.likes - 1) || request.auth.uid == resource.data.userID ); allow create: if request.auth != null && request.auth.uid == request.resource.data.userID; allow delete: if request.auth != null && request.auth.uid == resource.data.userID; } match /users/{userId} { allow read: if true; allow create: if request.auth != null && request.auth.uid == userId; allow write: if request.auth != null && request.auth.uid == userId; } match /messages/{messageId} { allow create: if request.auth != null && request.resource.data.senderID == request.auth.uid; allow read: if request.auth != null && ( request.auth.uid == resource.data.senderID || request.auth.uid == resource.data.recipientID ); allow update: if request.auth != null && ( (request.auth.uid == resource.data.recipientID && (request.resource.data.visibleForRecipient == false || request.resource.data.isRead == true)) || (request.auth.uid == resource.data.senderID && request.resource.data.visibleForSender == false) ); } } } ------neunuenenu backup: rules_version = '2'; service cloud.firestore { match /databases/{database}/documents { // Rules for the "allads" collection match /allads/{adId} { // Allow public reads allow read: if true; // Allow updates to "views" or "likes" by authenticated users allow update: if request.auth != null && ( (request.resource.data.views == resource.data.views + 1) || (request.resource.data.likes == resource.data.likes + 1 || request.resource.data.likes == resource.data.likes - 1) || request.auth.uid == resource.data.userID ); // Allow authenticated users to create ads allow create: if request.auth != null && request.auth.uid == request.resource.data.userID; // Allow ad creators to delete their own ads allow delete: if request.auth != null && request.auth.uid == resource.data.userID; } // Rules for the "users" collection match /users/{userId} { // Allow authenticated users to read/write their own data allow read, write: if request.auth != null && request.auth.uid == userId; } } } ------ ben code speicher :D---> rules_version = '2'; service cloud.firestore { match /databases/{database}/documents { match /{document=**} { allow read, write: if true; } } } -------- rules_version = '2'; service cloud.firestore { match /databases/{database}/documents { match /{document=**} { allow read, write: if true; } } } secure: rules_version = '2'; service cloud.firestore { match /databases/{database}/documents { match /allads/{adId} { allow read: if true; allow write: if request.auth != null && request.auth.uid == resource.data.userID; } match /users/{userId} { allow read: if request.auth != null && request.auth.uid == userId; allow write: if request.auth != null && request.auth.uid == userId; } } } thisisbest: rules_version = '2'; service cloud.firestore { match /databases/{database}/documents { match /allads/{adId} { // Allow public reads allow read: if true; // Allow updates to specific fields (e.g., views, likes) without requiring userID to match allow update: if request.auth != null && ( request.resource.data.keys().hasOnly(["views", "likes"]) || request.auth.uid == resource.data.userID ); // Allow authenticated users to create ads allow create: if request.auth != null && request.auth.uid == request.resource.data.userID; // Allow ad creators to delete their own ads allow delete: if request.auth != null && request.auth.uid == resource.data.userID; } match /users/{userId} { // Allow authenticated users to read/write their own data allow read, write: if request.auth != null && request.auth.uid == userId; } } } --->>> hier datenbank aufruf: import { collection, getDocs } from "firebase/firestore"; import { db } from "../../config/firebaseConfig"; const [ads, setAds] = useState([]); const [error, setError] = useState(""); useEffect(() => { const fetchAds = async () => { try { const allAdsCollection = collection(db, "allads"); const querySnapshot = await getDocs(allAdsCollection); const ads = querySnapshot.docs.map((doc) => ({ id: doc.id, ...doc.data(), })); setAds(ads); } catch (error) { console.error("Error fetching ads:", error); setError("Failed to fetch ads. Please try again later."); } }; fetchAds(); }, []); import styles from "./placead.module.css"; import React, { useState } from "react"; import { v4 as uuidv4 } from "uuid"; import CategorySelector from "./CategorySelector"; import { db, storage } from "../../config/firebaseConfig"; // Import Firebase database and storage instances import { collection, addDoc, serverTimestamp } from "firebase/firestore"; // Import Firestore functions import { ref, uploadBytesResumable, getDownloadURL } from "firebase/storage"; // Import Storage functions import useUserStore from "../../hooks/userStore"; // Import the Zustand store const PlaceAd = () => { const currentUser = useUserStore((state) => state.currentUser); // Get currentUser from Zustand store const [formData, setFormData] = useState({ title: "", price: "", description: "", }); const [selectedCategory, setSelectedCategory] = useState(""); const [selectedFlags, setSelectedFlags] = useState([]); const [images, setImages] = useState([]); const [uploadProgress, setUploadProgress] = useState(0); const [imageURLs, setImageURLs] = useState([]); const [error, setError] = useState(""); const [successMessage, setSuccessMessage] = useState(""); const [isUploading, setIsUploading] = useState(false); const handleFileChange = (e) => { const files = Array.from(e.target.files); if (files.length === 0) { setError("Bitte lade mindestens ein Bild hoch."); setImages([]); setImageURLs([]); return; } setImages(files); setImageURLs([]); // Clear previous URLs when new files are selected setError(""); }; const handleUploadToFirebase = async () => { setIsUploading(true); setUploadProgress(0); const urls = []; for (const file of images) { const storageRef = ref(storage, `ads/${currentUser.userID}/${uuidv4()}-${file.name}`); const uploadTask = uploadBytesResumable(storageRef, file); try { await new Promise((resolve, reject) => { uploadTask.on( 'state_changed', (snapshot) => { const progress = (snapshot.bytesTransferred / snapshot.totalBytes) * 100; setUploadProgress((prevProgress) => Math.max(prevProgress, progress)); // Update progress console.log('Upload is ' + progress + '% done'); switch (snapshot.state) { case 'paused': console.log('Upload is paused'); break; case 'running': console.log('Upload is running'); break; default: break; } }, (error) => { console.error("Fehler beim Hochladen des Bildes:", error); setError("Fehler beim Hochladen eines oder mehrerer Bilder."); setIsUploading(false); reject(error); }, async () => { // Upload completed successfully, now we can get the download URL const downloadURL = await getDownloadURL(uploadTask.snapshot.ref); urls.push(downloadURL); resolve(); } ); }); } catch (uploadError) { return; // Stop further uploads if one fails } } setImageURLs(urls); setIsUploading(false); setUploadProgress(0); }; const handleChange = (e) => { const { name, value } = e.target; setFormData((prev) => ({ ...prev, [name]: value })); }; const handleSubmit = async (e) => { e.preventDefault(); setError(""); setSuccessMessage(""); if (!currentUser) { setError("Du musst eingeloggt sein, um eine Anzeige zu erstellen."); return; } if (images.length === 0) { setError("Du musst mindestens ein Bild hochladen."); return; } if (isUploading) { setError("Bitte warte, bis die Bilder hochgeladen wurden."); return; } if (imageURLs.length === 0 && images.length > 0) { setError("Die Bilder konnten nicht hochgeladen werden. Bitte versuche es erneut."); return; } if (!selectedCategory) { setError("Bitte wähle eine Kategorie."); return; } const ad = { adID: uuidv4(), title: formData.title, price: formData.price, description: formData.description, category: selectedCategory, tags: selectedFlags, createdAt: serverTimestamp(), // Use Firebase server timestamp views: 0, likes: 0, images: imageURLs, location: { city: currentUser.address?.city || "", zip: currentUser.address?.zip || "", street: currentUser.address?.street || "", }, userID: currentUser.userID, }; try { const allAdsCollection = collection(db, "allads"); await addDoc(allAdsCollection, ad); setSuccessMessage("Anzeige erfolgreich erstellt!"); setFormData({ title: "", price: "", description: "" }); setSelectedCategory(""); setSelectedFlags([]); setImages([]); setImageURLs([]); } catch (error) { console.error("Fehler beim Speichern der Anzeige in Firebase:", error); setError("Fehler beim Erstellen der Anzeige. Bitte versuche es später erneut."); } }; return (