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 (
<div className={styles.container}>
<h1 className={styles.headline}>Neue Anzeige erstellen</h1>
<form onSubmit={handleSubmit} className={styles.formContainer}>
<input
className={styles.textInput}
name="title"
placeholder="Titel"
value={formData.title}
onChange={handleChange}
required
/>
<input
className={styles.textInput}
name="price"
placeholder="Preis (z. B. VB, 15€)"
value={formData.price}
onChange={handleChange}
required
/>
<textarea
className={styles.textArea}
name="description"
placeholder="Beschreibung"
value={formData.description}
onChange={handleChange}
required
/>
<CategorySelector
onCategoryChange={setSelectedCategory}
onFlagsChange={setSelectedFlags}
/>
<input
className={styles.fileInput}
type="file"
accept="image/*"
multiple
onChange={handleFileChange}
required
/>
{images.length > 0 && !isUploading && (
<button type="button" className={styles.uploadButton} onClick={handleUploadToFirebase}>
Bilder hochladen
</button>
)}
{isUploading && (
<div className={styles.progressBarContainer}>
<div className={styles.progressBar} style={{ width: `${uploadProgress}%` }}>
{Math.round(uploadProgress)}%
</div>
</div>
)}
{error && <p className={styles.errorMessage}>{error}</p>}
{successMessage && <p className={styles.successMessage}>{successMessage}</p>}
<button type="submit" className={styles.submitButton} disabled={isUploading || imageURLs.length === 0 && images.length > 0}>
Anzeige erstellen
</button>
</form>
{imageURLs.length > 0 && (
<div className={styles.imagePreviewContainer}>
{imageURLs.map((url, idx) => (
<img
key={idx}
className={styles.previewImage}
src={url}
alt={`Bild ${idx + 1}`}
/>
))}
</div>
)}
</div>
);
};
export default PlaceAd;