Dateien nach „promotion“ hochladen
Vorbereitung für Züchterfeatures
This commit is contained in:
parent
b3d2d298dc
commit
4ac1c27b75
1 changed files with 489 additions and 0 deletions
489
promotion/banyaro_zuechterolle_ausarbeitung.md
Normal file
489
promotion/banyaro_zuechterolle_ausarbeitung.md
Normal file
|
|
@ -0,0 +1,489 @@
|
||||||
|
# Züchter-Rolle — Banyaro.app
|
||||||
|
## Vollständige Ausarbeitung zur Umsetzung
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Übersicht
|
||||||
|
|
||||||
|
### Rollen-Modell
|
||||||
|
|
||||||
|
| Rolle | Beschreibung |
|
||||||
|
|---|---|
|
||||||
|
| `user` | Normaler Hundebesitzer (bestehend) |
|
||||||
|
| `breeder` | Verifizierter Züchter (neu) |
|
||||||
|
| `admin` | René — Plattform-Administration (bestehend) |
|
||||||
|
|
||||||
|
Die Rolle `breeder` erweitert `user` — ein Züchter hat alle normalen Nutzerrechte plus Züchter-spezifische Features.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Datenbankstruktur
|
||||||
|
|
||||||
|
### Tabelle: `users` (Erweiterung)
|
||||||
|
```sql
|
||||||
|
ALTER TABLE users ADD COLUMN role VARCHAR(20) DEFAULT 'user';
|
||||||
|
-- Werte: 'user', 'breeder', 'admin'
|
||||||
|
|
||||||
|
ALTER TABLE users ADD COLUMN breeder_status VARCHAR(20) DEFAULT NULL;
|
||||||
|
-- Werte: NULL (kein Antrag), 'pending', 'approved', 'rejected'
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Tabelle: `breeder_profiles`
|
||||||
|
```sql
|
||||||
|
CREATE TABLE breeder_profiles (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
user_id INTEGER NOT NULL UNIQUE,
|
||||||
|
kennel_name VARCHAR(100) NOT NULL, -- Zwingername
|
||||||
|
breed_id INTEGER NOT NULL, -- Nur eine Rasse (FK auf breeds)
|
||||||
|
vdh_member BOOLEAN DEFAULT FALSE,
|
||||||
|
association VARCHAR(100), -- Zuchtverein (z.B. KFZ, VDH)
|
||||||
|
description TEXT, -- Freitext Vorstellung
|
||||||
|
website VARCHAR(255),
|
||||||
|
location_lat FLOAT,
|
||||||
|
location_lng FLOAT,
|
||||||
|
location_city VARCHAR(100),
|
||||||
|
show_on_map BOOLEAN DEFAULT TRUE, -- Auf Karte sichtbar
|
||||||
|
verified_at DATETIME DEFAULT NULL,
|
||||||
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
FOREIGN KEY (user_id) REFERENCES users(id),
|
||||||
|
FOREIGN KEY (breed_id) REFERENCES breeds(id)
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Tabelle: `breeder_documents`
|
||||||
|
```sql
|
||||||
|
CREATE TABLE breeder_documents (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
user_id INTEGER NOT NULL,
|
||||||
|
document_type VARCHAR(50) NOT NULL,
|
||||||
|
-- Werte: 'vdh_membership', 'breeding_permit'
|
||||||
|
file_path VARCHAR(255) NOT NULL,
|
||||||
|
uploaded_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
FOREIGN KEY (user_id) REFERENCES users(id)
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Tabelle: `litters` (Würfe)
|
||||||
|
```sql
|
||||||
|
CREATE TABLE litters (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
breeder_id INTEGER NOT NULL, -- FK auf breeder_profiles
|
||||||
|
breed_id INTEGER NOT NULL,
|
||||||
|
father_name VARCHAR(100),
|
||||||
|
mother_name VARCHAR(100),
|
||||||
|
birth_date DATE,
|
||||||
|
expected_date DATE, -- Bei geplantem Wurf
|
||||||
|
total_puppies INTEGER,
|
||||||
|
available_count INTEGER,
|
||||||
|
description TEXT,
|
||||||
|
health_tests TEXT, -- HD, ED, Augen etc. als JSON
|
||||||
|
price_range VARCHAR(50), -- Optional, z.B. "1200-1500 €"
|
||||||
|
status VARCHAR(20) DEFAULT 'planned',
|
||||||
|
-- Werte: 'planned', 'born', 'available', 'closed'
|
||||||
|
visible BOOLEAN DEFAULT TRUE, -- Züchter steuert Sichtbarkeit
|
||||||
|
visible_until DATE DEFAULT NULL, -- Optional: Ablaufdatum
|
||||||
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
FOREIGN KEY (breeder_id) REFERENCES breeder_profiles(id),
|
||||||
|
FOREIGN KEY (breed_id) REFERENCES breeds(id)
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Tabelle: `puppies` (Einzelne Welpen — optional pro Wurf)
|
||||||
|
```sql
|
||||||
|
CREATE TABLE puppies (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
litter_id INTEGER NOT NULL,
|
||||||
|
name VARCHAR(100), -- Optional
|
||||||
|
gender VARCHAR(10), -- 'male', 'female'
|
||||||
|
color VARCHAR(50),
|
||||||
|
chip_number VARCHAR(50),
|
||||||
|
birth_weight FLOAT, -- Gramm
|
||||||
|
status VARCHAR(20) DEFAULT 'available',
|
||||||
|
-- Werte: 'available', 'reserved', 'adopted'
|
||||||
|
show_status BOOLEAN DEFAULT TRUE, -- Züchter entscheidet ob Status sichtbar
|
||||||
|
notes TEXT, -- Interne Notiz des Züchters
|
||||||
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
FOREIGN KEY (litter_id) REFERENCES litters(id)
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Tabelle: `puppy_weights` (Gewichtsverlauf Welpen)
|
||||||
|
```sql
|
||||||
|
CREATE TABLE puppy_weights (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
puppy_id INTEGER NOT NULL,
|
||||||
|
weight_g FLOAT NOT NULL,
|
||||||
|
measured_at DATE NOT NULL,
|
||||||
|
FOREIGN KEY (puppy_id) REFERENCES puppies(id)
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Tabelle: `breeder_inquiries` (Interessenten)
|
||||||
|
```sql
|
||||||
|
CREATE TABLE breeder_inquiries (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
litter_id INTEGER NOT NULL,
|
||||||
|
sender_id INTEGER NOT NULL, -- User der anfrägt
|
||||||
|
message TEXT NOT NULL,
|
||||||
|
status VARCHAR(20) DEFAULT 'new',
|
||||||
|
-- Werte: 'new', 'replied', 'reserved', 'closed'
|
||||||
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
FOREIGN KEY (litter_id) REFERENCES litters(id),
|
||||||
|
FOREIGN KEY (sender_id) REFERENCES users(id)
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Registrierungsprozess
|
||||||
|
|
||||||
|
### Schritt 1 — Antrag stellen (Frontend)
|
||||||
|
|
||||||
|
Der User navigiert zu "Mein Profil → Züchter werden".
|
||||||
|
|
||||||
|
**Formular:**
|
||||||
|
```
|
||||||
|
Zwingername *
|
||||||
|
Zuchtverein / Organisation *
|
||||||
|
Rasse * (Dropdown aus breeds-Tabelle)
|
||||||
|
VDH-Mitglied? (Ja / Nein)
|
||||||
|
Stadt / Region *
|
||||||
|
Website (optional)
|
||||||
|
Kurze Vorstellung (Freitext)
|
||||||
|
|
||||||
|
Dokumente hochladen:
|
||||||
|
[ ] VDH-Mitgliedsausweis (PDF oder Bild)
|
||||||
|
[ ] Zuchtzulassung des Vereins (PDF oder Bild)
|
||||||
|
→ Mindestens eines der beiden Dokumente erforderlich
|
||||||
|
|
||||||
|
[ ] Ich bestätige, dass meine Angaben korrekt sind
|
||||||
|
[ ] Ich habe die Nutzungsbedingungen für Züchter gelesen
|
||||||
|
|
||||||
|
[Antrag absenden]
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Schritt 2 — Backend-Verarbeitung
|
||||||
|
|
||||||
|
```python
|
||||||
|
# POST /api/breeder/apply
|
||||||
|
|
||||||
|
@router.post("/breeder/apply")
|
||||||
|
async def apply_for_breeder(
|
||||||
|
current_user: User = Depends(get_current_user),
|
||||||
|
form_data: BreederApplicationForm,
|
||||||
|
documents: List[UploadFile]
|
||||||
|
):
|
||||||
|
# Validierung
|
||||||
|
if current_user.breeder_status == 'pending':
|
||||||
|
raise HTTPException(400, "Antrag bereits gestellt")
|
||||||
|
if current_user.role == 'breeder':
|
||||||
|
raise HTTPException(400, "Bereits verifizierter Züchter")
|
||||||
|
if len(documents) == 0:
|
||||||
|
raise HTTPException(400, "Mindestens ein Dokument erforderlich")
|
||||||
|
|
||||||
|
# Dokumente speichern (nicht öffentlich erreichbar)
|
||||||
|
for doc in documents:
|
||||||
|
save_path = f"/private/breeder_docs/{current_user.id}/{doc.filename}"
|
||||||
|
save_document(doc, save_path)
|
||||||
|
db.add(BreederDocument(
|
||||||
|
user_id=current_user.id,
|
||||||
|
document_type=detect_doc_type(doc.filename),
|
||||||
|
file_path=save_path
|
||||||
|
))
|
||||||
|
|
||||||
|
# Profil-Entwurf anlegen
|
||||||
|
db.add(BreederProfile(
|
||||||
|
user_id=current_user.id,
|
||||||
|
kennel_name=form_data.kennel_name,
|
||||||
|
breed_id=form_data.breed_id,
|
||||||
|
...
|
||||||
|
))
|
||||||
|
|
||||||
|
# Status setzen
|
||||||
|
current_user.breeder_status = 'pending'
|
||||||
|
db.commit()
|
||||||
|
|
||||||
|
# Admin benachrichtigen
|
||||||
|
await notify_admin_new_breeder_application(current_user, form_data)
|
||||||
|
|
||||||
|
return {"message": "Antrag eingereicht. Du wirst per E-Mail benachrichtigt."}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Schritt 3 — Admin-Benachrichtigung
|
||||||
|
|
||||||
|
**E-Mail an René:**
|
||||||
|
```
|
||||||
|
Betreff: [Banyaro] Neuer Züchter-Antrag — {Zwingername}
|
||||||
|
|
||||||
|
Neuer Antrag von: {Username} ({E-Mail})
|
||||||
|
Zwingername: {Zwingername}
|
||||||
|
Rasse: {Rasse}
|
||||||
|
Verein: {Verein}
|
||||||
|
VDH: Ja/Nein
|
||||||
|
Dokumente: {Anzahl} hochgeladen
|
||||||
|
|
||||||
|
→ Im Admin-Bereich prüfen: banyaro.app/admin/breeders
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Schritt 4 — Admin-Bereich (Prüfung)
|
||||||
|
|
||||||
|
**Route:** `/admin/breeders`
|
||||||
|
|
||||||
|
**Ansicht je Antrag:**
|
||||||
|
- Antragsdaten vollständig anzeigen
|
||||||
|
- Dokumente inline anzeigen (PDF-Viewer oder Bild)
|
||||||
|
- Buttons: **[Freischalten]** | **[Ablehnen]**
|
||||||
|
- Freitextfeld für Ablehnungsgrund (wird per E-Mail gesendet)
|
||||||
|
|
||||||
|
```python
|
||||||
|
# POST /api/admin/breeder/{user_id}/approve
|
||||||
|
|
||||||
|
@router.post("/admin/breeder/{user_id}/approve")
|
||||||
|
async def approve_breeder(
|
||||||
|
user_id: int,
|
||||||
|
admin: User = Depends(require_admin)
|
||||||
|
):
|
||||||
|
user = db.get(User, user_id)
|
||||||
|
user.role = 'breeder'
|
||||||
|
user.breeder_status = 'approved'
|
||||||
|
|
||||||
|
profile = db.query(BreederProfile).filter_by(user_id=user_id).first()
|
||||||
|
profile.verified_at = datetime.utcnow()
|
||||||
|
db.commit()
|
||||||
|
|
||||||
|
await send_breeder_approval_email(user)
|
||||||
|
return {"message": "Züchter freigeschaltet"}
|
||||||
|
|
||||||
|
|
||||||
|
# POST /api/admin/breeder/{user_id}/reject
|
||||||
|
|
||||||
|
@router.post("/admin/breeder/{user_id}/reject")
|
||||||
|
async def reject_breeder(
|
||||||
|
user_id: int,
|
||||||
|
reason: str,
|
||||||
|
admin: User = Depends(require_admin)
|
||||||
|
):
|
||||||
|
user = db.get(User, user_id)
|
||||||
|
user.breeder_status = 'rejected'
|
||||||
|
db.commit()
|
||||||
|
|
||||||
|
await send_breeder_rejection_email(user, reason)
|
||||||
|
return {"message": "Antrag abgelehnt"}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Schritt 5 — User-Benachrichtigung
|
||||||
|
|
||||||
|
**Bei Freischaltung:**
|
||||||
|
```
|
||||||
|
Betreff: Willkommen als Züchter bei Banyaro! 🐾
|
||||||
|
|
||||||
|
Hallo {Username},
|
||||||
|
|
||||||
|
dein Züchter-Profil wurde erfolgreich verifiziert.
|
||||||
|
Ab sofort hast du Zugang zu allen Züchter-Features:
|
||||||
|
|
||||||
|
✅ Züchter-Profil auf der Karte
|
||||||
|
✅ Wurf-Verwaltung
|
||||||
|
✅ Wurfankündigungen
|
||||||
|
✅ Interessenten-Nachrichten
|
||||||
|
|
||||||
|
→ Jetzt Profil vervollständigen: banyaro.app/breeder/profile
|
||||||
|
|
||||||
|
Viel Erfolg mit deiner Zucht!
|
||||||
|
Das Banyaro-Team
|
||||||
|
```
|
||||||
|
|
||||||
|
**Bei Ablehnung:**
|
||||||
|
```
|
||||||
|
Betreff: Dein Züchter-Antrag bei Banyaro
|
||||||
|
|
||||||
|
Hallo {Username},
|
||||||
|
|
||||||
|
leider konnten wir deinen Antrag aktuell nicht bestätigen.
|
||||||
|
|
||||||
|
Grund: {Ablehnungsgrund}
|
||||||
|
|
||||||
|
Du kannst einen neuen Antrag stellen sobald du die
|
||||||
|
fehlenden Dokumente vorliegen hast.
|
||||||
|
|
||||||
|
Bei Fragen: mail@motocamp.de
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Züchter-Features im Detail
|
||||||
|
|
||||||
|
### 4.1 Öffentliches Züchter-Profil
|
||||||
|
|
||||||
|
**Route:** `/breeder/{kennel_name}`
|
||||||
|
|
||||||
|
**Anzeige:**
|
||||||
|
- Zwingername + verifiziertes Badge ✓
|
||||||
|
- Rasse (verlinkt auf Wiki-Eintrag)
|
||||||
|
- Zuchtverein
|
||||||
|
- Stadt / Region
|
||||||
|
- Beschreibung
|
||||||
|
- Website-Link
|
||||||
|
- Aktive Wurfankündigungen
|
||||||
|
- Kontakt-Button → öffnet internes Chat
|
||||||
|
|
||||||
|
**Auf der Karte:**
|
||||||
|
- Eigener Marker-Typ "Züchter" (unterschiedliches Icon)
|
||||||
|
- Popup mit Zwingername, Rasse, Link zum Profil
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 4.2 Wurf-Verwaltung
|
||||||
|
|
||||||
|
**Route:** `/breeder/litters`
|
||||||
|
|
||||||
|
**Funktionen:**
|
||||||
|
- Neuen Wurf anlegen (geplant oder bereits geboren)
|
||||||
|
- Einzelne Welpen anlegen (optional)
|
||||||
|
- Gewichtsverlauf pro Welpe erfassen
|
||||||
|
- Status pro Welpe: verfügbar / reserviert / abgegeben
|
||||||
|
- Züchter steuert selbst was öffentlich sichtbar ist
|
||||||
|
- Wurf archivieren (bleibt als Referenz erhalten)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 4.3 Wurfankündigung (öffentlich)
|
||||||
|
|
||||||
|
**Route:** `/litters` (öffentliche Übersicht für alle User)
|
||||||
|
|
||||||
|
**Filter:**
|
||||||
|
- Nach Rasse
|
||||||
|
- Nach PLZ / Umkreis
|
||||||
|
- Nach Status (geplant / verfügbar)
|
||||||
|
|
||||||
|
**Detailseite pro Wurf:**
|
||||||
|
- Elterntiere (Name, Foto optional)
|
||||||
|
- Geburtsdatum / erwarteter Termin
|
||||||
|
- Anzahl Welpen gesamt / verfügbar
|
||||||
|
- Gesundheitsuntersuchungen der Eltern
|
||||||
|
- Preisrahmen (optional)
|
||||||
|
- Fotos
|
||||||
|
- [Nachricht senden]-Button
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 4.4 Interessenten-Verwaltung
|
||||||
|
|
||||||
|
Eingehende Anfragen erscheinen im internen Chat-System.
|
||||||
|
Züchter kann Status der Anfrage setzen: neu / beantwortet / reserviert / abgeschlossen.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 4.5 Läufigkeits-Tracker (Züchter-Erweiterung)
|
||||||
|
|
||||||
|
Der bestehende Läufigkeits-Tracker wird für Züchter erweitert:
|
||||||
|
- Deckdatum erfassen
|
||||||
|
- Automatische Berechnung des erwarteten Wurftermins (63 Tage Trächtigkeitsdauer)
|
||||||
|
- Erinnerung: "Wurftermin in 7 Tagen"
|
||||||
|
- Direkte Verknüpfung: Läufigkeit → neuen Wurf anlegen
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. API-Endpunkte Übersicht
|
||||||
|
|
||||||
|
```
|
||||||
|
# Registrierung
|
||||||
|
POST /api/breeder/apply Antrag stellen
|
||||||
|
GET /api/breeder/status Antragsstatus abfragen
|
||||||
|
|
||||||
|
# Admin
|
||||||
|
GET /api/admin/breeders/pending Offene Anträge
|
||||||
|
POST /api/admin/breeder/{id}/approve Freischalten
|
||||||
|
POST /api/admin/breeder/{id}/reject Ablehnen
|
||||||
|
|
||||||
|
# Profil
|
||||||
|
GET /api/breeder/{kennel_name} Öffentliches Profil
|
||||||
|
PUT /api/breeder/profile Profil bearbeiten
|
||||||
|
|
||||||
|
# Würfe
|
||||||
|
GET /api/litters Öffentliche Übersicht
|
||||||
|
GET /api/litters/{id} Wurf-Detail
|
||||||
|
POST /api/breeder/litters Neuen Wurf anlegen
|
||||||
|
PUT /api/breeder/litters/{id} Wurf bearbeiten
|
||||||
|
DELETE /api/breeder/litters/{id} Wurf archivieren
|
||||||
|
|
||||||
|
# Welpen
|
||||||
|
POST /api/breeder/litters/{id}/puppies Welpe anlegen
|
||||||
|
PUT /api/breeder/puppies/{id} Welpe bearbeiten
|
||||||
|
POST /api/breeder/puppies/{id}/weight Gewicht erfassen
|
||||||
|
|
||||||
|
# Karte
|
||||||
|
GET /api/map/breeders Züchter für Karte
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Berechtigungsprüfung (Middleware)
|
||||||
|
|
||||||
|
```python
|
||||||
|
def require_breeder(current_user: User = Depends(get_current_user)):
|
||||||
|
if current_user.role not in ('breeder', 'admin'):
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=403,
|
||||||
|
detail="Diese Funktion ist nur für verifizierte Züchter verfügbar"
|
||||||
|
)
|
||||||
|
return current_user
|
||||||
|
```
|
||||||
|
|
||||||
|
Verwendung:
|
||||||
|
```python
|
||||||
|
@router.post("/breeder/litters")
|
||||||
|
async def create_litter(
|
||||||
|
breeder: User = Depends(require_breeder),
|
||||||
|
...
|
||||||
|
):
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Sicherheit & Datenschutz
|
||||||
|
|
||||||
|
- Hochgeladene Dokumente werden **außerhalb des öffentlichen Webroot** gespeichert
|
||||||
|
- Nur Admin kann Dokumente abrufen (authentifizierter Endpunkt)
|
||||||
|
- Dokumente werden nach Prüfung nicht dauerhaft benötigt — optional nach 90 Tagen löschen
|
||||||
|
- Standort des Züchters: nur Stadt/Region öffentlich, keine genaue Adresse
|
||||||
|
- Datenschutzerklärung um Züchter-Daten ergänzen
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. Umsetzungsreihenfolge (empfohlen)
|
||||||
|
|
||||||
|
| Schritt | Was | Aufwand |
|
||||||
|
|---|---|---|
|
||||||
|
| 1 | DB-Migration (users, breeder_profiles, breeder_documents) | Klein |
|
||||||
|
| 2 | Antrag-Formular Frontend + Dokument-Upload | Mittel |
|
||||||
|
| 3 | Admin-Bereich: Anträge prüfen + freischalten | Mittel |
|
||||||
|
| 4 | E-Mail-Benachrichtigungen (Antrag, Freischaltung, Ablehnung) | Klein |
|
||||||
|
| 5 | Öffentliches Züchter-Profil + Karten-Marker | Mittel |
|
||||||
|
| 6 | Wurf-Verwaltung (CRUD) | Mittel |
|
||||||
|
| 7 | Öffentliche Wurfankündigung + Filtersuche | Mittel |
|
||||||
|
| 8 | Welpen-Verwaltung + Gewichtsverlauf | Klein |
|
||||||
|
| 9 | Läufigkeits-Tracker Erweiterung | Klein |
|
||||||
|
| 10 | Interessenten-Chat-Integration | Klein |
|
||||||
|
|
||||||
|
**Gesamtaufwand geschätzt:** 3–5 Tage mit Claude Code
|
||||||
Loading…
Add table
Add a link
Reference in a new issue