refactor(docs): réorganiser la documentation selon principes DDD
Réorganise la documentation du projet selon les principes du Domain-Driven Design (DDD) pour améliorer la cohésion, la maintenabilité et l'alignement avec l'architecture modulaire du backend. **Structure cible:** ``` docs/domains/ ├── README.md (Context Map) ├── _shared/ (Core Domain) ├── recommendation/ (Supporting Subdomain) ├── content/ (Supporting Subdomain) ├── moderation/ (Supporting Subdomain) ├── advertising/ (Generic Subdomain) ├── premium/ (Generic Subdomain) └── monetization/ (Generic Subdomain) ``` **Changements effectués:** Phase 1: Création de l'arborescence des 7 bounded contexts Phase 2: Déplacement des règles métier (01-19) vers domains/*/rules/ Phase 3: Déplacement des diagrammes d'entités vers domains/*/entities/ Phase 4: Déplacement des diagrammes flux/états/séquences vers domains/*/ Phase 5: Création des README.md pour chaque domaine Phase 6: Déplacement des features Gherkin vers domains/*/features/ Phase 7: Création du Context Map (domains/README.md) Phase 8: Mise à jour de mkdocs.yml pour la nouvelle navigation Phase 9: Correction automatique des liens internes (script fix-markdown-links.sh) Phase 10: Nettoyage de l'ancienne structure (regles-metier/, diagrammes/, features/) **Configuration des tests:** - Makefile: godog run docs/domains/*/features/ - scripts/generate-bdd-docs.py: features_dir → docs/domains **Avantages:** ✅ Cohésion forte: toute la doc d'un domaine au même endroit ✅ Couplage faible: domaines indépendants, dépendances explicites ✅ Navigabilité améliorée: README par domaine = entrée claire ✅ Alignement code/docs: miroir de backend/internal/ ✅ Onboarding facilité: exploration domaine par domaine ✅ Tests BDD intégrés: features au plus près des règles métier Voir docs/REFACTOR-DDD.md pour le plan complet.
This commit is contained in:
633
docs/domains/moderation/rules/autres-comportements.md
Normal file
633
docs/domains/moderation/rules/autres-comportements.md
Normal file
@@ -0,0 +1,633 @@
|
||||
## 15. Autres comportements
|
||||
|
||||
### 15.1 Partage de contenu
|
||||
|
||||
**Décision** : Système de partage complet avec web player
|
||||
|
||||
#### 15.1.1 Bouton "Partager"
|
||||
|
||||
**Disponibilité** : Partout dans l'application
|
||||
|
||||
**Emplacements** :
|
||||
- Player en lecture (bouton dans contrôles)
|
||||
- Page profil créateur (sur chaque contenu)
|
||||
- Liste de recherche (menu contextuel)
|
||||
- Historique personnel
|
||||
|
||||
**Icône** : ⬆️ (universelle iOS/Android)
|
||||
|
||||
**Menu options** :
|
||||
- Copier le lien
|
||||
- WhatsApp
|
||||
- Email
|
||||
- SMS
|
||||
- Plus... (sheet natif OS)
|
||||
|
||||
**Justification** :
|
||||
- Viralité = croissance organique gratuite
|
||||
- Aucune friction, partage universel
|
||||
|
||||
---
|
||||
|
||||
#### 15.1.2 Comportement du lien partagé
|
||||
|
||||
**Format URL** : `https://roadwave.fr/share/c/[content_id]`
|
||||
|
||||
**Comportement multi-plateforme** :
|
||||
|
||||
```
|
||||
User clique lien partagé
|
||||
↓
|
||||
Page web responsive
|
||||
↓
|
||||
┌─────────────────────────────────┐
|
||||
│ Si app installée │
|
||||
│ → Deep link (ouverture directe) │
|
||||
└─────────────────────────────────┘
|
||||
↓
|
||||
┌─────────────────────────────────┐
|
||||
│ Si app non installée │
|
||||
│ → Web player + CTA téléchargement│
|
||||
└─────────────────────────────────┘
|
||||
```
|
||||
|
||||
**Contenu de la page web** :
|
||||
|
||||
```html
|
||||
┌───────────────────────────────────────┐
|
||||
│ RoadWave │
|
||||
├───────────────────────────────────────┤
|
||||
│ [Image cover 16:9] │
|
||||
│ │
|
||||
│ 📻 Titre du contenu │
|
||||
│ Par @créateur · 12 min · 🎧 2.3K │
|
||||
│ │
|
||||
│ 📍 Paris 5e · Ancré │
|
||||
│ 🏷️ #Voyage #Histoire │
|
||||
│ │
|
||||
│ Description : Lorem ipsum... │
|
||||
│ │
|
||||
│ [▶️ Écouter maintenant] │
|
||||
│ (Player HTML5 si contenu public) │
|
||||
│ │
|
||||
│ ────────────────────────────────── │
|
||||
│ │
|
||||
│ 📱 Télécharger l'app RoadWave │
|
||||
│ [App Store] [Google Play] │
|
||||
│ │
|
||||
│ [Voir le profil de @créateur] │
|
||||
└───────────────────────────────────────┘
|
||||
```
|
||||
|
||||
**Métadonnées Open Graph (SEO)** :
|
||||
|
||||
```html
|
||||
<meta property="og:title" content="[Titre contenu] - RoadWave">
|
||||
<meta property="og:description" content="[Description ou extrait]">
|
||||
<meta property="og:image" content="[URL cover image]">
|
||||
<meta property="og:audio" content="[URL audio si public]">
|
||||
<meta property="og:type" content="music.song">
|
||||
<meta property="og:site_name" content="RoadWave">
|
||||
<meta name="twitter:card" content="player">
|
||||
<meta name="twitter:player" content="https://roadwave.fr/player/[content_id]">
|
||||
```
|
||||
|
||||
**Deep linking** :
|
||||
- iOS : Universal Links (configuration `apple-app-site-association`)
|
||||
- Android : App Links (configuration `assetlinks.json`)
|
||||
- URL scheme : `roadwave://content/[content_id]`
|
||||
|
||||
**Justification** :
|
||||
- Meilleure viralité (partage social optimisé)
|
||||
- SEO (contenus indexés Google)
|
||||
- UX optimale (web + app)
|
||||
- Coût : 0€ (backend simple + CDN existant)
|
||||
|
||||
---
|
||||
|
||||
#### 15.1.3 Contenus Premium partagés
|
||||
|
||||
**Décision** : Preview 30 secondes + paywall
|
||||
|
||||
**Comportement** :
|
||||
|
||||
1. User clique lien contenu Premium partagé
|
||||
2. Page web affiche badge "👑 Contenu Premium"
|
||||
3. Player démarre automatiquement
|
||||
4. Après **30 secondes exactement** :
|
||||
- Fade out audio (2 secondes)
|
||||
- Overlay apparaît :
|
||||
|
||||
```
|
||||
┌─────────────────────────────────┐
|
||||
│ 👑 Contenu réservé Premium │
|
||||
│ │
|
||||
│ Profitez de ce contenu complet │
|
||||
│ et de milliers d'autres │
|
||||
│ sans publicité │
|
||||
│ │
|
||||
│ [Passer Premium - 4.99€/mois] │
|
||||
│ [Télécharger l'app] │
|
||||
└─────────────────────────────────┘
|
||||
```
|
||||
|
||||
5. Utilisateur peut :
|
||||
- S'abonner Premium (redirection web Mangopay)
|
||||
- Télécharger l'app (redirection stores)
|
||||
- Rejouer les 30 premières secondes (illimité)
|
||||
|
||||
**Tracking** :
|
||||
- Métriques créateur : "Partages Premium" + "Conversions Premium"
|
||||
- Créateur touche sa part si conversion (70%)
|
||||
|
||||
**Justification** :
|
||||
- Équilibre viralité / monétisation
|
||||
- 30s = assez pour donner envie, pas assez pour satisfaire
|
||||
- Protège revenus créateurs
|
||||
|
||||
---
|
||||
|
||||
### 15.2 Profil créateur
|
||||
|
||||
**Décision** : Profil public complet et transparent
|
||||
|
||||
#### 15.2.1 Structure de la page profil
|
||||
|
||||
**URL** : `https://roadwave.fr/@[pseudo]`
|
||||
|
||||
**Layout** :
|
||||
|
||||
```
|
||||
┌────────────────────────────────────────┐
|
||||
│ [Photo profil 120×120] │
|
||||
│ @pseudo ✓ │
|
||||
│ [Badge vérifié si applicable] │
|
||||
│ │
|
||||
│ Bio : Lorem ipsum dolor sit amet... │
|
||||
│ (300 caractères max) │
|
||||
│ │
|
||||
│ 🎧 1.2K abonnés │
|
||||
│ 📻 42 contenus │
|
||||
│ ⏱️ 18h de contenu créé │
|
||||
│ 🔊 54K écoutes totales │
|
||||
│ │
|
||||
│ [S'abonner] [Partager profil] [•••] │
|
||||
│ │
|
||||
│ ──────────────────────────────────── │
|
||||
│ │
|
||||
│ Contenus ▼ [Plus récents ▼] │
|
||||
│ │
|
||||
│ ┌──────────────────────────────────┐ │
|
||||
│ │ [Cover] Titre contenu 1 │ │
|
||||
│ │ 12 min · 🎧 2.3K · 📍 Paris │ │
|
||||
│ │ [▶️] │ │
|
||||
│ └──────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌──────────────────────────────────┐ │
|
||||
│ │ [Cover] Titre contenu 2 │ │
|
||||
│ │ 8 min · 🎧 5.1K · 📍 Lyon │ │
|
||||
│ │ [▶️] │ │
|
||||
│ └──────────────────────────────────┘ │
|
||||
│ │
|
||||
│ [Charger plus] │
|
||||
└────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
**Informations affichées** :
|
||||
|
||||
| Élément | Visibilité | Détails |
|
||||
|---------|------------|---------|
|
||||
| **Photo + pseudo** | ✅ Public | Identité visuelle |
|
||||
| **Badge vérifié ✓** | ✅ Public (si applicable) | Compte authentique |
|
||||
| **Bio** | ✅ Public | 0-300 caractères, markdown basique (gras, italique, liens) |
|
||||
| **Nombre abonnés** | ✅ Public | Arrondi si >1000 (ex: 1.2K, 54K) |
|
||||
| **Nombre contenus** | ✅ Public | Exact |
|
||||
| **Durée totale créée** | ✅ Public | Arrondi en heures (ex: 18h, 142h) |
|
||||
| **Écoutes totales** | ✅ Public | Arrondi (ex: 54K, 1.2M) |
|
||||
| **Liste abonnés** | ❌ Privé | Protection vie privée (RGPD) |
|
||||
| **Revenus** | ❌ Privé | Confidentialité financière |
|
||||
| **Localisation précise** | ❌ Privé | Sécurité |
|
||||
| **Email** | ❌ Privé | Anti-spam |
|
||||
|
||||
**Tri des contenus** :
|
||||
|
||||
| Option | Comportement |
|
||||
|--------|--------------|
|
||||
| **Plus récents** | Date publication DESC (défaut) |
|
||||
| **Plus populaires** | Écoutes complètes × (1 + (date_publication - now) / 90 jours) |
|
||||
| **Plus anciens** | Date publication ASC |
|
||||
| **Par tag** | Filtre multi-sélection tags |
|
||||
|
||||
**Recherche locale** :
|
||||
- Barre recherche dans profil : "Rechercher dans les contenus de @pseudo"
|
||||
- Recherche full-text sur titres + descriptions
|
||||
|
||||
**Actions menu [•••]** :
|
||||
- Partager profil
|
||||
- Signaler profil (spam, usurpation)
|
||||
- Bloquer créateur (masque tous ses contenus)
|
||||
|
||||
---
|
||||
|
||||
#### 15.2.2 Statistiques publiques
|
||||
|
||||
**Décision** : Stats arrondies et motivantes
|
||||
|
||||
**Affichage public** :
|
||||
|
||||
| Métrique | Format affichage | Exemple |
|
||||
|----------|------------------|---------|
|
||||
| **Abonnés** | Exact si <1000, arrondi sinon | 342 / 1.2K / 54K / 1.2M |
|
||||
| **Écoutes totales** | Arrondi dès 1000 | 842 / 5.4K / 142K / 2.1M |
|
||||
| **Contenus publiés** | Exact | 42 contenus |
|
||||
| **Durée totale** | Arrondi en heures | 18h / 142h de contenu |
|
||||
|
||||
**Métriques PRIVÉES (créateur uniquement)** :
|
||||
|
||||
| Métrique | Disponible dans dashboard créateur |
|
||||
|----------|-------------------------------------|
|
||||
| **Taux complétion moyen** | 78% (écoutes >80% / écoutes totales) |
|
||||
| **Évolution abonnés** | Graphique 30j / 90j / 1 an |
|
||||
| **Écoutes par contenu** | Tableau détaillé |
|
||||
| **Revenus** | Dashboard monétisation dédié |
|
||||
| **Taux conversion Premium** | Partages → conversions |
|
||||
| **Démographie** | Âge / zone géo (agrégée, anonymisée) |
|
||||
|
||||
**Justification** :
|
||||
- Arrondi = évite comparaisons anxiogènes
|
||||
- Preuve sociale pour nouveaux auditeurs (trust)
|
||||
- Gamification douce (motivation créateurs)
|
||||
- Privacy by design
|
||||
|
||||
---
|
||||
|
||||
#### 15.2.3 Badge vérifié
|
||||
|
||||
**Décision** : Badge unique ✓ (vérifié officiel)
|
||||
|
||||
**Critères d'attribution** (au moins UN des critères) :
|
||||
|
||||
1. **KYC monétisation validé** : identité vérifiée via Mangopay KYC
|
||||
2. **Célébrité / Média officiel** : validation manuelle équipe RoadWave
|
||||
3. **Communauté significative** : ≥10K abonnés + compte actif >6 mois
|
||||
|
||||
**Affichage** :
|
||||
- Badge bleu **✓** accolé au pseudo (partout : profil, player, recherche)
|
||||
- Tooltip au survol/appui long : "Compte vérifié"
|
||||
|
||||
**Processus d'obtention** :
|
||||
|
||||
| Type | Processus |
|
||||
|------|-----------|
|
||||
| **Automatique (KYC)** | Badge attribué dès validation documents Mangopay |
|
||||
| **Manuel (célébrité)** | Formulaire demande → équipe vérifie identité → validation 48-72h |
|
||||
| **Automatique (10K)** | Badge attribué automatiquement à 10K abonnés si compte >6 mois |
|
||||
|
||||
**Retrait du badge** :
|
||||
- Suspension monétisation → badge retiré temporairement
|
||||
- Strikes multiples → badge retiré définitivement
|
||||
- Usurpation identité détectée → ban + retrait
|
||||
|
||||
**Justification** :
|
||||
- Combat usurpations d'identité
|
||||
- Trust auditeurs (surtout pour médias/personnalités)
|
||||
- Simplicité (1 seul badge, pas de gamification excessive)
|
||||
- Coût : 0€ (champ boolean `verified` en DB)
|
||||
|
||||
---
|
||||
|
||||
### 15.3 Recherche
|
||||
|
||||
**Décision** : Recherche full-text + géo + filtres avancés
|
||||
|
||||
#### 15.3.1 Recherche par mot-clé
|
||||
|
||||
**Implémentation** : PostgreSQL full-text search (français)
|
||||
|
||||
**Configuration technique** :
|
||||
|
||||
```sql
|
||||
-- Index full-text optimisé français
|
||||
CREATE INDEX idx_content_search ON contents
|
||||
USING GIN(
|
||||
to_tsvector('french',
|
||||
coalesce(title, '') || ' ' ||
|
||||
coalesce(description, '') || ' ' ||
|
||||
coalesce(creator_pseudo, '')
|
||||
)
|
||||
);
|
||||
|
||||
-- Recherche avec ranking
|
||||
SELECT
|
||||
c.*,
|
||||
ts_rank(
|
||||
to_tsvector('french', c.title || ' ' || c.description),
|
||||
plainto_tsquery('french', $search_query)
|
||||
) AS rank
|
||||
FROM contents c
|
||||
WHERE to_tsvector('french', c.title || ' ' || c.description)
|
||||
@@ plainto_tsquery('french', $search_query)
|
||||
ORDER BY rank DESC, listen_count DESC
|
||||
LIMIT 20;
|
||||
```
|
||||
|
||||
**Champs indexés** :
|
||||
- Titre du contenu (poids × 3)
|
||||
- Description (poids × 1)
|
||||
- Pseudo créateur (poids × 2)
|
||||
- Tags (poids × 1.5)
|
||||
|
||||
**Fonctionnalités** :
|
||||
|
||||
| Feature | Description |
|
||||
|---------|-------------|
|
||||
| **Stemming français** | "voyages" trouve "voyage", "voyager", etc. |
|
||||
| **Correction auto** | Suggestion si 0 résultat |
|
||||
| **Recherches populaires** | "Essayez plutôt : balade paris, audio-guide louvre" |
|
||||
| **Historique personnel** | 10 dernières recherches sauvegardées |
|
||||
| **Autocomplete** | Suggestions pendant frappe (top 5) |
|
||||
|
||||
**Coût** : 0€ (PostgreSQL natif)
|
||||
|
||||
**Migration future** :
|
||||
- Si >100K contenus : Meilisearch (typo-tolerance avancée, ~20-50€/mois)
|
||||
- Si >1M contenus : Elasticsearch cluster
|
||||
|
||||
**Justification** :
|
||||
- PostgreSQL full-text = performant jusqu'à 500K contenus
|
||||
- Stemming français natif
|
||||
- 0€, aucune dépendance externe
|
||||
|
||||
---
|
||||
|
||||
#### 15.3.2 Recherche géographique
|
||||
|
||||
**Décision** : Recherche lieu + rayon paramétrable
|
||||
|
||||
**Interface utilisateur** :
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────┐
|
||||
│ 🔍 Recherche contenu... │
|
||||
├─────────────────────────────────────┤
|
||||
│ <20><> Lieu │
|
||||
│ [Paris, France ▼] │
|
||||
│ · Autour de moi (GPS actuel) │
|
||||
│ · Entrer une adresse/ville │
|
||||
│ │
|
||||
│ 📏 Rayon de recherche │
|
||||
│ [●─────────────────] 50 km │
|
||||
│ (curseur 5 km → 500 km) │
|
||||
│ │
|
||||
│ 🗺️ [Afficher sur carte] │
|
||||
└─────────────────────────────────────┘
|
||||
```
|
||||
|
||||
**Géocodage** :
|
||||
|
||||
| Service | Usage | Coût |
|
||||
|---------|-------|------|
|
||||
| **Nominatim (OSM)** | MVP (API publique) | 0€ (rate limit 1 req/s) |
|
||||
| **Nominatim self-hosted** | Scale (Docker) | 20-50€/mois VPS |
|
||||
| **Mapbox Geocoding** | Fallback premium | 0.50€ / 1000 requêtes |
|
||||
|
||||
**Processus de recherche géo** :
|
||||
|
||||
1. User tape "Louvre" ou "Paris"
|
||||
2. Autocomplete via Nominatim → liste suggestions
|
||||
3. User sélectionne → récupération coordonnées (lat, lon)
|
||||
4. Requête PostGIS :
|
||||
|
||||
```sql
|
||||
SELECT c.*,
|
||||
ST_Distance(c.location::geography, ST_Point($lon, $lat)::geography) AS distance
|
||||
FROM contents c
|
||||
WHERE ST_DWithin(
|
||||
c.location::geography,
|
||||
ST_Point($lon, $lat)::geography,
|
||||
$radius_meters
|
||||
)
|
||||
ORDER BY distance ASC;
|
||||
```
|
||||
|
||||
**Affichage résultats** :
|
||||
- Tri par défaut : distance croissante
|
||||
- Indication distance : "À 2.3 km" / "À 15 km" / "À 142 km"
|
||||
- Option carte : markers cliquables (clustering si >50 résultats)
|
||||
|
||||
**Coût** :
|
||||
- MVP : 0€ (Nominatim public)
|
||||
- Scale : 20-50€/mois (Nominatim self-hosted Docker)
|
||||
|
||||
**Justification** :
|
||||
- Essentiel pour tourisme / planification trajet
|
||||
- OpenStreetMap = pas de dépendance Google
|
||||
- PostGIS = performant (index GIST natif)
|
||||
|
||||
---
|
||||
|
||||
#### 15.3.3 Filtres avancés
|
||||
|
||||
**Décision** : 7 catégories de filtres combinables
|
||||
|
||||
**Interface filtres** :
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────┐
|
||||
│ Filtres [×] │
|
||||
├─────────────────────────────────────┤
|
||||
│ Type de contenu │
|
||||
│ ☐ Contenu court (<5 min) │
|
||||
│ ☐ Podcast (>5 min) │
|
||||
│ ☐ Radio live │
|
||||
│ ☐ Audio-guide │
|
||||
│ │
|
||||
│ Durée │
|
||||
│ ○ Toutes durées │
|
||||
│ ○ <5 min │
|
||||
│ ○ 5-15 min │
|
||||
│ ○ 15-30 min │
|
||||
│ ○ >30 min │
|
||||
│ │
|
||||
│ Classification âge │
|
||||
│ ☐ Tout public │
|
||||
│ ☐ 13+ │
|
||||
│ ☐ 16+ │
|
||||
│ ☐ 18+ │
|
||||
│ │
|
||||
│ Géo-pertinence │
|
||||
│ ☐ Ancré (lieu précis) │
|
||||
│ ☐ Contextuel (zone large) │
|
||||
│ ☐ Neutre (national) │
|
||||
│ │
|
||||
│ Tags (multi-sélection) │
|
||||
│ ☐ Automobile ☐ Voyage │
|
||||
│ ☐ Famille ☐ Histoire │
|
||||
│ ☐ Économie ☐ Sciences │
|
||||
│ ... (liste complète tags) │
|
||||
│ │
|
||||
│ Date de publication │
|
||||
│ ○ Toutes dates │
|
||||
│ ○ Dernières 24h │
|
||||
│ ○ Cette semaine │
|
||||
│ ○ Ce mois │
|
||||
│ ○ Cette année │
|
||||
│ │
|
||||
│ Abonnement │
|
||||
│ ○ Tous les contenus │
|
||||
│ ○ Gratuits uniquement │
|
||||
│ ○ Premium uniquement 👑 │
|
||||
│ │
|
||||
│ ────────────────────────────── │
|
||||
│ [Réinitialiser] [Appliquer] │
|
||||
└─────────────────────────────────────┘
|
||||
```
|
||||
|
||||
**Options de tri** :
|
||||
|
||||
| Tri | Algorithme |
|
||||
|-----|-----------|
|
||||
| **Pertinence** | Score recherche × (1 + log(listen_count + 1)) |
|
||||
| **Popularité** | Écoutes complètes derniers 30j DESC |
|
||||
| **Récent** | Date publication DESC |
|
||||
| **Proximité** | Distance GPS ASC (si recherche géo active) |
|
||||
| **Durée** | Durée audio ASC ou DESC |
|
||||
|
||||
**Sauvegarde de recherches** :
|
||||
|
||||
- Bouton "💾 Sauvegarder cette recherche"
|
||||
- Nom personnalisable : "Podcasts voyage Paris"
|
||||
- Maximum **5 recherches sauvegardées**
|
||||
- Accès rapide : onglet "Recherches sauvegardées" dans page recherche
|
||||
- Notifications optionnelles : "3 nouveaux contenus dans 'Podcasts voyage Paris'"
|
||||
|
||||
**Performances** :
|
||||
|
||||
```sql
|
||||
-- Index composites pour filtres
|
||||
CREATE INDEX idx_content_filters ON contents (
|
||||
content_type,
|
||||
duration,
|
||||
age_rating,
|
||||
geo_type,
|
||||
published_at
|
||||
);
|
||||
|
||||
-- Index GIN pour tags
|
||||
CREATE INDEX idx_content_tags ON contents USING GIN(tags);
|
||||
```
|
||||
|
||||
**Coût** : 0€ (PostgreSQL + index standards)
|
||||
|
||||
**Justification** :
|
||||
- Filtres essentiels pour découvrabilité
|
||||
- Combinables = puissance maximale
|
||||
- Sauvegarde = gain temps utilisateurs réguliers
|
||||
|
||||
---
|
||||
|
||||
#### 15.3.4 Page de résultats
|
||||
|
||||
**Décision** : Liste avec previews enrichies
|
||||
|
||||
**Layout résultats** :
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────┐
|
||||
│ 🔍 "voyage paris" │
|
||||
│ 42 résultats · Tri : Pertinence ▼ │
|
||||
│ [Filtres] [Carte] │
|
||||
├─────────────────────────────────────────┤
|
||||
│ ┌─────────────────────────────────────┐ │
|
||||
│ │ [Cover ] Balade à Paris │ │
|
||||
│ │ [16:9 ] @paris_stories ✓ │ │
|
||||
│ │ [Image ] 12 min · 🎧 2.3K │ │
|
||||
│ │ 📍 Paris 5e · Ancré │ │
|
||||
│ │ 🏷️ #Voyage #Histoire │ │
|
||||
│ │ [▶️ Écouter] [⋮] │ │
|
||||
│ └─────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌─────────────────────────────────────┐ │
|
||||
│ │ [Cover ] Secrets Montmartre │ │
|
||||
│ │ [16:9 ] @explore_paris │ │
|
||||
│ │ [Image ] 8 min · 🎧 5.1K │ │
|
||||
│ │ 📍 Paris 18e · Guide │ │
|
||||
│ │ 🏷️ #Voyage #Art │ │
|
||||
│ │ [▶️ Écouter] [⋮] │ │
|
||||
│ └─────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ [Charger plus] (20 suivants) │
|
||||
└─────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
**Informations par résultat** :
|
||||
|
||||
| Élément | Affichage |
|
||||
|---------|-----------|
|
||||
| **Cover image** | 16:9, 120×68 px, lazy loading |
|
||||
| **Titre** | Tronqué 2 lignes max |
|
||||
| **Créateur** | @pseudo + badge ✓ si vérifié, cliquable → profil |
|
||||
| **Durée** | Format : "3 min" / "12 min" / "1h 24 min" |
|
||||
| **Écoutes** | Arrondi : "2.3K" / "54K" / "1.2M" |
|
||||
| **Localisation** | Ville + type géo (Ancré/Contextuel/Neutre) |
|
||||
| **Tags** | Maximum 3 premiers tags |
|
||||
| **Badge Premium** | 👑 si contenu premium |
|
||||
| **Distance** | Si recherche géo : "À 2.3 km" |
|
||||
|
||||
**Actions contextuelles [⋮]** :
|
||||
- Partager
|
||||
- Ajouter à une playlist (future feature)
|
||||
- Télécharger (offline)
|
||||
- Signaler
|
||||
|
||||
**Pagination** :
|
||||
- **20 résultats** par page
|
||||
- Infinite scroll (charger automatiquement si scroll >80%)
|
||||
- Bouton "Charger 20 suivants" en bas (fallback si scroll auto désactivé)
|
||||
|
||||
**Vue carte (alternative)** :
|
||||
- Bouton toggle "Liste / Carte"
|
||||
- Map Leaflet (OpenStreetMap)
|
||||
- Markers cliquables → popup avec preview
|
||||
- Clustering si >50 résultats proches
|
||||
|
||||
**Coût** : 0€ (Leaflet open source + OSM tiles gratuit)
|
||||
|
||||
**Justification** :
|
||||
- Équilibre information / compacité
|
||||
- Lazy loading = performances
|
||||
- Infinite scroll = UX moderne
|
||||
|
||||
---
|
||||
|
||||
## Récapitulatif Section 15
|
||||
|
||||
| Point | Décision | Coût | Complexité |
|
||||
|-------|----------|------|------------|
|
||||
| **15.1.1** Bouton partager | Disponible partout (⬆️), menu natif OS | 0€ | Faible |
|
||||
| **15.1.2** Lien partagé | Web player + deep link + Open Graph SEO | 0€ | Moyenne |
|
||||
| **15.1.3** Premium partagé | Preview 30s + paywall overlay | 0€ | Faible |
|
||||
| **15.2.1** Page profil | Profil public complet (stats + bio + contenus + tri) | 0€ | Faible |
|
||||
| **15.2.2** Stats publiques | Arrondies (abonnés, écoutes, durée totale) | 0€ | Faible |
|
||||
| **15.2.3** Badge vérifié | ✓ si KYC/célébrité/>10K abonnés | 0€ | Faible |
|
||||
| **15.3.1** Recherche texte | PostgreSQL full-text french + stemming | 0€ | Moyenne |
|
||||
| **15.3.2** Recherche géo | Lieu + rayon (Nominatim OSM) | 0-50€/mois | Moyenne |
|
||||
| **15.3.3** Filtres | 7 catégories combinables + sauvegarde recherches | 0€ | Moyenne |
|
||||
| **15.3.4** Page résultats | Liste enrichie + vue carte Leaflet + infinite scroll | 0€ | Moyenne |
|
||||
|
||||
**Coût total MVP : 0-50€/mois** (Nominatim self-hosted optionnel)
|
||||
|
||||
---
|
||||
|
||||
## Points d'attention pour Gherkin
|
||||
|
||||
- Tester partage contenu public vs Premium (preview 30s)
|
||||
- Tester deep linking iOS/Android (ouverture app si installée)
|
||||
- Tester Open Graph (aperçu correct sur WhatsApp, Twitter, Facebook)
|
||||
- Tester profil public (stats arrondies, badge vérifié)
|
||||
- Tester recherche full-text français (stemming, accents)
|
||||
- Tester recherche géo + rayon (PostGIS distance)
|
||||
- Tester combinaison filtres multiples (AND logic)
|
||||
- Tester sauvegarde recherches (max 5)
|
||||
- Tester pagination infinite scroll + fallback bouton
|
||||
- Tester vue carte Leaflet (clustering, markers cliquables)
|
||||
Reference in New Issue
Block a user