Advertisement
bebo231312312321

Untitled

Jun 9th, 2025
283
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1.  
  2. import React, { useState, useRef, useMemo, useEffect, useReducer, forwardRef, useImperativeHandle } from "react";
  3. import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
  4. import {
  5.     faPlus, faMinus, faImage, faVideo, faSliders,
  6.     faUpload, faEye, faSave, faTimes, faCloudUploadAlt,
  7.     faEdit
  8. } from "@fortawesome/free-solid-svg-icons";
  9. import { useTranslation } from "react-i18next";
  10. import "./articleCreateForm.css";
  11. import { Editor } from 'react-draft-wysiwyg';
  12. import 'react-draft-wysiwyg/dist/react-draft-wysiwyg.css';
  13. import { useArticleContext } from "../../contexts/ArticleContext";
  14. import { createEditorState } from "../articleUtils/editor";
  15. import ScrollToTop from "../../ScrollToTop/ScrollToTop";
  16. import { ImageAltEditModal } from "../ArticleCreateForm/ImageAltEditModal/ImageAltEditModal";
  17. import VideoThumbnailGenerator from "../ArticleCreateForm/VideoThumbnailGenerator/VideoThumbnailGenerator";
  18. import { SectionQuickMenu } from "../ArticleCreateForm/SectionQuickMenu/SectionQuickMenu";
  19. import ArticlePreview from "./ArticlePreview/ArticlePreview";
  20. import { useCreateArticle } from "../../hooks/useCreateArticle";
  21. import VideoPlayer from "../ArticleView/VideoPlayer/VideoPlayer";
  22.  
  23. const ArticleCreateForm = forwardRef(({ initialValues: propInitialValues, onSubmitHandler, isEditMode }, ref) => {
  24.     const { t, i18n } = useTranslation();
  25.     const { createArticle } = useArticleContext();
  26.     const [previewMode, setPreviewMode] = useState(false);
  27.     const [isAltModalOpen, setIsAltModalOpen] = useState(false);
  28.     const [activeSection, setActiveSection] = useState(null);
  29.     const [currentEditingImage, setCurrentEditingImage] = useState({
  30.         sectionIndex: null,
  31.         imageIndex: null,
  32.         image: null
  33.     });
  34.  
  35.     // Използваме propInitialValues (ако има такива) или дефолтните стойности
  36.     const defaultValues = {
  37.         title: "",
  38.         slug: "",
  39.         author: "",
  40.         publishDate: new Date().toISOString().split('T')[0],
  41.         summary: createEditorState(),
  42.         mainImage: {
  43.             type: "image",
  44.             sources: [],
  45.             alt: createEditorState(),
  46.             thumbnail: "",
  47.             videoUrl: "",
  48.             subtitles: [],
  49.             allowDownload: false,
  50.         },
  51.         sections: [
  52.             {
  53.                 title: "",
  54.                 content: createEditorState(),
  55.                 image: [],
  56.                 order: 1,
  57.             },
  58.         ],
  59.         tags: [],
  60.         previousArticle: null,
  61.         nextArticle: null,
  62.     };
  63.  
  64.     // Използваме пропнатите стойности, ако има такива
  65.     const actualInitialValues = propInitialValues || defaultValues;
  66.  
  67.     // Определяме правилния onSubmitHandler
  68.     const submitHandler = onSubmitHandler || createArticle;
  69.  
  70.     // Подготвяме началните mediaFiles според наличните изображения от initialValues
  71.     const preparedMediaFiles = useMemo(() => {
  72.         const mediaFiles = {
  73.             mainImage: [],
  74.             sectionImages: {}
  75.         };
  76.  
  77.         // Ако редактираме статия със съществуващи изображения в секциите
  78.         if (actualInitialValues && actualInitialValues.sections) {
  79.             actualInitialValues.sections.forEach((section, index) => {
  80.                 if (Array.isArray(section.image) && section.image.length > 0) {
  81.                     // Имаме изображения в тази секция - добавяме празен масив
  82.                     // Това ще ни помогне да знаем, че секцията има изображения
  83.                     mediaFiles.sectionImages[index] = [];
  84.                 }
  85.             });
  86.         }
  87.  
  88.         return mediaFiles;
  89.     }, []);
  90.  
  91.     const {
  92.         values,
  93.         errors,
  94.         isUploading,
  95.         uploadProgress,
  96.         onChangeHandler,
  97.         onBlurHandler,
  98.         onSubmit,
  99.         handleMainImageTypeChange,
  100.         handleMainImageFiles,
  101.         handleSectionImageFile,
  102.         handleMainImageUrl,
  103.         handleSectionImageUrl,
  104.         removeUrlImage,
  105.         removeMainImage,
  106.         removeSectionImage,
  107.         addSection,
  108.         removeSection,
  109.         swapSectionsMedia,
  110.         addTag,
  111.         updateImageInfo,
  112.         removeTag,
  113.         mediaFiles,
  114.         convertEditorToHtml,
  115.         uploadThumbnailFile,
  116.         updateImageAlt,
  117.     } = useCreateArticle(actualInitialValues, submitHandler);
  118.  
  119.     useImperativeHandle(ref, () => ({
  120.         onSubmit,
  121.         mediaFiles,
  122.         values
  123.     }));
  124.  
  125.     const [newTag, setNewTag] = useState("");
  126.     const [, forceUpdate] = useReducer(x => x + 1, 0);
  127.     const [imageUrl, setImageUrl] = useState("");
  128.     const [sectionImageUrls, setSectionImageUrls] = useState({});
  129.  
  130.     const videoUrlInputRef = useRef(null);
  131.  
  132.     const videoPreviewUrl = useMemo(() => {
  133.         if (mediaFiles.mainImage && mediaFiles.mainImage.length > 0 && mediaFiles.mainImage[0]) {
  134.             try {
  135.                 return URL.createObjectURL(mediaFiles.mainImage[0]);
  136.             } catch (error) {
  137.                 console.error("Error creating URL:", error);
  138.                 return null;
  139.             }
  140.         }
  141.         return null;
  142.     }, [mediaFiles.mainImage]);
  143.  
  144.     const handleAddImageUrl = () => {
  145.         if (handleMainImageUrl(imageUrl)) {
  146.             setImageUrl("");
  147.         }
  148.     };
  149.  
  150.     const openAltEditModal = (sectionIndex, imageIndex, image) => {
  151.         // Копираме изображението за да избегнем проблеми с референции
  152.         setCurrentEditingImage({
  153.             sectionIndex,
  154.             imageIndex,
  155.             image: { ...image }
  156.         });
  157.         setIsAltModalOpen(true);
  158.     };
  159.  
  160.     // Функция за запазване на промените в ALT текста
  161.     const handleSaveImageInfo = (altEditorState, captionEditorState) => {
  162.         const { sectionIndex, imageIndex } = currentEditingImage;
  163.         updateImageInfo(sectionIndex, imageIndex, altEditorState, captionEditorState);
  164.     };
  165.  
  166.     // Кеширане на blob URL-и за изображения в слайдера
  167.     const mainImagePreviewUrls = useMemo(() => {
  168.         if (mediaFiles.mainImage && mediaFiles.mainImage.length > 0) {
  169.             return mediaFiles.mainImage.map(file => {
  170.                 try {
  171.                     return URL.createObjectURL(file);
  172.                 } catch (error) {
  173.                     console.error("Error creating URL:", error);
  174.                     return null;
  175.                 }
  176.             });
  177.         }
  178.         return [];
  179.     }, [mediaFiles.mainImage]);
  180.  
  181.     // Кеширане на blob URL-и за изображения в секциите
  182.     const sectionImagePreviewUrls = useMemo(() => {
  183.         const urls = {};
  184.  
  185.         // 1. Обработка на файлове от mediaFiles.sectionImages
  186.         if (mediaFiles.sectionImages) {
  187.             Object.entries(mediaFiles.sectionImages).forEach(([index, file]) => {
  188.                 try {
  189.                     if (Array.isArray(file) && file.length > 0 &&
  190.                         (file[0] instanceof Blob || file[0] instanceof File)) {
  191.                         urls[index] = URL.createObjectURL(file[0]);
  192.                     } else if (file && (file instanceof Blob || file instanceof File)) {
  193.                         urls[index] = URL.createObjectURL(file);
  194.                     }
  195.                 } catch (error) {
  196.                     console.error("Error creating URL for section image:", error);
  197.                     urls[index] = null;
  198.                 }
  199.             });
  200.         }
  201.  
  202.         return urls;
  203.     }, [mediaFiles.sectionImages]);
  204.  
  205.     // ВАЖНО: Почистване на blob URL-и при размонтиране на компонента
  206.     useEffect(() => {
  207.         return () => {
  208.             // Освобождаване на видео URL
  209.             if (videoPreviewUrl) {
  210.                 URL.revokeObjectURL(videoPreviewUrl);
  211.             }
  212.  
  213.             // Освобождаване на URL-и на основни изображения
  214.             mainImagePreviewUrls.forEach(url => {
  215.                 if (url) URL.revokeObjectURL(url);
  216.             });
  217.  
  218.             // Освобождаване на URL-и на секционни изображения
  219.             Object.values(sectionImagePreviewUrls).forEach(url => {
  220.                 if (url) URL.revokeObjectURL(url);
  221.             });
  222.         };
  223.     }, [videoPreviewUrl, mainImagePreviewUrls, sectionImagePreviewUrls]);
  224.  
  225.     const handleEditorChange = (editorState, name) => {
  226.         onChangeHandler(null, true, { name, value: editorState });
  227.     };
  228.  
  229.     const handleEditorBlur = (name, editorState) => {
  230.         onBlurHandler(null, true, { name, value: editorState });
  231.     };
  232.  
  233.     const moveSectionUp = (index) => {
  234.         if (index <= 0) return;
  235.  
  236.         // Създаваме ново копие на масива със секции
  237.         const updatedSections = [...values.sections];
  238.  
  239.         // Запазваме текущата секция и тази над нея
  240.         const currentSection = { ...updatedSections[index] };
  241.         const prevSection = { ...updatedSections[index - 1] };
  242.  
  243.         // Разменяме ги
  244.         updatedSections[index - 1] = currentSection;
  245.         updatedSections[index] = prevSection;
  246.  
  247.         // Актуализираме order свойството
  248.         updatedSections.forEach((section, idx) => {
  249.             section.order = idx + 1;
  250.         });
  251.  
  252.         // Правим директен update на секциите в стейта
  253.         onChangeHandler(null, true, { name: "sections", value: updatedSections });
  254.  
  255.         // ВАЖНО! Разменяме медия файловете също
  256.         swapSectionsMedia(index, index - 1);
  257.  
  258.         // Актуализираме активната секция
  259.         setActiveSection(index - 1);
  260.     };
  261.  
  262.     const moveSectionDown = (index) => {
  263.         if (index >= values.sections.length - 1) return;
  264.  
  265.         // Създаваме ново копие на масива със секции
  266.         const updatedSections = [...values.sections];
  267.  
  268.         // Запазваме текущата секция и тази под нея
  269.         const currentSection = { ...updatedSections[index] };
  270.         const nextSection = { ...updatedSections[index + 1] };
  271.  
  272.         // Разменяме ги
  273.         updatedSections[index + 1] = currentSection;
  274.         updatedSections[index] = nextSection;
  275.  
  276.         // Актуализираме order свойството
  277.         updatedSections.forEach((section, idx) => {
  278.             section.order = idx + 1;
  279.         });
  280.  
  281.         // Правим директен update на секциите в стейта
  282.         onChangeHandler(null, true, { name: "sections", value: updatedSections });
  283.  
  284.         // ВАЖНО! Разменяме медия файловете също
  285.         swapSectionsMedia(index, index + 1);
  286.  
  287.         // Актуализираме активната секция
  288.         setActiveSection(index + 1);
  289.     };
  290.  
  291.     const handleTagAdd = (e) => {
  292.         e.preventDefault();
  293.         if (newTag.trim()) {
  294.             addTag(newTag.trim());
  295.             setNewTag("");
  296.         }
  297.     };
  298.  
  299.     const handlePreviewToggle = () => {
  300.         setPreviewMode(!previewMode);
  301.     };
  302.  
  303.     const [expandedImageUrl, setExpandedImageUrl] = useState(null);
  304.  
  305.     const handleImageClick = (url) => {
  306.         setExpandedImageUrl(url);
  307.     };
  308.  
  309.     const closeExpandedImage = () => {
  310.         setExpandedImageUrl(null);
  311.     };
  312.  
  313.     // Функция за обработка на видео файлове
  314.     const handleVideoFile = (files) => {
  315.         if (!files || files.length === 0) return;
  316.  
  317.         const videoFile = files[0]; // Вземаме само първия файл за видео
  318.         if (videoFile) {
  319.             // Проверка за видео формат
  320.             if (!/video\/(mp4|webm|ogg)/.test(videoFile.type)) {
  321.                 alert(t('articles.createForm.invalidVideoFormat'));
  322.                 return;
  323.             }
  324.  
  325.             // Проверка за размер на файла (100MB = 104857600 bytes)
  326.             if (videoFile.size > 104857600) {
  327.                 alert(t('articles.createForm.videoSizeExceeded'));
  328.                 return;
  329.             }
  330.  
  331.             // Подаваме директно за обработка
  332.             handleMainImageFiles([videoFile]);
  333.  
  334.             // Добавяме форсирано обновяване, но сега използваме useReducer версията
  335.             setTimeout(() => forceUpdate(), 100);
  336.         }
  337.     };
  338.  
  339.     // Функция за добавяне на външно видео от URL
  340.     const handleAddVideoUrl = () => {
  341.         if (!values.mainImage.videoUrl) return;
  342.  
  343.         // Проверка на URL формата (опростена)
  344.         const youtubeRegex = /^(https?:\/\/)?(www\.)?(youtube\.com|youtu\.be)\/.+$/;
  345.         const vimeoRegex = /^(https?:\/\/)?(www\.)?vimeo\.com\/.+$/;
  346.  
  347.         if (!youtubeRegex.test(values.mainImage.videoUrl) && !vimeoRegex.test(values.mainImage.videoUrl)) {
  348.             alert(t('articles.createForm.invalidVideoUrl'));
  349.             return;
  350.         }
  351.  
  352.         // Генерираме thumbnail URL ако е YouTube
  353.         let thumbnailUrl = "";
  354.         if (youtubeRegex.test(values.mainImage.videoUrl)) {
  355.             // Опит да извлечем видео ID
  356.             let videoId = "";
  357.             if (values.mainImage.videoUrl.includes("youtube.com/watch?v=")) {
  358.                 videoId = values.mainImage.videoUrl.split("v=")[1]?.split("&")[0];
  359.             } else if (values.mainImage.videoUrl.includes("youtu.be/")) {
  360.                 videoId = values.mainImage.videoUrl.split("youtu.be/")[1]?.split("?")[0];
  361.             }
  362.  
  363.             if (videoId) {
  364.                 thumbnailUrl = `https://img.youtube.com/vi/${videoId}/hqdefault.jpg`;
  365.                 // Ако има thumbnail, сетваме го автоматично
  366.                 onChangeHandler({ target: { name: "mainImage.thumbnail", value: thumbnailUrl } });
  367.             }
  368.         }
  369.  
  370.         // Уведомяваме потребителя, че външното видео е добавено
  371.         alert(t('articles.createForm.videoAddedSuccess'));
  372.  
  373.         // Принуждаваме компонента да се преизрисува с useReducer версията
  374.         forceUpdate();
  375.     };
  376.  
  377.     // Общи настройки за редактора
  378.     const editorToolbarOptions = {
  379.         options: ['inline', 'blockType', 'fontSize', 'list', 'textAlign', 'link', 'emoji', 'history'],
  380.         inline: {
  381.             options: ['bold', 'italic', 'underline', 'strikethrough'],
  382.             className: 'editor-toolbar-inline',
  383.         },
  384.           fontSize: {
  385.              options: [12, 14, 16, 18, 20, 24, 28, 32, 36],
  386.             className: 'editor-toolbar-fontsize',
  387.             dropdownClassName: 'editor-fontsize-dropdown',
  388.             inDropdown: true,
  389.         },
  390.        
  391.         blockType: {
  392.             options: ['Normal', 'H2', 'H3', 'H4', 'Blockquote'],
  393.             className: 'editor-toolbar-block',
  394.         },
  395.      
  396.         list: {
  397.             options: ['unordered', 'ordered'],
  398.         },
  399.         textAlign: {
  400.             inDropdown: true,
  401.         },
  402.         link: {
  403.             inDropdown: false,
  404.             showOpenOptionOnHover: true,
  405.         },
  406.         emoji: {
  407.             emojis: [
  408.                 '😀', '😁', '😂', '😃', '😉', '😋', '😎', '😍', '😮', '🙂', '🙃', '🤑', '🤔', '🤗', '🤐',
  409.                 '🤡', '🤥', '🤨', '🤩', '🤪', '🤫', '🤬', '🤭', '🧐', '🤯', '😴', '😌', '😛', '😜', '😝'
  410.             ],
  411.         },
  412.     };
  413.  
  414.     // Опростени настройки за малки полета (alt текст)
  415.     const minimalEditorToolbarOptions = {
  416.         options: ['inline', 'link','fontSize'],
  417.         inline: {
  418.             options: ['bold', 'italic', 'underline'],
  419.             className: 'editor-toolbar-inline-small',
  420.         },
  421.         fontSize: {
  422.              options: [12, 14, 16, 18, 20, 24, 28, 32, 36],
  423.             className: 'editor-toolbar-fontsize',
  424.             dropdownClassName: 'editor-fontsize-dropdown',
  425.             inDropdown: true,
  426.             defaultSize: 16,
  427.         },
  428.         link: {
  429.             inDropdown: false,
  430.             showOpenOptionOnHover: true,
  431.         },
  432.     };
  433.  
  434.     if (previewMode) {
  435.         return (
  436.             <ArticlePreview
  437.                 article={values}
  438.                 onBack={handlePreviewToggle}
  439.                 mediaFiles={mediaFiles}
  440.                 convertEditorToHtml={convertEditorToHtml}
  441.             />
  442.         );
  443.     }
  444.  
  445.     // Определяме текстовете според режима (създаване или редактиране)
  446.     const formTitle = isEditMode
  447.         ? t('articles.editArticle.edit_article')
  448.         : t('articles.createForm.createNewArticle');
  449.  
  450.     const submitButtonText = isEditMode
  451.         ? t('articles.editArticle.save_changes')
  452.         : t('articles.createForm.saveBtn');
  453.  
  454.     return (
  455.         <div className="article-create-container">
  456.             {/* Постоянно фиксирано меню - ще се показва винаги */}
  457.             <SectionQuickMenu
  458.                 sectionIndex={activeSection !== null ? activeSection : 0}
  459.                 totalSections={values.sections.length}
  460.                 onAddSection={addSection}
  461.                 onMoveUp={moveSectionUp}
  462.                 onMoveDown={moveSectionDown}
  463.                 onRemove={removeSection}
  464.             />
  465.             <h2 className="article-form-title">{formTitle}</h2>
  466.  
  467.             <form className="article-form" onSubmit={onSubmit}>
  468.                 {/* Основна информация */}
  469.                 <div className="form-section">
  470.                     <h3>{t('articles.createForm.basicInfo')}</h3>
  471.                     <div className="form-section-content">
  472.                         <div className="form-group-article">
  473.                             <label htmlFor="title">{t('articles.createForm.title')} <span className="required">*</span></label>
  474.                             <input
  475.                                 type="text"
  476.                                 id="title"
  477.                                 name="title"
  478.                                 value={values.title}
  479.                                 onChange={onChangeHandler}
  480.                                 onBlur={onBlurHandler}
  481.                                 className={errors.title ? "error" : ""}
  482.                                 placeholder={t('articles.createForm.titlePlaceholder')}
  483.                             />
  484.                             {errors.title && <div className="error-message">{errors.title}</div>}
  485.                         </div>
  486.  
  487.                         <div className="form-group-article">
  488.                             <label htmlFor="slug">{t('articles.createForm.slug')} <span className="required">*</span></label>
  489.                             <input
  490.                                 type="text"
  491.                                 id="slug"
  492.                                 name="slug"
  493.                                 value={values.slug}
  494.                                 onChange={onChangeHandler}
  495.                                 onBlur={onBlurHandler}
  496.                                 className={errors.slug ? "error" : ""}
  497.                                 placeholder={t('articles.createForm.slugPlaceholder')}
  498.                             />
  499.                             {errors.slug && <div className="error-message">{errors.slug}</div>}
  500.                         </div>
  501.  
  502.                         <div className="form-group-article">
  503.                             <label htmlFor="author">{t('articles.createForm.author')} <span className="required">*</span></label>
  504.                             <input
  505.                                 type="text"
  506.                                 id="author"
  507.                                 name="author"
  508.                                 value={values.author}
  509.                                 onChange={onChangeHandler}
  510.                                 onBlur={onBlurHandler}
  511.                                 className={errors.author ? "error" : ""}
  512.                                 placeholder={t('articles.createForm.authorPlaceholder')}
  513.                             />
  514.                             {errors.author && <div className="error-message">{errors.author}</div>}
  515.                         </div>
  516.  
  517.                         <div className="form-group-article">
  518.                             <label htmlFor="publishDate">{t('articles.createForm.publishDate')}</label>
  519.                             <input
  520.                                 type="date"
  521.                                 id="publishDate"
  522.                                 name="publishDate"
  523.                                 value={values.publishDate}
  524.                                 onChange={onChangeHandler}
  525.                             />
  526.                         </div>
  527.  
  528.                         <div className="form-group-article">
  529.                             <label htmlFor="summary">{t('articles.createForm.summary')} <span className="required">*</span></label>
  530.                             <div className={errors.summary ? "editor-container error" : "editor-container"}>
  531.                                 <Editor
  532.                                     editorState={values.summary}
  533.                                     onEditorStateChange={(editorState) => handleEditorChange(editorState, "summary")}
  534.                                     onBlur={() => handleEditorBlur("summary", values.summary)}
  535.                                     toolbar={editorToolbarOptions}
  536.                                     placeholder={t('articles.createForm.summaryPlaceholder')}
  537.                                     wrapperClassName="editor-wrapper"
  538.                                     editorClassName="editor-main"
  539.                                     toolbarClassName="editor-toolbar"
  540.                                     key={i18n.language}
  541.                                 />
  542.                             </div>
  543.                             {errors.summary && <div className="error-message">{errors.summary}</div>}
  544.                         </div>
  545.                     </div>
  546.                 </div>
  547.  
  548.                 {/* Основно изображение или медия */}
  549.                 <div className="form-section">
  550.                     <h3>{t('articles.createForm.mainMedia')}</h3>
  551.                     <div className="form-section-content">
  552.                         <div className="media-type-selector">
  553.                             <button
  554.                                 type="button"
  555.                                 className={values.mainImage.type === "image" ? "active" : ""}
  556.                                 onClick={() => handleMainImageTypeChange("image")}
  557.                             >
  558.                                 <FontAwesomeIcon icon={faImage} /> {t('articles.createForm.singleImage')}
  559.                             </button>
  560.                             <button
  561.                                 type="button"
  562.                                 className={values.mainImage.type === "slider" ? "active" : ""}
  563.                                 onClick={() => handleMainImageTypeChange("slider")}
  564.                             >
  565.                                 <FontAwesomeIcon icon={faSliders} /> {t('articles.createForm.slider')}
  566.                             </button>
  567.                             <button
  568.                                 type="button"
  569.                                 className={values.mainImage.type === "video" ? "active" : ""}
  570.                                 onClick={() => handleMainImageTypeChange("video")}
  571.                             >
  572.                                 <FontAwesomeIcon icon={faVideo} /> {t('articles.createForm.video')}
  573.                             </button>
  574.                         </div>
  575.  
  576.                         <div className="media-upload-container">
  577.                             {(values.mainImage.type === "image" || values.mainImage.type === "slider") && (
  578.                                 <>
  579.                                     <div className="form-group-article">
  580.                                         <label htmlFor="mainImageAlt">{t('articles.createForm.altText')} <span className="required">*</span></label>
  581.                                         <div className={errors["mainImage.alt"] ? "editor-container error" : "editor-container"}>
  582.                                             <Editor
  583.                                                 editorState={values.mainImage.alt}
  584.                                                 onEditorStateChange={(editorState) => handleEditorChange(editorState, "mainImage.alt")}
  585.                                                 onBlur={() => handleEditorBlur("mainImage.alt", values.mainImage.alt)}
  586.                                                 toolbar={minimalEditorToolbarOptions}
  587.                                                 placeholder={t('articles.createForm.imageDescriptionPlaceholder')}
  588.                                                 wrapperClassName="editor-wrapper-small"
  589.                                                 editorClassName="editor-main-small"
  590.                                                 toolbarClassName="editor-toolbar-small"
  591.                                                 key={i18n.language}
  592.                                             />
  593.                                         </div>
  594.                                         {errors["mainImage.alt"] && <div className="error-message">{errors["mainImage.alt"]}</div>}
  595.                                     </div>
  596.  
  597.                                     {/* Нова секция за добавяне чрез URL */}
  598.                                     <div className="form-group-article">
  599.                                         <label>{t('articles.createForm.addViaUrl')}</label>
  600.                                         <div className="image-url-input">
  601.                                             <input
  602.                                                 type="text"
  603.                                                 placeholder={t('articles.createForm.imageUrlPlaceholder')}
  604.                                                 value={imageUrl}
  605.                                                 onChange={(e) => setImageUrl(e.target.value)}
  606.                                                 onKeyPress={(e) => e.key === 'Enter' && handleAddImageUrl()}
  607.                                             />
  608.                                             <button
  609.                                                 type="button"
  610.                                                 className="add-image-url-btn"
  611.                                                 onClick={handleAddImageUrl}
  612.                                             >
  613.                                                 <FontAwesomeIcon icon={faPlus} /> {t('articles.createForm.addBtn')}
  614.                                             </button>
  615.                                         </div>
  616.                                     </div>
  617.  
  618.                                     <div className="file-upload-area">
  619.                                         <div className="file-upload-icon">
  620.                                             <FontAwesomeIcon icon={faCloudUploadAlt} />
  621.                                         </div>
  622.                                         <p className="file-upload-text">
  623.                                             {t('articles.createForm.dragDropFile')}
  624.                                         </p>
  625.                                         <label htmlFor="mainImageFile" className="file-upload-label">
  626.                                             <FontAwesomeIcon icon={faUpload} />
  627.                                             {values.mainImage.type === "image"
  628.                                                 ? t('articles.createForm.uploadMainImageBtn')
  629.                                                 : t('articles.createForm.uploadSliderImagesBtn')}
  630.                                         </label>
  631.                                         <input
  632.                                             type="file"
  633.                                             id="mainImageFile"
  634.                                             multiple={values.mainImage.type === "slider"}
  635.                                             onChange={(e) => handleMainImageFiles(e.target.files)}
  636.                                             accept="image/jpeg,image/png,image/jpg,image/webp"
  637.                                             className="file-input"
  638.                                         />
  639.                                     </div>
  640.  
  641.                                     {/* Предпреглед на всички изображения - файлове и URL-и */}
  642.                                     {(mediaFiles.mainImage.length > 0 || values.mainImage.sources.length > 0) && (
  643.                                         <div className="media-preview-container">
  644.                                             {/* Показване на файловете */}
  645.                                             {mediaFiles.mainImage.map((file, index) => (
  646.                                                 <div key={`file-${index}`} className="image-preview-item">
  647.                                                     <img
  648.                                                         src={mainImagePreviewUrls[index]}
  649.                                                         alt={t('articles.createForm.preview', { index: index })}
  650.                                                         onClick={() => handleImageClick(mainImagePreviewUrls[index])}
  651.                                                     />
  652.                                                     <button
  653.                                                         type="button"
  654.                                                         className="remove-image-btn"
  655.                                                         onClick={() => removeMainImage(index)}
  656.                                                     >
  657.                                                         <FontAwesomeIcon icon={faTimes} />
  658.                                                     </button>
  659.                                                 </div>
  660.                                             ))}
  661.  
  662.                                             {/* Показване на URL адресите */}
  663.                                             {values.mainImage.sources.map((url, index) => (
  664.                                                 <div key={`url-${index}`} className="image-preview-item">
  665.                                                     <img
  666.                                                         src={url}
  667.                                                         alt={t('articles.createForm.urlImage', { index: index })}
  668.                                                         onClick={() => handleImageClick(url)}
  669.                                                     />
  670.                                                     <button
  671.                                                         type="button"
  672.                                                         className="remove-image-btn"
  673.                                                         onClick={() => removeUrlImage(index)}
  674.                                                     >
  675.                                                         <FontAwesomeIcon icon={faTimes} />
  676.                                                     </button>
  677.                                                 </div>
  678.                                             ))}
  679.                                         </div>
  680.                                     )}
  681.                                 </>
  682.                             )}
  683.  
  684.                             {/* Раздел за видео, показва се само когато типът е видео */}
  685.                             {values.mainImage.type === "video" && (
  686.                                 <>
  687.                                     <div className="form-group-article">
  688.                                         <label htmlFor="mainImageAlt">{t('articles.createForm.videoTitle')} <span className="required">*</span></label>
  689.                                         <div className={errors["mainImage.alt"] ? "editor-container error" : "editor-container"}>
  690.                                             <Editor
  691.                                                 editorState={values.mainImage.alt}
  692.                                                 onEditorStateChange={(editorState) => handleEditorChange(editorState, "mainImage.alt")}
  693.                                                 onBlur={() => handleEditorBlur("mainImage.alt", values.mainImage.alt)}
  694.                                                 toolbar={minimalEditorToolbarOptions}
  695.                                                 placeholder={t('articles.createForm.videoTitlePlaceholder')}
  696.                                                 wrapperClassName="editor-wrapper-small"
  697.                                                 editorClassName="editor-main-small"
  698.                                                 toolbarClassName="editor-toolbar-small"
  699.                                                 key={i18n.language}
  700.                                             />
  701.                                         </div>
  702.                                         {errors["mainImage.alt"] && <div className="error-message">{errors["mainImage.alt"]}</div>}
  703.                                     </div>
  704.  
  705.                                     <div className="form-group-article">
  706.                                         <label htmlFor="videoThumbnail">{t('articles.createForm.thumbnail')}</label>
  707.                                         <input
  708.                                             type="text"
  709.                                             id="videoThumbnail"
  710.                                             name="mainImage.thumbnail"
  711.                                             value={values.mainImage.thumbnail}
  712.                                             onChange={onChangeHandler}
  713.                                             placeholder={t('articles.createForm.videoThumbnailPlaceholder')}
  714.                                         />
  715.                                     </div>
  716.  
  717.                                     <div className="video-upload-options">
  718.                                         <div className="form-group-article">
  719.                                             <label>{t('articles.createForm.chooseVideoMethod')}</label>
  720.                                             <div className="video-options-buttons">
  721.                                                 <button
  722.                                                     type="button"
  723.                                                     className="video-option-btn"
  724.                                                     onClick={() => document.getElementById('mainVideoFile').click()}
  725.                                                 >
  726.                                                     <FontAwesomeIcon icon={faUpload} /> {t('articles.createForm.uploadFile')}
  727.                                                 </button>
  728.                                                 <span className="or-divider">{t('articles.createForm.or')}</span>
  729.                                                 <div className="video-url-input">
  730.                                                     <input
  731.                                                         type="text"
  732.                                                         placeholder={t('articles.createForm.videoUrlPlaceholder')}
  733.                                                         name="mainImage.videoUrl"
  734.                                                         value={values.mainImage.videoUrl || ''}
  735.                                                         onChange={onChangeHandler}
  736.                                                         ref={videoUrlInputRef}
  737.                                                         onKeyPress={(e) => e.key === 'Enter' && handleAddVideoUrl()}
  738.                                                     />
  739.                                                     <button
  740.                                                         type="button"
  741.                                                         className="add-video-url-btn"
  742.                                                         onClick={handleAddVideoUrl}
  743.                                                     >
  744.                                                         <FontAwesomeIcon icon={faPlus} /> {t('articles.createForm.addBtn')}
  745.                                                     </button>
  746.                                                 </div>
  747.                                             </div>
  748.                                         </div>
  749.  
  750.                                         <div className="file-upload-area">
  751.                                             <div className="file-upload-icon">
  752.                                                 <FontAwesomeIcon icon={faCloudUploadAlt} />
  753.                                             </div>
  754.                                             <p className="file-upload-text">
  755.                                                 {t('articles.createForm.dragDropVideo')}
  756.                                             </p>
  757.                                             <label htmlFor="mainVideoFile" className="file-upload-label">
  758.                                                 <FontAwesomeIcon icon={faUpload} /> {t('articles.createForm.uploadVideoBtn')}
  759.                                             </label>
  760.                                             <input
  761.                                                 type="file"
  762.                                                 id="mainVideoFile"
  763.                                                 onChange={(e) => handleVideoFile(e.target.files)}
  764.                                                 accept="video/mp4,video/webm,video/ogg"
  765.                                                 className="file-input"
  766.                                             />
  767.                                             <p className="upload-info">{t('articles.createForm.supportedFormats')}</p>
  768.                                         </div>
  769.                                     </div>
  770.  
  771.                                     {/* Предпреглед на видео - използва кеширания URL */}
  772.                                     {values.mainImage.type === "video" && mediaFiles.mainImage && mediaFiles.mainImage.length > 0 && (
  773.                                         <div className="video-preview-container" >
  774.                                             <div className="video-element-wrapper">
  775.                                                 <video
  776.                                                     controls
  777.                                                     width="100%"
  778.                                                     height="auto"
  779.                                                     src={videoPreviewUrl} // Използваме кеширания URL
  780.                                                     poster={values.mainImage.thumbnail || ""}
  781.                                                 >
  782.                                                     {t('articles.createForm.browserNotSupport')}
  783.                                                 </video>
  784.                                             </div>
  785.                                             <div className="video-controls-container">
  786.                                                 <div className="video-info-details">
  787.                                                     <h4 dangerouslySetInnerHTML={{ __html: convertEditorToHtml(values.mainImage.alt) || t('articles.createForm.videoFile') }}></h4>
  788.                                                     <p>{mediaFiles.mainImage[0]?.name || t('articles.createForm.unnamedFile')}</p>
  789.                                                     <p>{t('articles.createForm.size')} {mediaFiles.mainImage[0]?.size ? (mediaFiles.mainImage[0].size / (1024 * 1024)).toFixed(2) + " MB" : t('articles.createForm.unknownSize')}</p>
  790.                                                 </div>
  791.                                                 <button
  792.                                                     type="button"
  793.                                                     className="remove-video-btn"
  794.                                                     onClick={() => removeMainImage(0)}
  795.                                                 >
  796.                                                     <FontAwesomeIcon icon={faTimes} /> {t('articles.createForm.removeBtn')}
  797.                                                 </button>
  798.                                             </div>
  799.                                         </div>
  800.                                     )}
  801.                                     {/* Добавяме генератора на thumbnail за качени видео файлове */}
  802.                                     {values.mainImage.type === "video" && mediaFiles.mainImage && mediaFiles.mainImage.length > 0 && (
  803.                                         <VideoThumbnailGenerator
  804.                                             videoFile={mediaFiles.mainImage[0]}
  805.                                             onThumbnailGenerated={(thumbnailFile) => {
  806.                                                 uploadThumbnailFile(thumbnailFile);
  807.                                             }}
  808.                                         />
  809.                                     )}
  810.  
  811.                                     {/* Предпреглед за външно видео от URL */}
  812.                                     {values.mainImage.type === "video" && values.mainImage.videoUrl && !mediaFiles.mainImage?.length && (
  813.                                         <div className="video-preview-container">
  814.                                             <VideoPlayer
  815.                                                 src={values.mainImage.videoUrl}
  816.                                                 thumbnail={values.mainImage.thumbnail || ''}
  817.                                                 alt={convertEditorToHtml(values.mainImage.alt) || t('articles.createForm.urlVideo')}
  818.                                                 allowDownload={false}
  819.                                             />
  820.                                             <div className="video-controls-container">
  821.                                                 <div className="video-info-details">
  822.                                                     <h4 dangerouslySetInnerHTML={{ __html: convertEditorToHtml(values.mainImage.alt) || t('articles.createForm.externalVideo') }}></h4>
  823.                                                     {/* <p>URL: {values.mainImage.videoUrl}</p> */}
  824.                                                 </div>
  825.                                                 <button
  826.                                                     type="button"
  827.                                                     className="remove-video-btn"
  828.                                                     onClick={() => {
  829.                                                         onChangeHandler({ target: { name: "mainImage.videoUrl", value: "" } });
  830.                                                         forceUpdate(); // Използваме useReducer версията
  831.                                                     }}
  832.                                                 >
  833.                                                     <FontAwesomeIcon icon={faTimes} /> {t('articles.createForm.removeBtn')}
  834.                                                 </button>
  835.                                             </div>
  836.                                         </div>
  837.                                     )}
  838.                                 </>
  839.                             )}
  840.                         </div>
  841.                     </div>
  842.                 </div>
  843.  
  844.                 {/* Секции статии */}
  845.                 <div className="form-section">
  846.                     <h3>{t('articles.createForm.content')}</h3>
  847.                     <div className="form-section-content">
  848.                         <div className="section-header" style={{ background: 'none', padding: '0 0 20px 0' }}>
  849.                             <h4>{t('articles.createForm.sections')}</h4>
  850.                             <button type="button" className="add-section-btn" onClick={addSection}>
  851.                                 <FontAwesomeIcon icon={faPlus} /> {t('articles.createForm.addSectionBtn')}
  852.                             </button>
  853.                         </div>
  854.  
  855.                         {values.sections.map((section, index) => (
  856.                             <div
  857.                                 key={index}
  858.                                 className={`article-section-item ${activeSection === index ? 'active-section' : ''}`}
  859.                                 onClick={() => setActiveSection(index)}
  860.                             >
  861.                                 <div className="section-header">
  862.                                     <h4>{t('articles.createForm.sectionWithNumber', { number: index + 1 })}</h4>
  863.                                     {values.sections.length > 1 && (
  864.                                         <button
  865.                                             type="button"
  866.                                             className="remove-section-btn"
  867.                                             onClick={() => removeSection(index)}
  868.                                         >
  869.                                             <FontAwesomeIcon icon={faMinus} /> {t('articles.createForm.removeBtn')}
  870.                                         </button>
  871.                                     )}
  872.                                 </div>
  873.  
  874.                                 <div className="section-content-create">
  875.                                     <div className="form-group-article">
  876.                                         <label htmlFor={`section-title-${index}`}>{t('articles.createForm.title')} <span className="required">*</span></label>
  877.                                         <input
  878.                                             type="text"
  879.                                             id={`section-title-${index}`}
  880.                                             name={`sections[${index}].title`}
  881.                                             value={section.title}
  882.                                             onChange={onChangeHandler}
  883.                                             onBlur={onBlurHandler}
  884.                                             className={errors[`sections[${index}].title`] ? "error" : ""}
  885.                                             placeholder={t('articles.createForm.sectionTitlePlaceholder')}
  886.                                         />
  887.                                         {errors[`sections[${index}].title`] && <div className="error-message">{errors[`sections[${index}].title`]}</div>}
  888.                                     </div>
  889.  
  890.                                     <div className="form-group-article">
  891.                                         <label htmlFor={`section-content-${index}`}>{t('articles.createForm.contentSimple')} <span className="required">*</span></label>
  892.                                         <div className={errors[`sections[${index}].content`] ? "editor-container error" : "editor-container"}>
  893.                                             <Editor
  894.                                                 editorState={section.content}
  895.                                                 onEditorStateChange={(editorState) => handleEditorChange(editorState, `sections[${index}].content`)}
  896.                                                 onBlur={() => handleEditorBlur(`sections[${index}].content`, section.content)}
  897.                                                 toolbar={editorToolbarOptions}
  898.                                                 placeholder={t('articles.createForm.sectionContentPlaceholder')}
  899.                                                 wrapperClassName="editor-wrapper"
  900.                                                 editorClassName="editor-main"
  901.                                                 toolbarClassName="editor-toolbar"
  902.                                                 key={i18n.language}
  903.                                             />
  904.                                         </div>
  905.                                         {errors[`sections[${index}].content`] && <div className="error-message">{errors[`sections[${index}].content`]}</div>}
  906.                                     </div>
  907.  
  908.                                     <div className="form-group-article">
  909.                                         <label htmlFor={`section-image-${index}`}>{t('articles.createForm.sectionImages')}</label>
  910.  
  911.                                         <div className="form-group-article">
  912.                                             <label>{t('articles.createForm.addViaUrl')}</label>
  913.                                             <div className="image-url-input">
  914.                                                 <input
  915.                                                     type="text"
  916.                                                     placeholder={t('articles.createForm.imageUrlPlaceholder')}
  917.                                                     value={sectionImageUrls[index] || ''}
  918.                                                     onChange={(e) => setSectionImageUrls({ ...sectionImageUrls, [index]: e.target.value })}
  919.                                                     onKeyPress={(e) => e.key === 'Enter' && handleSectionImageUrl(sectionImageUrls[index], index)}
  920.                                                 />
  921.                                                 <button
  922.                                                     type="button"
  923.                                                     className="add-image-url-btn"
  924.                                                     onClick={() => {
  925.                                                         if (handleSectionImageUrl(sectionImageUrls[index], index)) {
  926.                                                             setSectionImageUrls({ ...sectionImageUrls, [index]: '' });
  927.                                                         }
  928.                                                     }}
  929.                                                 >
  930.                                                     <FontAwesomeIcon icon={faPlus} /> {t('articles.createForm.addBtn')}
  931.                                                 </button>
  932.                                             </div>
  933.                                         </div>
  934.  
  935.                                         <div className="file-upload-area" style={{ padding: '20px' }}>
  936.                                             <label htmlFor={`section-image-${index}`} className="file-upload-label">
  937.                                                 <FontAwesomeIcon icon={faUpload} /> {t('articles.createForm.chooseImage')}
  938.                                             </label>
  939.                                             <input
  940.                                                 type="file"
  941.                                                 id={`section-image-${index}`}
  942.                                                 multiple={true}
  943.                                                 onChange={(e) => {
  944.                                                     if (e.target.files && e.target.files.length > 0) {
  945.                                                         handleSectionImageFile(e.target.files, index);
  946.                                                     }
  947.                                                 }}
  948.                                                 accept="image/jpeg,image/png,image/jpg,image/webp"
  949.                                                 className="file-input"
  950.                                             />
  951.                                         </div>
  952.  
  953.                                         {/* Показване на всички изображения */}
  954.                                         <div className="section-images-container">
  955.  
  956.                                             {/* Показване на всички изображения от масива */}
  957.                                             {Array.isArray(section.image) && section.image.map((image, imgIndex) => {
  958.  
  959.                                                 if (!image || !image.src) return null;
  960.  
  961.                                                 return (
  962.                                                     <div key={`image-${imgIndex}`} className="section-image-preview">
  963.                                                         <img
  964.                                                             src={image.src}
  965.                                                             alt={t('articles.createForm.sectionWithNumber', { number: index + 1 })}
  966.                                                             onClick={() => handleImageClick(image.src)}
  967.                                                             onError={(e) => {
  968.                                                                 console.error(`Грешка при зареждане на изображение: ${image.src}`);
  969.                                                                 e.target.src = '/default-image-placeholder.jpg';
  970.                                                             }}
  971.                                                         />
  972.                                                         <div className="img-alt-actions">
  973.                                                             <button
  974.                                                                 type="button"
  975.                                                                 className="img-alt-edit-btn"
  976.                                                                 onClick={() => openAltEditModal(index, imgIndex, image)}
  977.                                                                 title={t('articles.createForm.editInfo')}
  978.                                                             >
  979.                                                                 <FontAwesomeIcon icon={faEdit} />
  980.                                                             </button>
  981.                                                             <button
  982.                                                                 type="button"
  983.                                                                 className="remove-image-btn"
  984.                                                                 onClick={() => removeSectionImage(index, imgIndex)}
  985.                                                                 title={t('articles.createForm.removeImage')}
  986.                                                             >
  987.                                                                 <FontAwesomeIcon icon={faTimes} />
  988.                                                             </button>
  989.                                                         </div>
  990.  
  991.                                                         {/* ALT текст */}
  992.                                                         {image.alt && convertEditorToHtml(image.alt) && (
  993.                                                             <div className="img-alt-text-preview">
  994.                                                                 ALT: <span className="truncated-alt-text" dangerouslySetInnerHTML={{ __html: convertEditorToHtml(image.alt) }}></span>
  995.                                                             </div>
  996.                                                         )}
  997.  
  998.                                                         {/* Caption */}
  999.                                                         {image.caption && convertEditorToHtml(image.caption) && (
  1000.                                                             <div className="img-caption-preview">
  1001.                                                                 <span className="truncated-caption-text" dangerouslySetInnerHTML={{ __html: convertEditorToHtml(image.caption) }}></span>
  1002.                                                             </div>
  1003.                                                         )}
  1004.                                                     </div>
  1005.                                                 );
  1006.                                             })}
  1007.  
  1008.                                             {/* За обратна съвместимост - ако image не е масив, но има src */}
  1009.                                             {section.image && !Array.isArray(section.image) && section.image.src && (
  1010.                                                 <div className="section-image-preview">
  1011.                                                     <img
  1012.                                                         src={section.image.src}
  1013.                                                         alt={t('articles.createForm.sectionWithNumber', { number: index + 1 })}
  1014.                                                         onClick={() => handleImageClick(section.image.src)}
  1015.                                                     />
  1016.                                                     <button
  1017.                                                         type="button"
  1018.                                                         className="remove-image-btn"
  1019.                                                         onClick={() => removeSectionImage(index)}
  1020.                                                     >
  1021.                                                         <FontAwesomeIcon icon={faTimes} />
  1022.                                                     </button>
  1023.                                                 </div>
  1024.                                             )}
  1025.                                         </div>
  1026.                                     </div>
  1027.                                 </div>
  1028.                             </div>
  1029.                         ))}
  1030.                     </div>
  1031.                 </div>
  1032.  
  1033.                 {/* Тагове */}
  1034.                 <div className="form-section">
  1035.                     <h3>{t('articles.createForm.tags')}</h3>
  1036.                     <div className="form-section-content">
  1037.                         <div className="tags-container">
  1038.                             <div className="tags-input-group">
  1039.                                 <input
  1040.                                     type="text"
  1041.                                     id="newTag"
  1042.                                     value={newTag}
  1043.                                     onChange={(e) => setNewTag(e.target.value)}
  1044.                                     placeholder={t('articles.createForm.tagPlaceholder')}
  1045.                                     onKeyPress={(e) => e.key === 'Enter' && handleTagAdd(e)}
  1046.                                 />
  1047.                                 <button
  1048.                                     type="button"
  1049.                                     className="add-tag-btn"
  1050.                                     onClick={handleTagAdd}
  1051.                                 >
  1052.                                     <FontAwesomeIcon icon={faPlus} /> {t('articles.createForm.addBtn')}
  1053.                                 </button>
  1054.                             </div>
  1055.  
  1056.                             {errors.tags && <div className="error-message">{errors.tags}</div>}
  1057.  
  1058.                             <div className="tags-list">
  1059.                                 {values.tags.map((tag, index) => (
  1060.                                     <div key={index} className="tag-item">
  1061.                                         <span>{tag}</span>
  1062.                                         <button
  1063.                                             type="button"
  1064.                                             onClick={() => removeTag(index)}
  1065.                                             className="remove-tag-btn"
  1066.                                         >
  1067.                                             <FontAwesomeIcon icon={faTimes} />
  1068.                                         </button>
  1069.                                     </div>
  1070.                                 ))}
  1071.                             </div>
  1072.                         </div>
  1073.                     </div>
  1074.                 </div>
  1075.  
  1076.                 {/* Прогрес при качване */}
  1077.                 {isUploading && (
  1078.                     <div className="upload-progress">
  1079.                         <div className="progress-bar">
  1080.                             <div className="progress-fill" style={{ width: `${uploadProgress}%` }}></div>
  1081.                         </div>
  1082.                         <span>{uploadProgress.toFixed(0)}% {t('articles.createForm.uploaded')}</span>
  1083.                     </div>
  1084.                 )}
  1085.  
  1086.                 {/* Бутони на формата */}
  1087.                 <div className="form-actions">
  1088.                     <button
  1089.                         type="button"
  1090.                         className="preview-btn"
  1091.                         onClick={handlePreviewToggle}
  1092.                     >
  1093.                         <FontAwesomeIcon icon={faEye} />  {t('articles.createForm.previewBtn')}
  1094.                     </button>
  1095.  
  1096.                     <button
  1097.                         type="submit"
  1098.                         className="submit-btn"
  1099.                         disabled={isUploading}
  1100.                     >
  1101.                         <FontAwesomeIcon icon={faSave} /> {submitButtonText}
  1102.                     </button>
  1103.                 </div>
  1104.             </form>
  1105.  
  1106.             {/* Модален прозорец за преглед на изображение в пълен размер */}
  1107.             {expandedImageUrl && (
  1108.                 <div className="image-modal" onClick={closeExpandedImage}>
  1109.                     <div className="image-modal-content">
  1110.                         <img src={expandedImageUrl} alt={t('articles.createForm.expandedView')} />
  1111.                         <button className="close-modal-btn" onClick={closeExpandedImage}>
  1112.                             <FontAwesomeIcon icon={faTimes} />
  1113.                         </button>
  1114.                     </div>
  1115.                 </div>
  1116.             )}
  1117.             <ScrollToTop />
  1118.             {/* В края на компонента, преди последния затварящ таг */}
  1119.             <ImageAltEditModal
  1120.                 isOpen={isAltModalOpen}
  1121.                 onClose={() => setIsAltModalOpen(false)}
  1122.                 image={currentEditingImage.image}
  1123.                 onSave={handleSaveImageInfo}
  1124.             />
  1125.         </div>
  1126.     );
  1127. });
  1128.  
  1129. export default ArticleCreateForm;
  1130.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement