# Reputation-System — kashilo.com

## Ziel

Vertrauen zwischen anonymen Nutzern aufbauen, ohne KYC, ohne Escrow.
Trust durch nachweisbares Verhalten statt durch Identität.

---

## Stufensystem

| Level | Bedingung | Badge | Effekt |
|-------|-----------|-------|--------|
| **Neu** | 0 bestätigte Deals | ⚪ | Hinweis "Neuer Account" auf Anzeigen |
| **Aktiv** | 3+ Deals, Account 30+ Tage | 🔵 | Kein Hinweis |
| **Vertrauenswürdig** | 10+ Deals, ⌀ 4.0+ Sterne | 🟢 | Badge auf Anzeigen |
| **Power Seller** | 30+ Deals, ⌀ 4.5+ Sterne | 🟣 | Badge + bevorzugte Platzierung |

---

## Datenmodell

### Directus: Collection `deals`

Bestätigte Transaktionen zwischen zwei Nutzern.

| Feld | Typ | Beschreibung |
|------|-----|--------------|
| `id` | UUID | Primary Key |
| `listing_id` | UUID, FK → listings | Bezug zur Anzeige |
| `conversation_id` | UUID, FK → conversations | Bezug zur Konversation |
| `seller_hash` | string(64) | SHA-256 Hash des Verkäufers |
| `buyer_hash` | string(64) | SHA-256 Hash des Käufers |
| `seller_confirmed` | boolean, default false | Verkäufer hat Deal bestätigt |
| `buyer_confirmed` | boolean, default false | Käufer hat Deal bestätigt |
| `status` | string | `pending`, `confirmed`, `disputed` |
| `date_created` | datetime | Erstellungsdatum |
| `date_confirmed` | datetime, nullable | Zeitpunkt der beidseitigen Bestätigung |

Ein Deal gilt als `confirmed`, wenn **beide Seiten** bestätigt haben.

### Directus: Collection `ratings`

Bewertungen nach bestätigtem Deal.

| Feld | Typ | Beschreibung |
|------|-----|--------------|
| `id` | UUID | Primary Key |
| `deal_id` | UUID, FK → deals | Bezug zum Deal |
| `rater_hash` | string(64) | Wer bewertet |
| `rated_hash` | string(64) | Wer bewertet wird |
| `score` | integer | 1–5 Sterne |
| `date_created` | datetime | Zeitpunkt |

Pro Deal kann jeder Teilnehmer **einmal** bewerten (nach Bestätigung).
Kein Freitext-Kommentar — nur Sterne (Privacy).

### Directus: Permissions

| Collection | Public Read | Public Create | Public Update |
|------------|:-----------:|:------------:|:------------:|
| `deals` | Ja (Filter: eigener Hash) | Ja | Ja (nur eigene `*_confirmed`) |
| `ratings` | Ja (Filter: `rated_hash`) | Ja (nur nach bestätigtem Deal) | Nein |

### Computed Fields (Frontend)

Diese Werte werden im Frontend aus `deals` + `ratings` berechnet:

```
reputation = {
    deals_completed: count(deals WHERE status = 'confirmed' AND user = seller OR buyer),
    avg_rating: avg(ratings WHERE rated_hash = user),
    account_age_days: now() - user.date_created,
    level: computed from above
}
```

---

## User Flow

### Deal bestätigen (im Chat)

```
1. Käufer und Verkäufer handeln im Chat
2. Eine Seite klickt "Deal abschliessen" → Deal wird erstellt (status: pending)
3. Andere Seite sieht Bestätigungsanfrage im Chat
4. Andere Seite klickt "Deal bestätigen" → status: confirmed
5. Beide Seiten können danach bewerten (1–5 Sterne)
```

### UI im Chat-Widget

```
┌─────────────────────────────────────┐
│  💬 Chat mit Verkäufer              │
│                                     │
│  ... Nachrichten ...                │
│                                     │
│  ┌─────────────────────────────┐    │
│  │ 🤝 Deal abschliessen       │    │
│  └─────────────────────────────┘    │
│                                     │
│  Nach beidseitiger Bestätigung:     │
│  ┌─────────────────────────────┐    │
│  │ ⭐⭐⭐⭐☆  Bewertung abgeben │    │
│  └─────────────────────────────┘    │
└─────────────────────────────────────┘
```

### Badge auf Listing-Card

```
┌──────────────────────┐
│  📷 Bild             │
│  Titel der Anzeige   │
│  50.00 EUR           │
│  📍 Zürich  🟢 10+   │  ← Badge + Deal-Count
└──────────────────────┘
```

### Seller-Card auf Listing-Seite

