fix: unauthorized on document upload and drag-drop in booking creation

- 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>
This commit is contained in:
David 2026-06-07 17:42:27 +02:00
parent b344650608
commit 1da5570e1c
2 changed files with 33 additions and 11 deletions

View File

@ -6,7 +6,7 @@
'use client';
import { useState, useEffect, Suspense } from 'react';
import { useState, useEffect, useRef, Suspense } from 'react';
import { useRouter, useSearchParams } from 'next/navigation';
import { ClipboardList, Mail, AlertTriangle } from 'lucide-react';
import type { CsvRateSearchResult } from '@/types/rates';
@ -55,6 +55,8 @@ function NewBookingPageContent() {
const [isSubmitting, setIsSubmitting] = useState(false);
const [termsAccepted, setTermsAccepted] = useState(false);
const [error, setError] = useState<string | null>(null);
const [isDragging, setIsDragging] = useState(false);
const fileInputRef = useRef<HTMLInputElement>(null);
const [formData, setFormData] = useState<BookingForm>({
carrierName: '',
@ -419,8 +421,30 @@ function NewBookingPageContent() {
{/* File Upload */}
<div className="space-y-4 mb-8">
<div className="border-2 border-dashed border-gray-300 rounded-lg p-8 text-center hover:border-blue-400 transition-colors">
<div
className={`border-2 border-dashed rounded-lg p-8 text-center transition-colors cursor-pointer ${
isDragging
? 'border-blue-500 bg-blue-50'
: 'border-gray-300 hover:border-blue-400 hover:bg-gray-50'
}`}
onDragOver={e => {
e.preventDefault();
setIsDragging(true);
}}
onDragLeave={e => {
if (!e.currentTarget.contains(e.relatedTarget as Node)) {
setIsDragging(false);
}
}}
onDrop={e => {
e.preventDefault();
setIsDragging(false);
handleFileChange(e.dataTransfer.files);
}}
onClick={() => fileInputRef.current?.click()}
>
<input
ref={fileInputRef}
type="file"
id="file-upload"
multiple
@ -428,12 +452,9 @@ function NewBookingPageContent() {
onChange={e => handleFileChange(e.target.files)}
className="hidden"
/>
<label
htmlFor="file-upload"
className="cursor-pointer flex flex-col items-center"
>
<div className="flex flex-col items-center">
<svg
className="w-16 h-16 text-gray-400 mb-4"
className={`w-16 h-16 mb-4 transition-colors ${isDragging ? 'text-blue-500' : 'text-gray-400'}`}
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
@ -446,10 +467,10 @@ function NewBookingPageContent() {
/>
</svg>
<p className="text-lg font-semibold text-gray-700 mb-2">
Cliquez pour sélectionner des fichiers
{isDragging ? 'Déposez vos fichiers ici' : 'Cliquez pour sélectionner des fichiers'}
</p>
<p className="text-sm text-gray-500">ou glissez-déposez vos documents ici</p>
</label>
</div>
</div>
{/* Document Type Suggestions */}

View File

@ -3,6 +3,7 @@
import { useState, useEffect, useCallback, useRef } from 'react';
import { useTranslations, useLocale } from 'next-intl';
import { listCsvBookings, CsvBookingResponse } from '@/lib/api/bookings';
import { getAuthToken } from '@/lib/api/client';
import { FileText, Image as ImageIcon, FileEdit, FileSpreadsheet, Paperclip } from 'lucide-react';
import type { ReactNode } from 'react';
import ExportButton from '@/components/ExportButton';
@ -277,7 +278,7 @@ export default function UserDocumentsPage() {
const formData = new FormData();
addFiles.forEach(file => formData.append('documents', file));
const token = localStorage.getItem('access_token');
const token = getAuthToken();
const response = await fetch(
`${process.env.NEXT_PUBLIC_API_URL}/api/v1/csv-bookings/${selectedBookingId}/documents`,
{ method: 'POST', headers: { Authorization: `Bearer ${token}` }, body: formData }
@ -335,7 +336,7 @@ export default function UserDocumentsPage() {
const formData = new FormData();
formData.append('document', replaceFile);
const token = localStorage.getItem('access_token');
const token = getAuthToken();
const response = await fetch(
`${process.env.NEXT_PUBLIC_API_URL}/api/v1/csv-bookings/${documentToReplace.bookingId}/documents/${documentToReplace.id}`,
{ method: 'PATCH', headers: { Authorization: `Bearer ${token}` }, body: formData }