profile page ui complated

This commit is contained in:
nabijonovdavronbek619@gmail.com
2026-04-06 17:55:54 +05:00
parent 27b1510842
commit db0fad7e00
18 changed files with 976 additions and 85 deletions

View File

@@ -0,0 +1,128 @@
'use client';
import { useState, useCallback, useRef } from 'react';
import { UploadedFile } from './tyeps';
import { countWordsFromFile, SUPPORTED_EXTENSIONS } from './wordCount';
interface UseFileUploadReturn {
documentName: string;
setDocumentName: (name: string) => void;
uploadedFile: UploadedFile | null;
isDragging: boolean;
isProcessing: boolean;
error: string | null;
fileInputRef: React.RefObject<HTMLInputElement | null>;
handleFileSelect: (file: File) => Promise<void>;
handleDrop: (e: React.DragEvent) => void;
handleDragOver: (e: React.DragEvent) => void;
handleDragLeave: () => void;
handleRemoveFile: () => void;
openFilePicker: () => void;
}
export function useFileUpload(): UseFileUploadReturn {
const [documentName, setDocumentName] = useState('');
const [uploadedFile, setUploadedFile] = useState<UploadedFile | null>(null);
const [isDragging, setIsDragging] = useState(false);
const [isProcessing, setIsProcessing] = useState(false);
const [error, setError] = useState<string | null>(null);
const fileInputRef = useRef<HTMLInputElement | null>(null);
const validateFile = (file: File): string | null => {
const ext = '.' + file.name.split('.').pop()?.toLowerCase();
if (!SUPPORTED_EXTENSIONS.includes(ext)) {
return `Unsupported file type. Allowed: ${SUPPORTED_EXTENSIONS.join(', ')}`;
}
if (file.size > 50 * 1024 * 1024) {
return 'File size must be less than 50 MB';
}
return null;
};
const handleFileSelect = useCallback(async (file: File) => {
setError(null);
const validationError = validateFile(file);
if (validationError) {
setError(validationError);
return;
}
// Optimistic UI: show file immediately
const optimistic: UploadedFile = {
file,
name: file.name,
sizeKB: Math.round(file.size / 1024),
wordCount: 0,
status: 'uploading',
};
setUploadedFile(optimistic);
setIsProcessing(true);
// Auto-fill document name if empty
setDocumentName((prev) =>
prev.trim() === '' ? file.name.replace(/\.[^/.]+$/, '') : prev,
);
// Count words on the frontend (no round-trip needed)
const result = await countWordsFromFile(file);
if (result.error) {
setError(result.error);
setUploadedFile({ ...optimistic, status: 'error', wordCount: 0 });
} else {
setUploadedFile({
...optimistic,
status: 'done',
wordCount: result.count,
});
}
setIsProcessing(false);
}, []);
const handleDrop = useCallback(
(e: React.DragEvent) => {
e.preventDefault();
setIsDragging(false);
const file = e.dataTransfer.files[0];
if (file) handleFileSelect(file);
},
[handleFileSelect],
);
const handleDragOver = useCallback((e: React.DragEvent) => {
e.preventDefault();
setIsDragging(true);
}, []);
const handleDragLeave = useCallback(() => {
setIsDragging(false);
}, []);
const handleRemoveFile = useCallback(() => {
setUploadedFile(null);
setError(null);
if (fileInputRef.current) fileInputRef.current.value = '';
}, []);
const openFilePicker = useCallback(() => {
fileInputRef.current?.click();
}, []);
return {
documentName,
setDocumentName,
uploadedFile,
isDragging,
isProcessing,
error,
fileInputRef,
handleFileSelect,
handleDrop,
handleDragOver,
handleDragLeave,
handleRemoveFile,
openFilePicker,
};
}