```
┌──────────────────────────┐
│  ?  Anonymer Anbieter    │
│     Mitglied seit 2025   │
│     🟢 Vertrauenswürdig  │
│     12 Deals · ⌀ 4.3 ⭐  │
└──────────────────────────┘
```

---

## Scam-Prävention

| Massnahme | Beschreibung |
|-----------|--------------|
| **Neuer-Account-Warnung** | "Neuer Account — starte mit kleinen Beträgen" |
| **Preis-Limit** | Max. Anzeigenpreis für Level "Neu": 100 EUR/USD/CHF |
| **Cooldown** | Neue Accounts: max. 3 Anzeigen pro Tag |
| **Report-Konsequenz** | 3+ Reports → automatische Prüfung, Anzeigen ausgeblendet |
| **Self-Rating-Schutz** | Nur nach beidseitig bestätigtem Deal bewertbar |

---

## Implementierung

### Phase 1: Backend (Directus)
- [ ] Collection `deals` anlegen (siehe Anleitung unten)
- [ ] Collection `ratings` anlegen (siehe Anleitung unten)
- [ ] Permissions setzen (Public-Rolle, siehe unten)
- [ ] Flow: Notification bei Deal-Bestätigung (optional)

---

## Directus: Collection `deals` anlegen

```
Settings > Data Model > + Create Collection
Name: deals
Primary Key: UUID (auto-generated)
```

| Feld | Typ | Interface | Einstellungen |
|------|-----|-----------|---------------|
| `id` | UUID | – | Primary Key, auto-generated |
| `listing` | UUID (String) | Input | Listing-ID (kein FK, da Public-Rolle keinen Zugriff auf M2O hat) |
| `conversation` | UUID (String) | Input | Conversation-ID |
| `seller_hash` | String | Input | Required, max 64 chars |
| `buyer_hash` | String | Input | Required, max 64 chars |
| `seller_confirmed` | Boolean | Toggle | Default: `false` |
| `buyer_confirmed` | Boolean | Toggle | Default: `false` |
| `status` | String | Dropdown | `pending`, `confirmed`, `disputed` (Default: `pending`) |
| `date_created` | DateTime | DateTime | Auto, Read-only (Directus: "On Create" → Save Current Date/Time) |
| `date_confirmed` | DateTime | DateTime | Nullable, kein Default |

### Schritt-für-Schritt

1. **Collection erstellen:**
   - Settings → Data Model → `+ Create Collection`
   - Name: `deals`
   - Primary Key Field: Type `UUID`, Auto-generated: ✓
   - Optional Fields: ✓ `date_created`

2. **Felder anlegen (in dieser Reihenfolge):**

   **listing** (String):
   - Interface: Input
   - Schema: String, max length 36
   - Nicht nullable

   **conversation** (String):
   - Interface: Input
   - Schema: String, max length 36
   - Nicht nullable

   **seller_hash** (String):
   - Interface: Input
   - Schema: String, max length 64
   - Nicht nullable

   **buyer_hash** (String):
   - Interface: Input
   - Schema: String, max length 64
   - Nicht nullable

   **seller_confirmed** (Boolean):
   - Interface: Toggle
   - Schema: Boolean, Default: `false`

   **buyer_confirmed** (Boolean):
   - Interface: Toggle
   - Schema: Boolean, Default: `false`

   **status** (String):
   - Interface: Dropdown
   - Options: `pending`, `confirmed`, `disputed`
   - Schema: String, Default: `pending`

   **date_confirmed** (DateTime):
   - Interface: DateTime
   - Schema: Timestamp, Nullable: ✓

---

## Directus: Collection `ratings` anlegen

```
Settings > Data Model > + Create Collection
Name: ratings
Primary Key: UUID (auto-generated)
```

| Feld | Typ | Interface | Einstellungen |
|------|-----|-----------|---------------|
| `id` | UUID | – | Primary Key, auto-generated |
| `deal` | UUID (String) | Input | Deal-ID |
| `rater_hash` | String | Input | Required, max 64 chars, wer bewertet |
| `rated_hash` | String | Input | Required, max 64 chars, wer bewertet wird |
| `score` | Integer | Slider | Min: 1, Max: 5 |
| `date_created` | DateTime | DateTime | Auto, Read-only |

### Schritt-für-Schritt

1. **Collection erstellen:**
   - Settings → Data Model → `+ Create Collection`
   - Name: `ratings`
   - Primary Key Field: Type `UUID`, Auto-generated: ✓
   - Optional Fields: ✓ `date_created`

2. **Felder anlegen:**

   **deal** (String):
   - Interface: Input
   - Schema: String, max length 36
   - Nicht nullable

   **rater_hash** (String):
   - Interface: Input
   - Schema: String, max length 64
   - Nicht nullable

   **rated_hash** (String):
   - Interface: Input
   - Schema: String, max length 64
   - Nicht nullable

   **score** (Integer):
   - Interface: Slider
   - Options: Min `1`, Max `5`, Step `1`
   - Schema: Integer, nicht nullable

