From 87b8d68f410e232168b00cb6fb1168bee5b7734e Mon Sep 17 00:00:00 2001 From: smanylov Date: Wed, 24 Dec 2025 21:36:59 +0700 Subject: [PATCH] file upload update --- src/app/[locale]/pages/marking-audio/page.tsx | 12 +- src/app/[locale]/pages/marking-photo/page.tsx | 11 +- src/app/[locale]/pages/marking-video/page.tsx | 14 +- src/app/actions/definitions.ts | 2 - src/app/components/upload-section-file.tsx | 170 +++++++++++++----- src/app/styles/pages-styles.scss | 8 + .../ui/marking-page/upload-section-video.tsx | 1 + src/i18n/messages/en.json | 7 +- src/i18n/messages/ru.json | 5 +- 9 files changed, 156 insertions(+), 74 deletions(-) diff --git a/src/app/[locale]/pages/marking-audio/page.tsx b/src/app/[locale]/pages/marking-audio/page.tsx index 8df2cef..34d43ed 100644 --- a/src/app/[locale]/pages/marking-audio/page.tsx +++ b/src/app/[locale]/pages/marking-audio/page.tsx @@ -1,16 +1,16 @@ import ProtectionSummary from '@/app/ui/marking-page/protection-summary'; -import TestSection from '@/app/ui/marking-page/test-section'; import FilesTable from '@/app/ui/dashboard/files-table'; -import {useTranslations} from 'next-intl'; +import PageTitle from '@/app/ui/page-title'; + +const ALLOWED_EXTENSIONS: string[] = ['jpg', 'jpeg', 'png', 'gif', 'bmp']; +const FILE_TYPE = "VIDEO" +const MAX_SIZE = 1048576000; export default function Page() { - const t = useTranslations('Global'); return (
-
-

{t('audio-protection')}

-
+ {/* */} diff --git a/src/app/[locale]/pages/marking-photo/page.tsx b/src/app/[locale]/pages/marking-photo/page.tsx index 502b9a0..39f5b3c 100644 --- a/src/app/[locale]/pages/marking-photo/page.tsx +++ b/src/app/[locale]/pages/marking-photo/page.tsx @@ -2,21 +2,22 @@ import ProtectionSummary from '@/app/ui/marking-page/protection-summary'; import PageTitle from '@/app/ui/page-title'; import UploadSectionFile from '@/app/components/upload-section-file'; import FilesTable from '@/app/ui/dashboard/files-table'; -import { FileExtension } from '@/app/actions/definitions'; import { getAllowedFilesExtensions } from '@/app/actions/file-upload'; -const ALLOWED_EXTENSIONS: FileExtension[] = ['jpg', 'jpeg', 'png', 'gif', 'bmp']; +const ALLOWED_EXTENSIONS: string[] = ['jpg', 'jpeg', 'png', 'gif', 'bmp']; const FILE_TYPE = "IMAGE" +const MAX_SIZE = 1048576000; export default async function Page() { - /* const allowedFilesExtensions = await getAllowedFilesExtensions(FILE_TYPE); - console.log(allowedFilesExtensions); */ + /* const allowedFilesExtensions = await getAllowedFilesExtensions(FILE_TYPE); + console.log(allowedFilesExtensions); + console.log(allowedFilesExtensions); */ return (
- + {/* */} {/* */} diff --git a/src/app/[locale]/pages/marking-video/page.tsx b/src/app/[locale]/pages/marking-video/page.tsx index 476b82e..c78b7ad 100644 --- a/src/app/[locale]/pages/marking-video/page.tsx +++ b/src/app/[locale]/pages/marking-video/page.tsx @@ -1,20 +1,20 @@ import ProtectionSummary from '@/app/ui/marking-page/protection-summary'; -import TestSection from '@/app/ui/marking-page/test-section'; import UploadSectionVideo from '@/app/ui/marking-page/upload-section-video'; import FilesTable from '@/app/ui/dashboard/files-table'; -import {useTranslations} from 'next-intl'; +import PageTitle from '@/app/ui/page-title'; +import UploadSectionFile from '@/app/components/upload-section-file'; +const ALLOWED_EXTENSIONS: string[] = ['mp4', 'avi', 'webm', 'mkv', 'flv', 'wmv', 'mov']; +const FILE_TYPE = "VIDEO" +const MAX_SIZE = 1048576000; export default function Page() { - const t = useTranslations('Global'); return (
-
-

{t('video-protection')}

-
+ - + {/* */}
diff --git a/src/app/actions/definitions.ts b/src/app/actions/definitions.ts index 95672ba..454a4e7 100644 --- a/src/app/actions/definitions.ts +++ b/src/app/actions/definitions.ts @@ -78,8 +78,6 @@ export const loginFormSchema = z export const localDevelopmentUrl = 'http://localhost'; -export type FileExtension = 'jpg' | 'jpeg' | 'png' | 'gif' | 'bmp'; - export const testUserData = { fullName: 'test', email: 'test@mail.com', diff --git a/src/app/components/upload-section-file.tsx b/src/app/components/upload-section-file.tsx index 31a33c0..feeafe6 100644 --- a/src/app/components/upload-section-file.tsx +++ b/src/app/components/upload-section-file.tsx @@ -4,7 +4,6 @@ import React, { useRef, useState, useCallback, ChangeEvent, DragEvent } from 're import { IconShieldAdd } from '@/app/ui/icons/icons'; import { fileUpload, cancelUpload, chunkUpload, checkChunkStatus } from '@/app/actions/file-upload'; import { useTranslations } from 'next-intl'; -import { FileExtension } from '@/app/actions/definitions'; interface SelectedFile { file: File; @@ -27,40 +26,79 @@ interface FileUploadInitResponse { interface UploadSectionFile { fileType: string - allowedExtensions: FileExtension[] + allowedExtensions: string[] + maxFileSize: number } -/* type SupportedFormat = 'image/jpeg' | 'image/png' | 'image/gif' | 'image/bmp'; */ - -export default function UploadSectionFile({ fileType, allowedExtensions }: UploadSectionFile) { +export default function UploadSectionFile({ fileType, allowedExtensions, maxFileSize }: UploadSectionFile) { const fileInputRef = useRef(null); const [isDragging, setIsDragging] = useState(false); const [selectedFile, setSelectedFile] = useState(null); const [error, setError] = useState(null); const [uploadId, setUploadId] = useState(null); const [isFileUploaded, setIsFileUploaded] = useState(false); + const [uploadProgress, setUploadProgress] = useState(0); const t = useTranslations('Global'); - console.log('fileType'); - console.log(allowedExtensions); - let SUPPORTED_FORMATS: string[] = [] + let SUPPORTED_FORMATS: string[] = []; - if (fileType === "IMAGE") { - SUPPORTED_FORMATS = [ - 'image/jpeg', - 'image/png', - 'image/gif', - 'image/bmp' - ]; + switch (fileType) { + case 'IMAGE': + SUPPORTED_FORMATS = [ + 'image/jpeg', + 'image/jpg', + 'image/png', + 'image/gif', + 'image/bmp', + 'image/webp', + 'image/svg+xml', + 'image/tiff', + 'image/x-icon' + ]; + break; + case 'VIDEO': + SUPPORTED_FORMATS = [ + 'video/mp4', + 'video/mpeg', + 'video/ogg', + 'video/webm', + 'video/quicktime', + 'video/x-msvideo', + 'video/x-matroska', + 'video/x-flv', + 'video/3gpp', + 'video/3gpp2' + ]; + break; + case 'AUDIO': + SUPPORTED_FORMATS = [ + 'audio/mpeg', + 'audio/wav', + 'audio/wave', + 'audio/x-wav', + 'audio/x-pn-wav', + 'audio/ogg', + 'audio/webm', + 'audio/aac', + 'audio/mp4', + 'audio/x-m4a', + 'audio/flac', + 'audio/x-flac', + 'audio/opus' + ]; + break; + + default: + break; } - const ALLOWED_EXTENSIONS: FileExtension[] = allowedExtensions; + const ALLOWED_EXTENSIONS: string[] = allowedExtensions; const validateFile = (file: File): { isValid: boolean; errorMessage?: string } => { if (!SUPPORTED_FORMATS.includes(file.type as string)) { const extension = file.name.split('.').pop()?.toLowerCase(); - if (!extension || !ALLOWED_EXTENSIONS.includes(extension as FileExtension)) { + if (!extension || !ALLOWED_EXTENSIONS.includes(extension as string)) { return { isValid: false, errorMessage: 'Неподдерживаемый формат файла.' @@ -68,11 +106,12 @@ export default function UploadSectionFile({ fileType, allowedExtensions }: Uploa } } - const MAX_SIZE = 100 * 1024 * 1024; + const MAX_SIZE = maxFileSize; + if (file.size > MAX_SIZE) { return { isValid: false, - errorMessage: 'Файл слишком большой. Максимальный размер: 100MB' + errorMessage: 'Файл слишком большой. Максимальный размер: 1000MB' }; } @@ -107,23 +146,28 @@ export default function UploadSectionFile({ fileType, allowedExtensions }: Uploa setError(null); return; } - + setUploadProgress(0); setError(null); const validation = validateFile(file); if (!validation.isValid) { setError(validation.errorMessage || 'Неизвестная ошибка валидации'); + return; } try { - const dimensions = await getImageDimensions(file); + let dimensions = undefined; - if (dimensions.width < 150 || dimensions.height < 150 || - dimensions.width > 8000 || dimensions.height > 8000) { - setError(`Размер изображения: ${dimensions.width}x${dimensions.height}. Допустимый диапазон: 150x150 - 4000x4000 пикселей.`); - return; + if (fileType === "IMAGE") { + dimensions = await getImageDimensions(file) as { width: number, height: number }; + if (dimensions.width < 150 || dimensions.height < 150 || + dimensions.width > 10000 || dimensions.height > 10000) { + setError(`Размер изображения: ${dimensions.width}x${dimensions.height}. Допустимый диапазон: 150x150 - 10000x10000 пикселей.`); + + return; + } } const objectUrl = URL.createObjectURL(file); @@ -133,14 +177,15 @@ export default function UploadSectionFile({ fileType, allowedExtensions }: Uploa preview: objectUrl, name: file.name, size: `${(file.size / 1024 / 1024).toFixed(2)} MB`, - dimensions + ...(dimensions && { dimensions }) }); console.log('Файл обработан:', { + file: file, name: file.name, type: file.type, size: file.size, - dimensions + ...(dimensions && { dimensions }) }); } catch (err) { @@ -192,13 +237,21 @@ export default function UploadSectionFile({ fileType, allowedExtensions }: Uploa setIsFileUploaded(false); setSelectedFile(null); setError(null); + setUploadProgress(0); }; - const handlerFileUpload = async (file: SelectedFile): Promise => { + const handlerFileUpload = async (file: SelectedFile): Promise => { + const extension = file.name.split('.').pop(); + + if (!extension) { + setError(t('file-has-no-extension')); + return; + } + const initMessageBody = { file_name: file.name, file_type: fileType, - extension: file.name.split('.')[1], + extension: extension, file_size: file.file.size }; @@ -239,26 +292,26 @@ export default function UploadSectionFile({ fileType, allowedExtensions }: Uploa start = end; chunkIndex++; + setUploadProgress( + () => { + return Math.floor(chunkIndex / totalChunks * 100); + } + ); } } catch (error) { setError('Chunk uploaded error ' + error); console.error('Chunk upload error:', error); } + } else { + console.log(response); + setError('file uploaded error'); } } - React.useEffect(() => { - return () => { - if (selectedFile) { - URL.revokeObjectURL(selectedFile.preview); - } - }; - }, [selectedFile]); - return (

- {t('download-for-protection', { fileType: t(fileType.toLocaleLowerCase()) })} + {t('upload-for-protection', { fileType: t(fileType.toLocaleLowerCase()) })}

- Предпросмотр загруженного изображения setError('Не удалось загрузить превью изображения')} - /> + {fileType === 'IMAGE' && ( + Предпросмотр загруженного изображения setError('Не удалось загрузить превью изображения')} + /> + )} -
+ {fileType === 'VIDEO' && ( +
)} diff --git a/src/app/styles/pages-styles.scss b/src/app/styles/pages-styles.scss index 0b138c2..2fbc548 100644 --- a/src/app/styles/pages-styles.scss +++ b/src/app/styles/pages-styles.scss @@ -517,6 +517,14 @@ min-width: 160px; transform: translateY(0px); box-shadow: none; + + &.disabled { + opacity: 0.5; + + &:hover { + transform: none; + } + } } .btn-cancel { diff --git a/src/app/ui/marking-page/upload-section-video.tsx b/src/app/ui/marking-page/upload-section-video.tsx index 868afca..5161bed 100644 --- a/src/app/ui/marking-page/upload-section-video.tsx +++ b/src/app/ui/marking-page/upload-section-video.tsx @@ -1,4 +1,5 @@ 'use client'; +/* removed */ import { useState, useRef } from 'react'; diff --git a/src/i18n/messages/en.json b/src/i18n/messages/en.json index ba0fa26..5a2c5e0 100644 --- a/src/i18n/messages/en.json +++ b/src/i18n/messages/en.json @@ -17,7 +17,7 @@ "checks": "Checks", "check": "Checks", "videos": "Videos", - "video": "vides", + "video": "videos", "audios": "Audio", "audio": "audio", "images": "Images", @@ -126,8 +126,9 @@ "or": "or", "drag-the-file-here": "Drag the file here", "select-files-to-protect": "Select files to protect", - "download-for-protection": "Download {fileType} for protection", - "image": "image" + "upload-for-protection": "Upload {fileType} for protection", + "image": "image", + "file-has-no-extension": "File has no extension" }, "Login-register-form": { "and": "and", diff --git a/src/i18n/messages/ru.json b/src/i18n/messages/ru.json index 458d77a..c235f61 100644 --- a/src/i18n/messages/ru.json +++ b/src/i18n/messages/ru.json @@ -126,8 +126,9 @@ "or": "или", "drag-the-file-here": "Перетащите файл сюда", "select-files-to-protect": "Выбрать файлы для защиты", - "download-for-protection": "Загрузить {fileType} для защиты", - "image": "изображений" + "upload-for-protection": "Загрузить {fileType} для защиты", + "image": "изображений", + "file-has-no-extension": "Файл не имеет расширения" }, "Login-register-form": { "and": "и",