From 62b91f0ce98c97e7b0d298b2fe88f6bb83ca4848 Mon Sep 17 00:00:00 2001 From: smanylov Date: Tue, 17 Feb 2026 17:58:32 +0700 Subject: [PATCH] add extension dropdown for image convert --- src/app/actions/fileUpload.ts | 6 +- .../DropDownListForImageConvert.tsx | 115 ++++++++++++++++++ src/app/components/UploadSectionFile.tsx | 49 +++++--- src/app/styles/pages-styles.scss | 23 ++++ src/i18n/messages/en.json | 3 +- src/i18n/messages/ru.json | 3 +- 6 files changed, 180 insertions(+), 19 deletions(-) create mode 100644 src/app/components/DropDownListForImageConvert.tsx diff --git a/src/app/actions/fileUpload.ts b/src/app/actions/fileUpload.ts index ae142b9..776e1c4 100644 --- a/src/app/actions/fileUpload.ts +++ b/src/app/actions/fileUpload.ts @@ -14,7 +14,7 @@ interface initMessageBody { token?: string } -export async function fileUpload(messageBody: initMessageBody, needConvert: boolean | undefined) { +export async function fileUpload(messageBody: initMessageBody, convertTo: string | null | undefined) { const token = await getSessionData('token'); if (!token) { return; @@ -23,8 +23,8 @@ export async function fileUpload(messageBody: initMessageBody, needConvert: bool message.action = 'init'; message.token = token; - // нужно будет добавить свойство needConvert в тело запроса. - // console.log(needConvert); + // нужно будет добавить свойство convertTo в тело запроса он может прийти со значением reject тогда конвертировать не нужно. + // console.log(convertTo); try { const response = await fetch(`${API_BASE_URL}/api/v1/data`, { diff --git a/src/app/components/DropDownListForImageConvert.tsx b/src/app/components/DropDownListForImageConvert.tsx new file mode 100644 index 0000000..51c75c2 --- /dev/null +++ b/src/app/components/DropDownListForImageConvert.tsx @@ -0,0 +1,115 @@ +import React, { ReactNode, useState, useRef } from 'react'; +import { useClickOutside } from '@/app/hooks/useClickOutside'; +import { SelectedFile } from './UploadSectionFile'; +import { useTranslations } from 'next-intl'; + +interface DropDownItem { + file: SelectedFile; + convertTo: string; +} + +interface DropDownListProps { + children: ReactNode; + value: string | null | undefined; + callBack: (value: DropDownItem) => void; + translatedValue?: string | null | undefined; + selectedFiles: SelectedFile[]; +} + +interface LiElementProps extends React.LiHTMLAttributes { + 'data-file'?: string; + 'data-convert-to'?: string; +} + +export default function DropDownListForImageConvert({ + children, + value, + callBack, + translatedValue, + selectedFiles +}: DropDownListProps) { + const [isOpen, setIsOpen] = useState(false); + const dropdownRef = useRef(null); + const t = useTranslations('Global') + + useClickOutside(dropdownRef, + () => { + setIsOpen(false); + }, + isOpen + ); + + const getDisplayValue = (): string => { + if (translatedValue) return translatedValue; + return value ? value : t('do-not-convert'); + }; + + const findFileByName = (fileName: string): SelectedFile | undefined => { + return selectedFiles.find(file => file.name === fileName); + }; + + const enhancedChildren = React.Children.map(children, (child, index) => { + if (React.isValidElement(child)) { + const fileName = child.props['data-file']; + const convertTo = child.props['data-convert-to']; + + const isCurrent = value === convertTo; + + return React.cloneElement(child, { + onClick: () => { + if (fileName && convertTo) { + const file = findFileByName(fileName); + if (file) { + callBack({ file, convertTo }); + } else { + console.error(`Файл с именем ${fileName} не найден`); + } + setIsOpen(false); + } + }, + className: `dropdown-item ${isCurrent ? "current" : ""}`, + key: index, + }); + } + return child; + }); + + return ( +
+ + {isOpen && ( +
+
    + {enhancedChildren} +
+
+ )} +
+ ); +} \ No newline at end of file diff --git a/src/app/components/UploadSectionFile.tsx b/src/app/components/UploadSectionFile.tsx index ae5cf46..941bb45 100644 --- a/src/app/components/UploadSectionFile.tsx +++ b/src/app/components/UploadSectionFile.tsx @@ -6,8 +6,9 @@ import { fileUpload, cancelUpload, chunkUpload, checkChunkStatus } from '@/app/a import { useTranslations } from 'next-intl'; import { useNavigationBlocker } from '@/app/hooks/useNavigationBlocker'; import { useQueryClient } from '@tanstack/react-query'; +import DropDownListForImageConvert from '@/app/components/DropDownListForImageConvert'; -interface SelectedFile { +export interface SelectedFile { file: File; preview: string | undefined; name: string; @@ -20,7 +21,7 @@ interface SelectedFile { progress: number; status: 'pending' | 'uploading' | 'completed' | 'error' | 'cancelled'; error?: string; - needConvert?: boolean; + convertTo?: string | null; } interface FileUploadInitResponse { @@ -239,7 +240,7 @@ export default function UploadSectionFile({ file_size: fileInfo.file.size }; - const response = await fileUpload(initMessageBody, fileInfo.needConvert) as FileUploadInitResponse; + const response = await fileUpload(initMessageBody, fileInfo.convertTo) as FileUploadInitResponse; if (!response?.upload_id) { throw new Error('Failed to get upload_id'); @@ -393,12 +394,15 @@ export default function UploadSectionFile({ } }); - function converButtonHandler(file: SelectedFile) { + function converButtonHandler(value: { + convertTo: string; + file: SelectedFile + }): void { setSelectedFiles(selectedFiles.map(selectedFile => { - if (selectedFile.name === file.name) { + if (selectedFile.name === value.file.name) { return { ...selectedFile, - needConvert: !selectedFile.needConvert + convertTo: value.convertTo }; } return selectedFile; @@ -544,14 +548,31 @@ export default function UploadSectionFile({
{ (fileType === 'image' && file.status === 'pending') && ( - + <> + +
  • + {t('do-not-convert')} +
  • + { + allowedExtensions && allowedExtensions.map(extension => { + if (extension === 'gif') { + return null + } + + return ( +
  • + {t('convert-to')} {extension} +
  • + ) + }) + } +
    + ) }
    diff --git a/src/app/styles/pages-styles.scss b/src/app/styles/pages-styles.scss index cb863cd..1639155 100644 --- a/src/app/styles/pages-styles.scss +++ b/src/app/styles/pages-styles.scss @@ -473,6 +473,29 @@ transform: translateY(-2px); box-shadow: 0 8px 25px v.$shadow-1; } + + .dropdown-wrapper { + .dropdown-button { + border: 2px solid v.$p-color; + color: v.$p-color; + min-width: 200px; + } + + .dropdown-list { + border: 1px solid v.$p-color; + color: v.$p-color; + + li { + &:first-child { + border-radius: 12px 12px 0 0; + } + + &:last-child { + border-radius: 0 0 12px 12px; + } + } + } + } } .selected-file-action-panel { diff --git a/src/i18n/messages/en.json b/src/i18n/messages/en.json index 49bf054..442fadb 100644 --- a/src/i18n/messages/en.json +++ b/src/i18n/messages/en.json @@ -226,7 +226,8 @@ "all-content": "All content", "monitoring": "Monitoring", "efficiency": "Efficiency", - "convert-image": "Convert image to" + "convert-to": "Convert to", + "do-not-convert": "Do not convert" }, "Login-register-form": { "and": "and", diff --git a/src/i18n/messages/ru.json b/src/i18n/messages/ru.json index 9699907..06689e0 100644 --- a/src/i18n/messages/ru.json +++ b/src/i18n/messages/ru.json @@ -226,7 +226,8 @@ "all-content": "Весь контент", "monitoring": "Мониторинг", "efficiency": "Эффективность", - "convert-image": "Конвертировать изображение в" + "convert-to": "Конвертировать в", + "do-not-convert": "Не конвертировать" }, "Login-register-form": { "and": "и",