---

## Directus: Permissions (Public-Rolle)

```
Settings > Access Control > Public
```

### Collection `deals`

**Read:**
- ✓ Erlaubt
- Felder: Alle
- Filter: Eigene Deals sehen
  ```json
  {
    "_or": [
      { "seller_hash": { "_eq": "$CURRENT_USER" } },
      { "buyer_hash": { "_eq": "$CURRENT_USER" } }
    ]
  }
  ```
  **Hinweis:** Da wir SHA-256 Hashes nutzen (nicht Directus-User-IDs), funktioniert `$CURRENT_USER` hier NICHT.
  Stattdessen: **Keine Read-Filter setzen** (alle Deals lesbar). Die Filterung passiert im Frontend via API-Query.
  Alternativ: Kein Filter, aber nur bestimmte Felder freigeben (z.B. `seller_hash`, `buyer_hash`, `status`, `date_confirmed` — ohne `conversation`, `listing`).

**Empfohlene Read-Permissions (ohne Filter, mit Feld-Einschränkung):**
- Felder: `id`, `listing`, `conversation`, `seller_hash`, `buyer_hash`, `seller_confirmed`, `buyer_confirmed`, `status`, `date_created`, `date_confirmed`

**Create:**
- ✓ Erlaubt
- Felder: `listing`, `conversation`, `seller_hash`, `buyer_hash`, `seller_confirmed`, `buyer_confirmed`, `status`
- Kein Validation-Filter nötig (Frontend validiert)

**Update:**
- ✓ Erlaubt
- Felder: **Nur** `seller_confirmed`, `buyer_confirmed`, `status`, `date_confirmed`
- **Kein Custom-Filter** (da `$CURRENT_USER` nicht mit Hashes funktioniert)
- Schutz: Im Frontend wird geprüft ob der User Teilnehmer ist

**Delete:**
- ✗ Nicht erlaubt

### Collection `ratings`

**Read:**
- ✓ Erlaubt
- Felder: `id`, `deal`, `rater_hash`, `rated_hash`, `score`, `date_created`
- Kein Filter (Ratings sind öffentlich sichtbar für Reputation)

**Create:**
- ✓ Erlaubt
- Felder: `deal`, `rater_hash`, `rated_hash`, `score`

**Update:**
- ✗ Nicht erlaubt (Bewertungen sind final)

**Delete:**
- ✗ Nicht erlaubt

---

## Directus: DIRECTUS-SCHEMA.md aktualisieren

Nach dem Anlegen diese Tabellen in `docs/DIRECTUS-SCHEMA.md` ergänzen und die Permissions-Tabelle erweitern:

```
| `deals`   | ✓ | ✓ | ✓ | - | Update nur confirmed/status Felder |
| `ratings` | ✓ | ✓ | - | - | Bewertungen sind final |
```

### Phase 2: Service (Frontend)
- [x] `js/services/reputation.js` — Deals/Ratings CRUD, Level-Berechnung, Caching
- [x] i18n-Keys in allen 7 Sprachen
- [ ] Integration in `conversations.js` — Deal-Bestätigung im Chat

### Phase 3: UI
- [x] Chat-Widget: "Deal abschliessen" Button + Bestätigungsanfrage
- [x] Chat-Widget: Sterne-Bewertung nach bestätigtem Deal
- [ ] Listing-Card: Badge + Deal-Count
- [x] Listing-Page Seller-Card: Level, Deals, Rating
- [x] Neuer-Account-Warnung auf Listing-Page

### Phase 4: Scam-Prävention
- [ ] Preis-Limit für neue Accounts
- [ ] Anzeigen-Cooldown für neue Accounts
- [ ] Report-Integration mit Konsequenzen

---

## i18n Keys (benötigt)

```
reputation.level.new        = "Neuer Account"
reputation.level.active     = "Aktiv"
reputation.level.trusted    = "Vertrauenswürdig"
reputation.level.power      = "Power Seller"
reputation.deals            = "{{count}} Deals"
reputation.dealsSingular    = "1 Deal"
reputation.avgRating        = "⌀ {{rating}} ⭐"
reputation.newWarning       = "Neuer Account — starte mit kleinen Beträgen"
reputation.confirmDeal      = "Deal abschliessen"
reputation.dealPending      = "Warte auf Bestätigung"
reputation.dealConfirmed    = "Deal bestätigt"
reputation.rate             = "Bewertung abgeben"
reputation.rated            = "Bewertet"
```
