"use client"; import React, { useState, useRef, useEffect } from 'react'; import { UploadCloud, X, Check, Loader2 } from 'lucide-react'; import { Button } from '@/components/ui/button'; import { useSession } from 'next-auth/react'; import { toast } from '@/components/ui/use-toast'; interface FileUploadProps { type: 'logo' | 'attachment'; missionId?: string; // Make missionId optional onUploadComplete?: (data: any) => void; onFileSelect?: (file: File) => void; // New callback for when file is selected but not uploaded yet maxSize?: number; // in bytes, default 5MB acceptedFileTypes?: string; isNewMission?: boolean; // Flag to indicate if this is a new mission being created } export function FileUpload({ type, missionId, onUploadComplete, onFileSelect, maxSize = 5 * 1024 * 1024, // 5MB acceptedFileTypes = type === 'logo' ? 'image/*' : '.pdf,.doc,.docx,.xls,.xlsx,.jpg,.jpeg,.png', isNewMission = false }: FileUploadProps) { // Log props on init console.log('FileUpload component initialized with props:', { type, missionId, hasMissionId: !!missionId, maxSize, acceptedFileTypes }); const { data: session } = useSession(); const [isDragging, setIsDragging] = useState(false); const [isUploading, setIsUploading] = useState(false); const [progress, setProgress] = useState(0); const [file, setFile] = useState(null); const [error, setError] = useState(null); const fileInputRef = useRef(null); const isMounted = useRef(true); // Cleanup on unmount useEffect(() => { return () => { isMounted.current = false; }; }, []); // Log when session changes useEffect(() => { console.log('Session state in FileUpload:', { isSessionLoaded: !!session, hasUser: !!session?.user, userId: session?.user?.id }); }, [session]); // Log when missionId changes useEffect(() => { console.log('MissionId in FileUpload component:', missionId); }, [missionId]); // Handle drag events const handleDragOver = (e: React.DragEvent) => { e.preventDefault(); e.stopPropagation(); setIsDragging(true); }; const handleDragLeave = (e: React.DragEvent) => { e.preventDefault(); e.stopPropagation(); setIsDragging(false); }; const validateFile = (file: File): boolean => { // Check file size if (file.size > maxSize) { setError(`File size exceeds the limit of ${maxSize / (1024 * 1024)}MB`); return false; } // Check file type for logo if (type === 'logo' && !file.type.startsWith('image/')) { setError('Only image files are allowed for logo'); return false; } // For attachments, check file extension if (type === 'attachment') { const ext = file.name.split('.').pop()?.toLowerCase(); const allowedExt = ['pdf', 'doc', 'docx', 'xls', 'xlsx', 'jpg', 'jpeg', 'png']; if (ext && !allowedExt.includes(ext)) { setError(`File type .${ext} is not allowed. Allowed types: ${allowedExt.join(', ')}`); return false; } } setError(null); return true; }; const handleFileDrop = (e: React.DragEvent) => { e.preventDefault(); e.stopPropagation(); setIsDragging(false); if (e.dataTransfer.files && e.dataTransfer.files.length > 0) { const droppedFile = e.dataTransfer.files[0]; if (validateFile(droppedFile)) { setFile(droppedFile); // If this is a new mission, call onFileSelect instead of waiting for upload if (isNewMission && onFileSelect) { onFileSelect(droppedFile); } } } }; const handleFileChange = (e: React.ChangeEvent) => { if (e.target.files && e.target.files.length > 0) { const selectedFile = e.target.files[0]; if (validateFile(selectedFile)) { setFile(selectedFile); // If this is a new mission, call onFileSelect instead of waiting for upload if (isNewMission && onFileSelect) { onFileSelect(selectedFile); } } } }; const handleUpload = async () => { if (!file) { console.error('Upload failed: No file selected'); toast({ title: 'Upload failed', description: 'No file selected. Please select a file first.', variant: 'destructive', }); return; } if (!session?.user?.id) { console.error('Upload failed: No user session'); toast({ title: 'Authentication required', description: 'You need to be logged in to upload files. Please log in and try again.', variant: 'destructive', }); return; } // For new missions, just notify through the callback and don't try to upload if (isNewMission) { if (onFileSelect) { onFileSelect(file); } toast({ title: 'File selected', description: 'The file will be uploaded when you save the mission.', variant: 'default', }); return; } // For existing missions, we need a missionId if (!missionId) { console.error('Upload failed: Missing mission ID'); toast({ title: 'Upload failed', description: 'Missing mission ID. Please refresh the page and try again.', variant: 'destructive', }); return; } console.log('Starting upload process...', { fileName: file.name, fileSize: file.size, fileType: file.type, missionId, userId: session.user.id, uploadType: type }); setIsUploading(true); setProgress(0); try { // Create form data const formData = new FormData(); formData.append('file', file); formData.append('missionId', missionId); formData.append('type', type); console.log('FormData prepared, sending to API...'); // Upload the file const response = await fetch('/api/missions/upload', { method: 'POST', body: formData }); console.log('API response received:', { status: response.status, statusText: response.statusText, ok: response.ok }); if (!response.ok) { const errorData = await response.json(); console.error('API returned error:', errorData); throw new Error(errorData.error || 'Upload failed'); } const result = await response.json(); console.log('Upload successful, result:', result); // Only update state if the component is still mounted if (isMounted.current) { setProgress(100); // Reset file after successful upload setTimeout(() => { if (isMounted.current) { setFile(null); setIsUploading(false); setProgress(0); // Call the callback if provided if (onUploadComplete) { onUploadComplete(result); } } toast({ title: 'File uploaded successfully', description: type === 'logo' ? 'Logo has been updated' : `${file.name} has been added to attachments`, variant: 'default', }); }, 1000); } } catch (error) { console.error('Upload error details:', error); // Only update state if the component is still mounted if (isMounted.current) { setIsUploading(false); } toast({ title: 'Upload failed', description: error instanceof Error ? error.message : 'An error occurred during upload', variant: 'destructive', }); } }; const handleCancel = () => { setFile(null); setError(null); }; return (
{!file ? (

{type === 'logo' ? 'Upload logo image' : 'Upload attachment'}

Drag and drop or click to browse

{error && (

{error}

)}
) : (
{type === 'logo' ? ( Preview ) : (
{file.name.split('.').pop()?.toUpperCase()}
)}

{file.name}

{(file.size / 1024).toFixed(2)} KB

{isUploading ? (
{progress}%
) : ( <> {!isNewMission && ( )} )}
{isUploading && (
)}
)}
); }