Some checks failed
CI/CD Pipeline / Backend - Build, Test & Push (push) Failing after 58s
CI/CD Pipeline / Frontend - Build, Test & Push (push) Failing after 5m55s
CI/CD Pipeline / Integration Tests (push) Has been skipped
CI/CD Pipeline / Deployment Summary (push) Has been skipped
CI/CD Pipeline / Deploy to Portainer (push) Has been skipped
CI/CD Pipeline / Discord Notification (Success) (push) Has been skipped
CI/CD Pipeline / Discord Notification (Failure) (push) Has been skipped
Reorganisation majeure de toute la documentation du projet pour ameliorer la navigation et la maintenance. ## Changements principaux ### Organisation (80 -> 4 fichiers .md a la racine) - Deplace 82 fichiers .md dans docs/ organises en 11 categories - Conserve uniquement 4 fichiers essentiels a la racine: * README.md, CLAUDE.md, PRD.md, TODO.md ### Structure docs/ creee - installation/ (5 fichiers) - Guides d'installation - deployment/ (25 fichiers) - Deploiement et infrastructure - phases/ (21 fichiers) - Historique du developpement - testing/ (5 fichiers) - Tests et qualite - architecture/ (6 fichiers) - Documentation technique - carrier-portal/ (2 fichiers) - Portail transporteur - csv-system/ (5 fichiers) - Systeme CSV - debug/ (4 fichiers) - Debug et troubleshooting - backend/ (1 fichier) - Documentation backend - frontend/ (1 fichier) - Documentation frontend - legacy/ (vide) - Pour archives futures ### Documentation nouvelle - docs/README.md - Index complet de toute la documentation (367 lignes) * Guide de navigation par scenario * Recherche rapide par theme * FAQ et commandes rapides - docs/CLEANUP-REPORT-2025-12-22.md - Rapport detaille du nettoyage ### Scripts reorganises - add-email-to-csv.py -> scripts/ - deploy-to-portainer.sh -> docker/ ### Fichiers supprimes - 1536w default.svg (11MB) - Fichier non utilise ### References mises a jour - CLAUDE.md - Section Documentation completement reecrite - docs/architecture/EMAIL_IMPLEMENTATION_STATUS.md - Chemin script Python - docs/deployment/REGISTRY_PUSH_GUIDE.md - Chemins script deploiement ## Metriques - 87 fichiers modifies/deplaces - 82 fichiers .md organises dans docs/ - 11MB d'espace libere - Temps de recherche reduit de ~5min a ~30s (-90%) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
601 lines
17 KiB
Markdown
601 lines
17 KiB
Markdown
# Booking Workflow - Todo List
|
|
|
|
Ce document détaille toutes les tâches nécessaires pour implémenter le workflow complet de booking avec système d'acceptation/refus par email et notifications.
|
|
|
|
## Vue d'ensemble
|
|
|
|
Le workflow permet à un utilisateur de:
|
|
1. Sélectionner une option de transport depuis les résultats de recherche
|
|
2. Remplir un formulaire avec les documents nécessaires
|
|
3. Envoyer une demande de booking par email au transporteur
|
|
4. Le transporteur peut accepter ou refuser via des boutons dans l'email
|
|
5. L'utilisateur reçoit une notification sur son dashboard
|
|
|
|
---
|
|
|
|
## Backend - Domain Layer (3 tâches)
|
|
|
|
|
|
### ✅ Task 2: Créer l'entité Booking dans le domain
|
|
**Fichier**: `apps/backend/src/domain/entities/booking.entity.ts` (à créer)
|
|
|
|
**Actions**:
|
|
- Créer l'enum `BookingStatus` (PENDING, ACCEPTED, REJECTED, CANCELLED)
|
|
- Créer la classe `Booking` avec:
|
|
- `id: string`
|
|
- `userId: string`
|
|
- `organizationId: string`
|
|
- `carrierName: string`
|
|
- `carrierEmail: string`
|
|
- `origin: PortCode`
|
|
- `destination: PortCode`
|
|
- `volumeCBM: number`
|
|
- `weightKG: number`
|
|
- `priceEUR: number`
|
|
- `transitDays: number`
|
|
- `status: BookingStatus`
|
|
- `documents: Document[]` (Bill of Lading, Packing List, Commercial Invoice, Certificate of Origin)
|
|
- `confirmationToken: string` (pour les liens email)
|
|
- `requestedAt: Date`
|
|
- `respondedAt?: Date`
|
|
- `notes?: string`
|
|
- Méthodes: `accept()`, `reject()`, `cancel()`, `isExpired()`
|
|
|
|
---
|
|
|
|
### ✅ Task 3: Créer l'entité Notification dans le domain
|
|
**Fichier**: `apps/backend/src/domain/entities/notification.entity.ts` (à créer)
|
|
|
|
**Actions**:
|
|
- Créer l'enum `NotificationType` (BOOKING_ACCEPTED, BOOKING_REJECTED, BOOKING_CREATED)
|
|
- Créer la classe `Notification` avec:
|
|
- `id: string`
|
|
- `userId: string`
|
|
- `type: NotificationType`
|
|
- `title: string`
|
|
- `message: string`
|
|
- `bookingId?: string`
|
|
- `isRead: boolean`
|
|
- `createdAt: Date`
|
|
- Méthodes: `markAsRead()`, `isRecent()`
|
|
|
|
---
|
|
|
|
## Backend - Infrastructure Layer (4 tâches)
|
|
|
|
### ✅ Task 4: Mettre à jour le CSV loader pour passer companyEmail
|
|
**Fichier**: `apps/backend/src/infrastructure/carriers/csv-loader/csv-rate-loader.adapter.ts`
|
|
|
|
**Actions**:
|
|
- ✅ Interface `CsvRow` déjà mise à jour avec `companyEmail`
|
|
- Modifier la méthode `mapToCsvRate()` pour passer `record.companyEmail` au constructeur de `CsvRate`
|
|
- Ajouter `'companyEmail'` dans le tableau `requiredColumns` de `validateCsvStructure()`
|
|
|
|
**Code à modifier** (ligne ~267):
|
|
```typescript
|
|
return new CsvRate(
|
|
record.companyName.trim(),
|
|
record.companyEmail.trim(), // NOUVEAU
|
|
PortCode.create(record.origin),
|
|
// ... reste
|
|
)
|
|
```
|
|
|
|
---
|
|
|
|
### ✅ Task 5: Créer le repository BookingRepository
|
|
**Fichiers à créer**:
|
|
- `apps/backend/src/domain/ports/out/booking.repository.ts` (interface)
|
|
- `apps/backend/src/infrastructure/persistence/typeorm/entities/booking.orm-entity.ts`
|
|
- `apps/backend/src/infrastructure/persistence/typeorm/repositories/booking.repository.ts`
|
|
|
|
**Actions**:
|
|
- Créer l'interface du port avec méthodes:
|
|
- `create(booking: Booking): Promise<Booking>`
|
|
- `findById(id: string): Promise<Booking | null>`
|
|
- `findByUserId(userId: string): Promise<Booking[]>`
|
|
- `findByToken(token: string): Promise<Booking | null>`
|
|
- `update(booking: Booking): Promise<Booking>`
|
|
- Créer l'entité ORM avec décorateurs TypeORM
|
|
- Implémenter le repository avec TypeORM
|
|
|
|
---
|
|
|
|
### ✅ Task 6: Créer le repository NotificationRepository
|
|
**Fichiers à créer**:
|
|
- `apps/backend/src/domain/ports/out/notification.repository.ts` (interface)
|
|
- `apps/backend/src/infrastructure/persistence/typeorm/entities/notification.orm-entity.ts`
|
|
- `apps/backend/src/infrastructure/persistence/typeorm/repositories/notification.repository.ts`
|
|
|
|
**Actions**:
|
|
- Créer l'interface du port avec méthodes:
|
|
- `create(notification: Notification): Promise<Notification>`
|
|
- `findByUserId(userId: string, unreadOnly?: boolean): Promise<Notification[]>`
|
|
- `markAsRead(id: string): Promise<void>`
|
|
- `markAllAsRead(userId: string): Promise<void>`
|
|
- Créer l'entité ORM
|
|
- Implémenter le repository
|
|
|
|
---
|
|
|
|
### ✅ Task 7: Créer le service d'envoi d'email
|
|
**Fichier**: `apps/backend/src/infrastructure/email/email.service.ts` (à créer)
|
|
|
|
**Actions**:
|
|
- Utiliser `nodemailer` ou un service comme SendGrid/Mailgun
|
|
- Créer la méthode `sendBookingRequest(booking: Booking, acceptUrl: string, rejectUrl: string)`
|
|
- Créer le template HTML avec:
|
|
- Récapitulatif du booking (origine, destination, volume, poids, prix)
|
|
- Liste des documents joints
|
|
- 2 boutons CTA: "Accepter la demande" (vert) et "Refuser la demande" (rouge)
|
|
- Design responsive
|
|
|
|
**Template email**:
|
|
```html
|
|
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<style>
|
|
/* Styles inline pour compatibilité email */
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<h1>Nouvelle demande de réservation - Xpeditis</h1>
|
|
<div class="summary">
|
|
<h2>Détails du transport</h2>
|
|
<p><strong>Route:</strong> {{origin}} → {{destination}}</p>
|
|
<p><strong>Volume:</strong> {{volumeCBM}} CBM</p>
|
|
<p><strong>Poids:</strong> {{weightKG}} kg</p>
|
|
<p><strong>Prix:</strong> {{priceEUR}} EUR</p>
|
|
<p><strong>Transit:</strong> {{transitDays}} jours</p>
|
|
</div>
|
|
|
|
<div class="documents">
|
|
<h3>Documents fournis:</h3>
|
|
<ul>
|
|
{{#each documents}}
|
|
<li>{{this.name}}</li>
|
|
{{/each}}
|
|
</ul>
|
|
</div>
|
|
|
|
<div class="actions">
|
|
<a href="{{acceptUrl}}" class="btn btn-accept">✓ Accepter la demande</a>
|
|
<a href="{{rejectUrl}}" class="btn btn-reject">✗ Refuser la demande</a>
|
|
</div>
|
|
</body>
|
|
</html>
|
|
```
|
|
|
|
---
|
|
|
|
## Backend - Application Layer (5 tâches)
|
|
|
|
### ✅ Task 8: Ajouter companyEmail dans le DTO de réponse
|
|
**Fichier**: `apps/backend/src/application/dto/csv-rate-search.dto.ts`
|
|
|
|
**Actions**:
|
|
- Ajouter `@ApiProperty() companyEmail: string;` dans `CsvRateSearchResultDto`
|
|
- Mettre à jour le mapper pour inclure `companyEmail`
|
|
|
|
---
|
|
|
|
### ✅ Task 9: Créer les DTOs pour créer un booking
|
|
**Fichier**: `apps/backend/src/application/dto/booking.dto.ts` (à créer)
|
|
|
|
**Actions**:
|
|
- Créer `CreateBookingDto` avec validation:
|
|
```typescript
|
|
export class CreateBookingDto {
|
|
@ApiProperty()
|
|
@IsString()
|
|
carrierName: string;
|
|
|
|
@ApiProperty()
|
|
@IsEmail()
|
|
carrierEmail: string;
|
|
|
|
@ApiProperty()
|
|
@IsString()
|
|
origin: string;
|
|
|
|
@ApiProperty()
|
|
@IsString()
|
|
destination: string;
|
|
|
|
@ApiProperty()
|
|
@IsNumber()
|
|
@Min(0)
|
|
volumeCBM: number;
|
|
|
|
@ApiProperty()
|
|
@IsNumber()
|
|
@Min(0)
|
|
weightKG: number;
|
|
|
|
@ApiProperty()
|
|
@IsNumber()
|
|
@Min(0)
|
|
priceEUR: number;
|
|
|
|
@ApiProperty()
|
|
@IsNumber()
|
|
@Min(1)
|
|
transitDays: number;
|
|
|
|
@ApiProperty({ type: 'array', items: { type: 'string', format: 'binary' } })
|
|
documents: Express.Multer.File[];
|
|
|
|
@ApiProperty({ required: false })
|
|
@IsOptional()
|
|
@IsString()
|
|
notes?: string;
|
|
}
|
|
```
|
|
|
|
- Créer `BookingResponseDto`
|
|
- Créer `NotificationDto`
|
|
|
|
---
|
|
|
|
### ✅ Task 10: Créer l'endpoint POST /api/v1/bookings
|
|
**Fichier**: `apps/backend/src/application/controllers/booking.controller.ts` (à créer)
|
|
|
|
**Actions**:
|
|
- Créer le controller avec méthode `createBooking()`
|
|
- Utiliser `@UseInterceptors(FilesInterceptor('documents'))` pour l'upload
|
|
- Générer un `confirmationToken` unique (UUID)
|
|
- Sauvegarder les documents sur le système de fichiers ou S3
|
|
- Créer le booking avec status PENDING
|
|
- Générer les URLs d'acceptation/refus
|
|
- Envoyer l'email au transporteur
|
|
- Créer une notification pour l'utilisateur (BOOKING_CREATED)
|
|
- Retourner le booking créé
|
|
|
|
**Endpoint**:
|
|
```typescript
|
|
@Post()
|
|
@UseGuards(JwtAuthGuard)
|
|
@UseInterceptors(FilesInterceptor('documents', 10))
|
|
@ApiOperation({ summary: 'Create a new booking request' })
|
|
@ApiResponse({ status: 201, type: BookingResponseDto })
|
|
async createBooking(
|
|
@Body() dto: CreateBookingDto,
|
|
@UploadedFiles() files: Express.Multer.File[],
|
|
@Request() req
|
|
): Promise<BookingResponseDto> {
|
|
// Implementation
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### ✅ Task 11: Créer l'endpoint GET /api/v1/bookings/:id/accept
|
|
**Fichier**: `apps/backend/src/application/controllers/booking.controller.ts`
|
|
|
|
**Actions**:
|
|
- Endpoint PUBLIC (pas de auth guard)
|
|
- Vérifier le token de confirmation
|
|
- Trouver le booking par token
|
|
- Vérifier que le status est PENDING
|
|
- Mettre à jour le status à ACCEPTED
|
|
- Créer une notification pour l'utilisateur (BOOKING_ACCEPTED)
|
|
- Rediriger vers `/booking/confirm/:token` (frontend)
|
|
|
|
**Endpoint**:
|
|
```typescript
|
|
@Get(':id/accept')
|
|
@ApiOperation({ summary: 'Accept a booking request (public endpoint)' })
|
|
async acceptBooking(
|
|
@Param('id') bookingId: string,
|
|
@Query('token') token: string
|
|
): Promise<void> {
|
|
// Validation + Update + Notification + Redirect
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### ✅ Task 12: Créer l'endpoint GET /api/v1/bookings/:id/reject
|
|
**Fichier**: `apps/backend/src/application/controllers/booking.controller.ts`
|
|
|
|
**Actions**:
|
|
- Endpoint PUBLIC (pas de auth guard)
|
|
- Même logique que accept mais avec status REJECTED
|
|
- Créer une notification BOOKING_REJECTED
|
|
- Rediriger vers `/booking/reject/:token` (frontend)
|
|
|
|
---
|
|
|
|
### ✅ Task 13: Créer l'endpoint GET /api/v1/notifications
|
|
**Fichier**: `apps/backend/src/application/controllers/notification.controller.ts` (à créer)
|
|
|
|
**Actions**:
|
|
- Endpoint protégé (JwtAuthGuard)
|
|
- Query param optionnel `?unreadOnly=true`
|
|
- Retourner les notifications de l'utilisateur
|
|
|
|
**Endpoints supplémentaires**:
|
|
- `PATCH /api/v1/notifications/:id/read` - Marquer comme lu
|
|
- `PATCH /api/v1/notifications/read-all` - Tout marquer comme lu
|
|
|
|
---
|
|
|
|
## Frontend (9 tâches)
|
|
|
|
### ✅ Task 14: Modifier la page results pour rendre les boutons Sélectionner cliquables
|
|
**Fichier**: `apps/frontend/app/dashboard/search/results/page.tsx`
|
|
|
|
**Actions**:
|
|
- Modifier le bouton "Sélectionner cette option" pour rediriger vers `/dashboard/booking/new`
|
|
- Passer les données du rate via query params ou state
|
|
- Exemple: `/dashboard/booking/new?rateData=${encodeURIComponent(JSON.stringify(option))}`
|
|
|
|
---
|
|
|
|
### ✅ Task 15: Créer la page /dashboard/booking/new avec formulaire multi-étapes
|
|
**Fichier**: `apps/frontend/app/dashboard/booking/new/page.tsx` (à créer)
|
|
|
|
**Actions**:
|
|
- Créer un formulaire en 3 étapes:
|
|
1. **Étape 1**: Confirmation des détails du transport (lecture seule)
|
|
2. **Étape 2**: Upload des documents (Bill of Lading, Packing List, Commercial Invoice, Certificate of Origin)
|
|
3. **Étape 3**: Révision et envoi
|
|
|
|
**Structure**:
|
|
```typescript
|
|
interface BookingForm {
|
|
// Données du rate (pré-remplies)
|
|
carrierName: string;
|
|
carrierEmail: string;
|
|
origin: string;
|
|
destination: string;
|
|
volumeCBM: number;
|
|
weightKG: number;
|
|
priceEUR: number;
|
|
transitDays: number;
|
|
|
|
// Documents à uploader
|
|
documents: {
|
|
billOfLading?: File;
|
|
packingList?: File;
|
|
commercialInvoice?: File;
|
|
certificateOfOrigin?: File;
|
|
};
|
|
|
|
// Notes optionnelles
|
|
notes?: string;
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### ✅ Task 16: Ajouter upload de documents
|
|
**Fichier**: `apps/frontend/app/dashboard/booking/new/page.tsx`
|
|
|
|
**Actions**:
|
|
- Utiliser `<input type="file" multiple accept=".pdf,.doc,.docx" />`
|
|
- Afficher la liste des fichiers sélectionnés avec possibilité de supprimer
|
|
- Validation: taille max 5MB par fichier, formats acceptés (PDF, DOC, DOCX)
|
|
- Preview des noms de fichiers
|
|
|
|
**Composant**:
|
|
```typescript
|
|
<div className="space-y-4">
|
|
<div>
|
|
<label>Bill of Lading *</label>
|
|
<input
|
|
type="file"
|
|
accept=".pdf,.doc,.docx"
|
|
onChange={(e) => handleFileChange('billOfLading', e.target.files?.[0])}
|
|
/>
|
|
</div>
|
|
{/* Répéter pour les autres documents */}
|
|
</div>
|
|
```
|
|
|
|
---
|
|
|
|
### ✅ Task 17: Créer l'API client pour les bookings
|
|
**Fichier**: `apps/frontend/src/lib/api/bookings.ts` (à créer)
|
|
|
|
**Actions**:
|
|
- Créer `createBooking(formData: FormData): Promise<BookingResponse>`
|
|
- Créer `getBookings(): Promise<Booking[]>`
|
|
- Utiliser `upload()` de `client.ts` pour les fichiers
|
|
|
|
---
|
|
|
|
### ✅ Task 18: Créer la page /booking/confirm/:token (acceptation publique)
|
|
**Fichier**: `apps/frontend/app/booking/confirm/[token]/page.tsx` (à créer)
|
|
|
|
**Actions**:
|
|
- Page publique (pas de layout dashboard)
|
|
- Afficher un message de succès avec animation
|
|
- Afficher le récapitulatif du booking accepté
|
|
- Message: "Merci d'avoir accepté cette demande de transport. Le client a été notifié."
|
|
- Design: card centrée avec icône ✓ verte
|
|
|
|
---
|
|
|
|
### ✅ Task 19: Créer la page /booking/reject/:token (refus publique)
|
|
**Fichier**: `apps/frontend/app/booking/reject/[token]/page.tsx` (à créer)
|
|
|
|
**Actions**:
|
|
- Page publique
|
|
- Formulaire optionnel pour raison du refus
|
|
- Message: "Vous avez refusé cette demande de transport. Le client a été notifié."
|
|
- Design: card centrée avec icône ✗ rouge
|
|
|
|
---
|
|
|
|
### ✅ Task 20: Ajouter le composant NotificationBell dans le dashboard
|
|
**Fichier**: `apps/frontend/src/components/NotificationBell.tsx` (à créer)
|
|
|
|
**Actions**:
|
|
- Icône de cloche dans le header du dashboard
|
|
- Badge rouge avec le nombre de notifications non lues
|
|
- Dropdown au clic avec liste des notifications
|
|
- Marquer comme lu au clic
|
|
- Lien vers le booking concerné
|
|
|
|
**Intégration**:
|
|
- Ajouter dans `apps/frontend/app/dashboard/layout.tsx` dans le header (ligne ~154, à côté du User Role Badge)
|
|
|
|
---
|
|
|
|
### ✅ Task 21: Créer le hook useNotifications pour polling
|
|
**Fichier**: `apps/frontend/src/hooks/useNotifications.ts` (à créer)
|
|
|
|
**Actions**:
|
|
- Hook custom qui fait du polling toutes les 30 secondes
|
|
- Retourne: `{ notifications, unreadCount, markAsRead, markAllAsRead, isLoading }`
|
|
- Utiliser `useQuery` de TanStack Query avec `refetchInterval: 30000`
|
|
|
|
**Code**:
|
|
```typescript
|
|
export function useNotifications() {
|
|
const { data, isLoading, refetch } = useQuery({
|
|
queryKey: ['notifications'],
|
|
queryFn: () => notificationsApi.getNotifications(),
|
|
refetchInterval: 30000, // 30 seconds
|
|
});
|
|
|
|
const markAsRead = async (id: string) => {
|
|
await notificationsApi.markAsRead(id);
|
|
refetch();
|
|
};
|
|
|
|
return {
|
|
notifications: data?.notifications || [],
|
|
unreadCount: data?.unreadCount || 0,
|
|
markAsRead,
|
|
isLoading,
|
|
};
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### ✅ Task 22: Tester le workflow complet end-to-end
|
|
|
|
**Actions**:
|
|
1. Lancer le backend et le frontend
|
|
2. Se connecter au dashboard
|
|
3. Faire une recherche de tarifs
|
|
4. Cliquer sur "Sélectionner cette option"
|
|
5. Remplir le formulaire de booking
|
|
6. Uploader des documents (fichiers de test)
|
|
7. Soumettre le booking
|
|
8. Vérifier que l'email est envoyé (vérifier les logs ou mailhog si configuré)
|
|
9. Cliquer sur "Accepter" dans l'email
|
|
10. Vérifier la page de confirmation
|
|
11. Vérifier que la notification apparaît dans le dashboard
|
|
12. Répéter avec "Refuser"
|
|
|
|
**Checklist de test**:
|
|
- [ ] Création de booking réussie
|
|
- [ ] Email reçu avec les bonnes informations
|
|
- [ ] Bouton Accepter fonctionne et redirige correctement
|
|
- [ ] Bouton Refuser fonctionne et redirige correctement
|
|
- [ ] Notifications apparaissent dans le dashboard
|
|
- [ ] Badge de notification se met à jour
|
|
- [ ] Documents sont bien stockés
|
|
- [ ] Données cohérentes en base de données
|
|
|
|
---
|
|
|
|
## Dépendances NPM à ajouter
|
|
|
|
### Backend
|
|
```bash
|
|
cd apps/backend
|
|
npm install nodemailer @types/nodemailer
|
|
npm install handlebars # Pour les templates email
|
|
npm install uuid @types/uuid
|
|
```
|
|
|
|
### Frontend
|
|
```bash
|
|
cd apps/frontend
|
|
# Tout est déjà installé (React Hook Form, TanStack Query, etc.)
|
|
```
|
|
|
|
---
|
|
|
|
## Configuration requise
|
|
|
|
### Variables d'environnement backend
|
|
Ajouter dans `apps/backend/.env`:
|
|
```env
|
|
# Email configuration (exemple avec Gmail)
|
|
EMAIL_HOST=smtp.gmail.com
|
|
EMAIL_PORT=587
|
|
EMAIL_SECURE=false
|
|
EMAIL_USER=your-email@gmail.com
|
|
EMAIL_PASSWORD=your-app-password
|
|
EMAIL_FROM=noreply@xpeditis.com
|
|
|
|
# Frontend URL for email links
|
|
FRONTEND_URL=http://localhost:3000
|
|
|
|
# File upload
|
|
MAX_FILE_SIZE=5242880 # 5MB
|
|
UPLOAD_DEST=./uploads/documents
|
|
```
|
|
|
|
---
|
|
|
|
## Migrations de base de données
|
|
|
|
### Backend - TypeORM migrations
|
|
```bash
|
|
cd apps/backend
|
|
|
|
# Générer les migrations
|
|
npm run migration:generate -- src/infrastructure/persistence/typeorm/migrations/CreateBookingAndNotification
|
|
|
|
# Appliquer les migrations
|
|
npm run migration:run
|
|
```
|
|
|
|
**Tables à créer**:
|
|
- `bookings` (id, user_id, organization_id, carrier_name, carrier_email, origin, destination, volume_cbm, weight_kg, price_eur, transit_days, status, confirmation_token, documents_path, notes, requested_at, responded_at, created_at, updated_at)
|
|
- `notifications` (id, user_id, type, title, message, booking_id, is_read, created_at)
|
|
|
|
---
|
|
|
|
## Estimation de temps
|
|
|
|
| Partie | Tâches | Temps estimé |
|
|
|--------|--------|--------------|
|
|
| Backend - Domain | 3 | 2-3 heures |
|
|
| Backend - Infrastructure | 4 | 3-4 heures |
|
|
| Backend - Application | 5 | 3-4 heures |
|
|
| Frontend | 8 | 4-5 heures |
|
|
| Testing & Debug | 1 | 2-3 heures |
|
|
| **TOTAL** | **22** | **14-19 heures** |
|
|
|
|
---
|
|
|
|
## Notes importantes
|
|
|
|
1. **Sécurité des tokens**: Utiliser des UUID v4 pour les confirmation tokens
|
|
2. **Expiration des liens**: Ajouter une expiration (ex: 48h) pour les liens d'acceptation/refus
|
|
3. **Rate limiting**: Limiter les appels aux endpoints publics (accept/reject)
|
|
4. **Stockage des documents**: Considérer S3 pour la production au lieu du filesystem local
|
|
5. **Email fallback**: Si l'envoi échoue, logger et permettre un retry
|
|
6. **Notifications temps réel**: Pour une V2, considérer WebSockets au lieu du polling
|
|
|
|
---
|
|
|
|
## Prochaines étapes
|
|
|
|
Une fois cette fonctionnalité complète, on pourra ajouter:
|
|
- [ ] Page de liste des bookings (`/dashboard/bookings`)
|
|
- [ ] Filtres et recherche dans les bookings
|
|
- [ ] Export des bookings en PDF/Excel
|
|
- [ ] Historique des statuts (timeline)
|
|
- [ ] Chat intégré avec le transporteur
|
|
- [ ] Système de rating après livraison
|