Les options du formulaire de recherche etaient collectees mais jamais stockees.
Ajout d'une colonne jsonb options sur csv_bookings (+ migration), stockee a la
creation et a l'edition (editFromRate), renvoyee dans la reponse, et re-pre-
remplie a la reprise (search-advanced <- URL <- booking.options).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Remplace la page d'édition autonome par une reprise du parcours de réservation :
"Modifier" ré-ouvre la recherche pré-remplie (origine/destination/volume/poids/
palettes reconstruits), l'utilisateur ajuste si besoin, choisit une compagnie
(résultats) et passe au paiement — la réservation existante est mise à jour au
lieu d'en créer une nouvelle.
- Backend: PATCH /csv-bookings/:id/rate + UpdateCsvBookingRateDto + service
updateBookingRate + domaine editFromRate (carrier/route/conteneur/transit/
cargo/prix modifiables avant paiement).
- Front: search-advanced pré-rempli via query (+ editBookingId, démarre à
l'étape colis) ; results en mode édition met à jour puis redirige vers /pay ;
liens "Modifier" (liste, modale, paiement) pointent vers le parcours ;
suppression de la page /booking/:id/edit autonome.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
La recherche de tarif renvoie le transporteur en slug ("ssc-consolidation")
alors que la réservation stocke le nom d'affichage ("SSC Consolidation"), donc
le matching exact companyName === carrierName ne trouvait jamais l'offre →
"aucun tarif trouvé" et prix jamais recalculé.
Matching robuste : comparaison normalisée du transporteur (minuscules, sans
séparateurs) + conteneur + transit, avec fallbacks. Vérifié en conditions
réelles (GET booking, search-csv-offers, PATCH details renvoient 200 ; le prix
change bien quand le volume change).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Suppression de documents: cause = deleteDocument restreint aux statuts
PENDING/PENDING_PAYMENT + blocage du dernier document. Alignement sur
replaceDocument (aucune restriction de statut) et autorisation de 0 document
(validation domaine assouplie ; creation exige toujours >=1 doc cote service).
- Edition avant paiement: recalcul auto du prix. La page /booking/:id/edit
relance la recherche de tarif (meme transporteur/conteneur/transit) pour le
nouveau volume/poids et met a jour fret/FOB/total ; nouveaux champs de prix
dans UpdateCsvBookingDetailsDto + editDetails.
- Liste reservations: actions regroupees dans un menu 3 points (kebab) en
positionnement fixed (anti-clipping), desktop + mobile.
- Responsive (<=1280px): tables documents & blog admin en overflow-x-auto,
menus documents en fixed, grilles de la page edition en sm:.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Edition d'une reservation avant paiement: page /booking/:id/edit
(volume, poids, palettes, notes + gestion documents), endpoint
PATCH /csv-bookings/:id/details (owner + PENDING_PAYMENT), methode
domaine editDetails. Acces "Modifier" depuis la liste, la modale detail
et la page de paiement.
- Filtre "A finaliser" (PENDING_PAYMENT + PENDING_BANK_TRANSFER) + libelles
et couleurs de tous les statuts.
- Tableau des reservations: 6 colonnes + colonne Actions collante a droite
(plus de scroll horizontal pour atteindre Payer/Voir).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Telechargement docs transporteur: proxy de streaming API (fin des URLs
presignees MinIO injoignables depuis l'exterieur)
- Documents: ajout de l'action "Supprimer" (endpoint DELETE deja existant)
- Blog: suppression reelle de l'image de couverture (null explicite bout en bout)
- Blog: le bouton Retour navigateur ferme l'editeur au lieu de quitter vers Logs
- Paiement: bouton Retour revient au contexte precedent (router.back)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Replace localStorage.getItem('access_token') with getAuthToken() which
also checks sessionStorage — fixes Unauthorized error for users logged
in without 'remember me'
- Add real drag-and-drop support to booking creation page (step 2):
onDragOver/onDragLeave/onDrop handlers on the dropzone container,
isDragging state with visual feedback (blue border + text change),
click-to-browse still works via hidden input ref
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add PENDING_PAYMENT and PENDING_BANK_TRANSFER to document statuses
translations (was displaying raw i18n key instead of human label)
- Fix getStatusLabel/getStatusColor to handle all booking statuses
- Fix select dropdown in Add Document modal to use getStatusLabel()
instead of hardcoded PENDING/ACCEPTED binary
- Add direct "Payer" button (orange) on PENDING_PAYMENT rows in both
desktop table and mobile cards, linking to /dashboard/booking/[id]/pay
- Update detail modal to show payment CTA for PENDING_PAYMENT bookings
- Refactor file handling in document modals to use React state (addFiles,
replaceFile) instead of reading from DOM ref — fixes upload reliability
and shows selected filename feedback in the dropzone
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- SIRET/SIREN admin: controller applied siren but skipped siret — add missing updateSiret() call
- Document upload on validated booking: backend blocked PENDING_BANK_TRANSFER;
frontend filter excluded it — both now allow all non-terminal statuses
- Accent encoding in filenames: Multer stores originalname as latin1;
re-decode to utf8 before storing and displaying
- Drag & drop in document modals: replace bare input with styled dropzone
supporting click and native drag-and-drop in both Add and Replace modals
- Resume existing booking: add Actions column with View button and booking
detail modal to the bookings list page
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Fixed issue where password form fields (especially "New Password")
were being pre-filled with values, either from browser autocomplete
or residual form state.
Changes:
1. Added explicit empty defaultValues to password form
- currentPassword: ''
- newPassword: ''
- confirmPassword: ''
2. Added autoComplete attributes to prevent browser pre-fill:
- currentPassword: autoComplete="current-password"
- newPassword: autoComplete="new-password"
- confirmPassword: autoComplete="new-password"
3. Added useEffect to reset password form when switching tabs:
- Ensures clean state when navigating to "Change Password" tab
- Prevents stale values from persisting
4. Explicit reset values on successful password change:
- Previously used passwordForm.reset() without values
- Now explicitly sets all fields to empty strings
This ensures password fields are always empty and never pre-filled
by the browser or by residual form state.
Refs: apps/frontend/app/dashboard/profile/page.tsx:64-70,85-95
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Fixed critical issues with the profile page (/dashboard/profile):
1. **Form data not persisting on page refresh**:
- Added useEffect to update form values when user data loads
- Forms now properly populate after auth context loads user data
2. **Blank page on refresh**:
- Added loading and error states for better UX
- Handle case where user is not loaded yet (loading spinner)
- Handle case where user fails to load (retry button)
3. **Password change API endpoint correction**:
- Fixed: POST /api/v1/users/change-password (incorrect)
- Corrected to: PATCH /api/v1/users/me/password (matches backend)
- Updated return type to include { message: string }
The root cause was that useForm defaultValues were set once at
component mount when user was still null. The form never updated
when user data was subsequently loaded by the auth context.
Now the form properly resets with user data via useEffect, and
proper loading/error states prevent showing a blank page.
Refs: apps/frontend/app/dashboard/profile/page.tsx:68-78
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Fix password change feature that was previously non-functional:
- Add changePassword function in frontend API (src/lib/api/users.ts)
- Update API endpoint to match backend: PATCH /api/v1/users/me/password
- Connect profile page to real API instead of mock implementation
- Export changePassword function from API index
The backend endpoint was already implemented but frontend was using
a placeholder Promise.resolve(). Now properly calls the backend API.
Refs: apps/frontend/app/dashboard/profile/page.tsx:87-105
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Replace full logo (1875x1699) with simplified icon (512x512) optimized for favicon display
- Implement CSS media queries for automatic theme adaptation:
* Dark mode: white X (#FFFFFF) with cyan dot (#34CCCD)
* Light mode: dark blue X and dot (#1D3865)
- Remove old logo files (xpeditis-icon.svg, xpeditis-logo.svg)
- Simplified design with clean X shape and accent dot for better visibility at small sizes
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Replace the icon with the full Xpeditis logo (logo-black.svg) as requested.
Updated references in:
- app/icon.svg (favicon)
- app/layout.tsx (metadata and social cards)
- public/manifest.json (PWA icon)
Note: The full logo (1875x1699px) will be scaled down in browser tabs.
Consider creating a square optimized version for better display in small sizes.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Add site favicon and enhance metadata for better SEO and social sharing:
- Added app/icon.svg from existing logo for browser tab icon
- Enhanced metadata with Open Graph and Twitter card support
- Created manifest.json for PWA support
- Added metadataBase for proper social image resolution
- Updated .env.example with NEXT_PUBLIC_APP_URL
The Xpeditis logo (blue background with cyan X) now appears in:
- Browser tabs (favicon)
- Bookmarks
- Mobile home screen (PWA)
- Social media shares (Open Graph)
Configuration for different environments:
- Dev: NEXT_PUBLIC_APP_URL=http://localhost:3000
- Preprod: NEXT_PUBLIC_APP_URL=https://app.preprod.xpeditis.com
- Prod: NEXT_PUBLIC_APP_URL=https://xpeditis.com🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>