Advertisement
bruno83

DocumentForm.jsx with old useEffect logic in commented section for 79-17

Nov 15th, 2024
34
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. DocumentForm.jsx with old useEffect logic in commented section for 79-17:
  2.  
  3. import { CommentOutlined, DeleteOutlined, ExclamationCircleOutlined } from '@ant-design/icons';
  4. import { DragDropContext, Draggable, Droppable } from '@hello-pangea/dnd';
  5. import { App, Button, Checkbox, Collapse, Divider, Form, Input, Modal, Timeline, Tree } from 'antd';
  6. import Title from 'antd/es/typography/Title';
  7. import { isEmpty, isEqual, some } from 'lodash';
  8. import React, { useEffect, useMemo, useState } from 'react';
  9. import { useTranslation } from 'react-i18next';
  10. import ReactQuill from 'react-quill-new';
  11. import { useDispatch } from 'react-redux';
  12. import { useNavigate } from 'react-router-dom';
  13. import { useFileDrawer } from '../../context/FileDrawerContext';
  14. import { getDocumentTypes } from '../../store/group/groupActions';
  15. import { parseQuillText } from '../../utils';
  16. import { decodeDetails } from '../../utils/pdfUtils';
  17. import withLoadStatus from '../common/withLoadStatus';
  18.  
  19. const modules = {
  20.   clipboard: {
  21.     matchVisual: false,
  22.   },
  23.   toolbar: {
  24.     container: [
  25.       ['bold', 'italic', 'underline', 'strike'], // Bold, Italic, Underline, Strikethrough
  26.       [{ header: [1, 2, 3, 4, 5, 6, false] }], // Heading sizes
  27.       [{ align: [] }], // Alignment
  28.       [{ list: 'ordered' }, { list: 'bullet' }], // Ordered & unordered lists
  29.       [{ indent: '-1' }, { indent: '+1' }], // Indent & Outdent
  30.       ['clean'], // Clear formatting
  31.     ],
  32.   },
  33. };
  34.  
  35. const normalizeValue = (value) => {
  36.   return value === '<p><br></p>' ? '' : value;
  37. };
  38.  
  39. const DocumentForm = ({
  40.   startLoading,
  41.   stopLoading,
  42.   isLoading,
  43.   parentId,
  44.   contextId,
  45.   contextNotes,
  46.   getContextNotes,
  47.   document,
  48.   isEditing,
  49.   updateAction,
  50.   createAction,
  51.   hasChanges,
  52.   setHasChanges,
  53.   fetchDocuments,
  54. }) => {
  55.   const { t } = useTranslation();
  56.   const { modal } = App.useApp();
  57.  
  58.   const [searchTerm, setSearchTerm] = useState('');
  59.  
  60.   const [selectedDocument, setSelectedDocument] = useState(document || { noteRefs: [] });
  61.   const [selectedNotes, setSelectedNotes] = useState([]);
  62.   const [selectedFiles, setSelectedFiles] = useState({});
  63.   const dispatch = useDispatch();
  64.   const navigate = useNavigate();
  65.   const [form] = Form.useForm();
  66.  
  67.   const [initialFieldValues, setInitialFieldValues] = useState(null);
  68.   const [importedNotes, setImportedNotes] = useState([]);
  69.   const [header, setHeader] = useState(selectedDocument?.header || '');
  70.   const [footer, setFooter] = useState(selectedDocument?.footer || '');
  71.   const { openDrawer } = useFileDrawer();
  72.   const [activeKey, setActiveKey] = useState(null);
  73.  
  74.   useEffect(() => {
  75.     console.log('Current activeKey:', activeKey);
  76.   }, [activeKey]);
  77.  
  78.   const handleFormChange = () => {
  79.     if (initialFieldValues) {
  80.       const currentFieldValues = form.getFieldsValue();
  81.  
  82.       // Normalize values for header and footer
  83.       const normalizedCurrentValues = {
  84.         ...currentFieldValues,
  85.         header: normalizeValue(currentFieldValues.header),
  86.         footer: normalizeValue(currentFieldValues.footer),
  87.       };
  88.  
  89.       const normalizedInitialValues = {
  90.         ...initialFieldValues,
  91.         header: normalizeValue(initialFieldValues.header),
  92.         footer: normalizeValue(initialFieldValues.footer),
  93.       };
  94.  
  95.       const hasDifferences = some(
  96.         normalizedCurrentValues,
  97.         (value, key) => value !== normalizedInitialValues[key],
  98.       );
  99.  
  100.       setHasChanges(hasDifferences);
  101.     }
  102.   };
  103.  
  104.   useEffect(() => {
  105.     const hasDifferences = !isEqual(selectedDocument.noteRefs, document?.noteRefs || []);
  106.  
  107.     setHasChanges(hasDifferences);
  108.   }, [selectedDocument.noteRefs, document?.noteRefs]);
  109.  
  110.   const handleCancel = () => {
  111.     form.resetFields();
  112.     setHasChanges(false);
  113.  
  114.     if (isEditing) {
  115.       if (document.noteRefs && document.details) {
  116.         const decodedDetails = decodeDetails(document.details);
  117.         const noteRefsMap = new Map(document.noteRefs.map((note) => [note.id, note]));
  118.  
  119.         const detailedNotes = decodedDetails.map(({ id, note, files, note_activities }) => {
  120.           const noteRef = noteRefsMap.get(id) || {};
  121.  
  122.           return {
  123.             ...noteRef,
  124.             id,
  125.             note: parseQuillText(note || ''),
  126.             files: files || [],
  127.             subject: noteRef.subject || '',
  128.             note_activities: note_activities || [],
  129.           };
  130.         });
  131.  
  132.         setImportedNotes(detailedNotes);
  133.       }
  134.     } else {
  135.       setImportedNotes([]);
  136.     }
  137.   };
  138.  
  139.   /*useEffect(() => {
  140.     const headerKeys = document?.noteRefs?.map((ref) => `header-${ref.id}`) || [];
  141.  
  142.     const headerValues = headerKeys.reduce((acc, key) => {
  143.       const headerId = key.split('header-')[1];
  144.       const matchingNoteRef = document.noteRefs.find((ref) => ref.id === headerId);
  145.       const noteValue = matchingNoteRef?.note || form.getFieldValue(key) || '';
  146.       acc[key] = noteValue;
  147.  
  148.       return acc;
  149.     }, {});
  150.  
  151.     form.setFieldsValue({
  152.       name: document?.name || form.getFieldValue('name') || '',
  153.       header: document?.header || form.getFieldValue('header') || '',
  154.       footer: document?.footer || form.getFieldValue('footer') || '',
  155.       ...headerValues,
  156.     });
  157.  
  158.     setInitialFieldValues({
  159.       name: document?.name || form.getFieldValue('name') || '',
  160.       header: document?.header || form.getFieldValue('header') || '',
  161.       footer: document?.footer || form.getFieldValue('footer') || '',
  162.       ...headerValues,
  163.     });
  164.   }, [form, document?.name, document?.footer, document?.header, document?.noteRefs]);*/
  165.  
  166.   useEffect(() => {
  167.     const headerKeys = selectedDocument?.noteRefs?.map((ref) => `header-${ref.id}`) || [];
  168.  
  169.     const headerValues = headerKeys.reduce((acc, key) => {
  170.       const headerId = key.split('header-')[1];
  171.       const matchingNoteRef = selectedDocument?.noteRefs?.find((ref) => ref.id === headerId) || {};
  172.       const noteValue = matchingNoteRef?.note || form.getFieldValue(key) || '';
  173.       acc[key] = noteValue;
  174.       return acc;
  175.     }, {});
  176.  
  177.     const initialValues = {
  178.       name: selectedDocument?.name || '',
  179.       header: selectedDocument?.header || '',
  180.       footer: selectedDocument?.footer || '',
  181.       ...headerValues,
  182.     };
  183.  
  184.     form.setFieldsValue(initialValues);
  185.     setInitialFieldValues(initialValues);
  186.   }, [form, selectedDocument, setInitialFieldValues]);
  187.  
  188.   useEffect(() => {
  189.     if (!isEqual(importedNotes, selectedDocument.noteRefs)) {
  190.       if (selectedDocument.noteRefs && selectedDocument.details) {
  191.         const decodedDetails = decodeDetails(selectedDocument.details);
  192.         const noteRefsMap = new Map(selectedDocument.noteRefs.map((note) => [note.id, note]));
  193.  
  194.         const detailedNotes = decodedDetails.map(({ id, note, files, note_activities }) => {
  195.           const noteRef = noteRefsMap.get(id) || {};
  196.  
  197.           return {
  198.             ...noteRef,
  199.             id,
  200.             note: note || '',
  201.             files: files || [],
  202.             subject: noteRef.subject || '',
  203.             note_activities: note_activities || [],
  204.           };
  205.         });
  206.  
  207.         setImportedNotes(detailedNotes); // Samo ako ima razlike
  208.       }
  209.     }
  210.   }, [selectedDocument, importedNotes]);
  211.  
  212.   const handleQuillChange = (key, content) => {
  213.     if (key === 'header') {
  214.       setHeader(content);
  215.     } else if (key === 'footer') {
  216.       setFooter(content);
  217.     } else {
  218.       // Update the notes state for specific note ID
  219.       setImportedNotes((prevNotes) =>
  220.         prevNotes.map((note) => (note.id === key ? { ...note, note: content } : note)),
  221.       );
  222.     }
  223.   };
  224.  
  225.   const [isModalOpen, setIsModalOpen] = useState(false);
  226.  
  227.   const showModal = async () => {
  228.     setSearchTerm('');
  229.     await dispatch(getContextNotes(parentId));
  230.     setIsModalOpen(true);
  231.   };
  232.  
  233.   const handleOk = () => {
  234.     // Create an array to hold the note references with selected files
  235.     const newNoteRefs = contextNotes
  236.       .filter((note) => selectedNotes.includes(note.id)) // Filter notes based on selected notes
  237.       .map((note) => ({
  238.         ...note, // Spread the whole note object
  239.         files: note.files.filter((file) => selectedFiles[note.id]?.includes(file.id)), // Filter files based on selected file IDs
  240.       }));
  241.  
  242.     const existingNotes = decodeDetails(selectedDocument.details) || [];
  243.  
  244.     // Combine existing notes with new notes, ensuring uniqueness based on id
  245.     const combinedNotes = [
  246.       ...existingNotes,
  247.       ...importedNotes.map((note) => ({
  248.         //za orderanje u docs edit
  249.         id: note.id,
  250.         note: note.note || '',
  251.         files: note.files,
  252.         note_activities: note.note_activities,
  253.       })),
  254.     ];
  255.  
  256.     // Create the new details string from the combined notes
  257.     const updatedDetails = !isEmpty(combinedNotes)
  258.       ? `<ul>${combinedNotes
  259.           .map(
  260.             (note) =>
  261.               `<li data-note-id="${note.id}" data-note-text="${note.note}">${note.note}
  262.               ${
  263.                 !isEmpty(note.files) &&
  264.                 `<ul>${note.files
  265.                   .map((file) => {
  266.                     return `<li data-file-name="${file.name}" data-file-description="${
  267.                      file.description || ''
  268.                    }" data-file-url="${file.url}" data-file-mime_type="${file.mime_type}">${
  269.                       file.name
  270.                     }</li>`;
  271.                   })
  272.                   .join('')}</ul>`
  273.               }
  274.               ${
  275.                 !isEmpty(note.note_activities) &&
  276.                 `<ul>${note.note_activities
  277.                   .map((activity) => {
  278.                     return `<li data-comment-id="${activity.id}" data-comment-subject="${activity.subject}" data-comment-created_by="${activity.created_by}" data-comment-completed_at="${activity.completed_at}">${activity.comment}</li>`;
  279.                   })
  280.                   .join('')}</ul>`
  281.               }
  282.           </li>`,
  283.           )
  284.           .join('')}</ul>`
  285.       : '';
  286.  
  287.     // Set the selectedDocument with the noteRefs
  288.     setSelectedDocument((prevDocument) => ({
  289.       ...prevDocument,
  290.       noteRefs: [...newNoteRefs, ...importedNotes.map((note) => ({ id: note.id, ...note }))],
  291.  
  292.       details: updatedDetails,
  293.     }));
  294.  
  295.     setIsModalOpen(false);
  296.     setSelectedNotes([]);
  297.     setSelectedFiles({});
  298.   };
  299.  
  300.   const handleModalCancel = () => {
  301.     setIsModalOpen(false);
  302.     setSelectedNotes([]);
  303.     setSelectedFiles({});
  304.   };
  305.  
  306.   const handleCollapseChange = (key) => {
  307.     console.log('Toggling activeKey:', key);
  308.     setActiveKey(key);
  309.   };
  310.  
  311.   const handleNoteCheckboxChange = (noteId) => {
  312.     const isNoteSelected = selectedNotes.includes(noteId);
  313.     const updatedSelectedNotes = isNoteSelected
  314.       ? selectedNotes.filter((id) => id !== noteId) // Uncheck if already selected
  315.       : [...selectedNotes, noteId]; // Check if not selected
  316.  
  317.     // Update the selected files based on note selection
  318.     const updatedSelectedFiles = { ...selectedFiles };
  319.  
  320.     if (isNoteSelected) {
  321.       // If note is being unchecked, remove all files
  322.       delete updatedSelectedFiles[noteId];
  323.     } else {
  324.       // If note is being checked, select all files
  325.       const filesToSelect = contextNotes
  326.         .find((note) => note.id === noteId)
  327.         ?.files.map((file) => file.id); // Use file.id
  328.       if (filesToSelect) {
  329.         updatedSelectedFiles[noteId] = filesToSelect; // Add all file IDs
  330.       }
  331.     }
  332.  
  333.     // Update selected files to ensure files are selected based on their notes
  334.     selectedNotes.forEach((selectedNoteId) => {
  335.       // Initialize array if it doesn't exist
  336.       if (!updatedSelectedFiles[selectedNoteId]) {
  337.         updatedSelectedFiles[selectedNoteId] = [];
  338.       }
  339.     });
  340.  
  341.     setSelectedNotes(updatedSelectedNotes);
  342.     setSelectedFiles(updatedSelectedFiles);
  343.   };
  344.  
  345.   const handleFileCheckboxChange = (noteId, fileId) => {
  346.     // Change fileName to fileId
  347.     const updatedSelectedFiles = {
  348.       ...selectedFiles,
  349.       [noteId]: selectedFiles[noteId]
  350.         ? selectedFiles[noteId].includes(fileId) // Change to fileId
  351.           ? selectedFiles[noteId].filter((id) => id !== fileId) // Change to fileId
  352.           : [...(selectedFiles[noteId] || []), fileId] // Change to fileId
  353.         : [fileId], // Change to fileId
  354.     };
  355.  
  356.     // Update the selected notes if files are being checked and the note is not already selected
  357.     const updatedSelectedNotes = Object.keys(updatedSelectedFiles).reduce((acc, id) => {
  358.       if (updatedSelectedFiles[id].length > 0 && !acc.includes(id)) {
  359.         acc.push(id); // Add noteId if at least one file is selected
  360.       }
  361.       return acc;
  362.     }, selectedNotes);
  363.  
  364.     setSelectedFiles(updatedSelectedFiles);
  365.     setSelectedNotes(updatedSelectedNotes);
  366.   };
  367.  
  368.   const items = useMemo(() => {
  369.     return contextNotes.map((note) => {
  370.       const totalFiles = note.files.length;
  371.       const selectedFileCount = selectedFiles[note.id]?.length || 0;
  372.       const isIntermediate = selectedFileCount > 0 && selectedFileCount < totalFiles;
  373.  
  374.       return {
  375.         key: note.id,
  376.         label: (
  377.           <div style={{ display: 'flex', alignItems: 'center' }}>
  378.             <Checkbox
  379.               checked={selectedNotes.includes(note.id)}
  380.               indeterminate={isIntermediate}
  381.               onChange={() => handleNoteCheckboxChange(note.id)}
  382.               onClick={(e) => e.stopPropagation()}
  383.             />
  384.             <Title level={4} style={{ margin: '0 0 0 8px' }}>
  385.               {note.subject}
  386.             </Title>
  387.           </div>
  388.         ),
  389.         children: (
  390.           <div style={{ paddingLeft: '20px' }}>
  391.             <div dangerouslySetInnerHTML={{ __html: parseQuillText(note.note) }} />
  392.             <div>
  393.               {note.files.map((file) => (
  394.                 <Checkbox
  395.                   key={file.id}
  396.                   checked={selectedFiles[note.id]?.includes(file.id)}
  397.                   onChange={() => handleFileCheckboxChange(note.id, file.id)}
  398.                 >
  399.                   {file.name}
  400.                 </Checkbox>
  401.               ))}
  402.             </div>
  403.           </div>
  404.         ),
  405.       };
  406.     });
  407.   }, [contextNotes, selectedNotes, selectedFiles]);
  408.  
  409.   const filteredItems = useMemo(() => {
  410.     return items.filter((item) =>
  411.       item.label.props.children[1].props.children.toLowerCase().includes(searchTerm.toLowerCase()),
  412.     );
  413.   }, [items, searchTerm]);
  414.  
  415.   const treeData = useMemo(() => {
  416.     // Ensure selectedDocument is not null or undefined before accessing noteRefs
  417.     if (!selectedDocument || !selectedDocument.noteRefs) return [];
  418.  
  419.     // Map through all noteRefs and create a unique tree structure for each note's files
  420.     return selectedDocument.noteRefs.map((note) => ({
  421.       noteId: note.id,
  422.       files: note.files.map((file) => ({
  423.         title: file.name,
  424.         key: file.id,
  425.         ...file,
  426.       })),
  427.     }));
  428.   }, [selectedDocument]);
  429.  
  430.   const handleFileClick = (fileId, noteId) => {
  431.     if (Array.isArray(fileId) && fileId.length === 1) {
  432.       const fileToDownload = selectedDocument.noteRefs
  433.         .flatMap((note) => note.files)
  434.         .find((file) => file.id === fileId[0]);
  435.  
  436.       if (fileToDownload) {
  437.         openDrawer({
  438.           file: { ...fileToDownload },
  439.           parentId: noteId,
  440.         });
  441.       }
  442.     }
  443.   };
  444.  
  445.   const handleSubmit = async (values) => {
  446.     startLoading();
  447.     const response = await dispatch(getDocumentTypes()).unwrap();
  448.  
  449.     const details = !isEmpty(importedNotes)
  450.       ? `<ul>${importedNotes
  451.           .map(
  452.             (note) => `<li data-note-id="${note.id}" data-note-text="${note.note}">${note.note}
  453.           ${
  454.             !isEmpty(note.files) &&
  455.             `<ul>${note.files
  456.               .map((file) => {
  457.                 return `<li data-file-name="${file.name}" data-file-description="${
  458.                  file.description || ''
  459.                }" data-file-url="${file.url}" data-file-mime_type="${file.mime_type}">${
  460.                   file.name
  461.                 }</li>`;
  462.               })
  463.               .join('')}</ul>`
  464.           }
  465.           ${
  466.             !isEmpty(note.note_activities) &&
  467.             `<ul>${note.note_activities
  468.               .map(
  469.                 (activity) =>
  470.                   `<li data-comment-id="${activity.id}" data-comment-subject="${activity.subject}" data-comment-created_by="${activity.created_by}" data-comment-completed_at="${activity.completed_at}">${activity.comment}</li>`,
  471.               )
  472.               .join('')}</ul>`
  473.           }
  474.         </li>`,
  475.           )
  476.           .join('')}</ul>`
  477.       : '';
  478.  
  479.     /*const updatedNoteRefs = importedNotes.map((note) => ({
  480.       ...note,
  481.       note: note.note || '',
  482.     }));*/
  483.  
  484.     const { name } = values;
  485.  
  486.     const document = {
  487.       name,
  488.       header,
  489.       footer,
  490.       details,
  491.       type: response,
  492.       //noteRefs: updatedNoteRefs,
  493.       noteRefs: importedNotes.map((note) => ({ id: note.id, ...note })), //za orderanje u docs edit
  494.       link: 'link',
  495.       version: selectedDocument.version || 1,
  496.     };
  497.  
  498.     if (isEditing) {
  499.       await dispatch(
  500.         updateAction({
  501.           parentId,
  502.           contextId,
  503.           document,
  504.           successMessage: t('notifications.company.notes.update'),
  505.         }),
  506.       ).unwrap();
  507.     } else {
  508.       await dispatch(
  509.         createAction({
  510.           contextId,
  511.           document,
  512.           successMessage: t('notifications.company.notes.create'),
  513.         }),
  514.       ).unwrap();
  515.     }
  516.  
  517.     await dispatch(fetchDocuments(contextId)).unwrap();
  518.     stopLoading();
  519.     setHasChanges(false);
  520.     if (!isEditing) navigate(-1);
  521.   };
  522.  
  523.   const showCommentDeleteConfirm = (noteId) => {
  524.     modal.confirm({
  525.       title: t('document.form.deleteModalTitle'),
  526.       icon: <ExclamationCircleOutlined />,
  527.       content: t('document.form.deleteModalContent'),
  528.       okText: t('button.delete'),
  529.       okType: 'danger',
  530.       cancelText: t('button.cancel'),
  531.       onOk() {
  532.         handleDeleteNoteComment(noteId);
  533.       },
  534.     });
  535.   };
  536.  
  537.   const handleDeleteNoteComment = (noteId) => {
  538.     const existingNotes = decodeDetails(selectedDocument.details) || [];
  539.  
  540.     /*const updatedNoteRefs = selectedDocument.noteRefs.map((note) => {
  541.       if (note.id === noteId) {
  542.         return {
  543.           ...note,
  544.           note_activities: [],
  545.         };
  546.       }
  547.       return note;
  548.     });*/
  549.  
  550.     const updatedExistingNotes = existingNotes.map((note) => {
  551.       if (note.id === noteId) {
  552.         return {
  553.           ...note,
  554.           note_activities: [],
  555.         };
  556.       }
  557.       return note;
  558.     });
  559.  
  560.     const updatedDetails = `<ul>${updatedExistingNotes
  561.       .map(
  562.         (note) => `<li data-note-id="${note.id}" data-note-text="${note.note}">${note.note}
  563.         ${
  564.           !isEmpty(note.files) &&
  565.           `<ul>${note.files
  566.             .map((file) => {
  567.               return `<li data-file-name="${file.name}" data-file-description="${
  568.                file.description || ''
  569.              }" data-file-url="${file.url}" data-file-mime_type="${file.mime_type}">${
  570.                 file.name
  571.               }</li>`;
  572.             })
  573.             .join('')}</ul>`
  574.         }
  575.         ${
  576.           !isEmpty(note.note_activities) &&
  577.           `<ul>${note.note_activities
  578.             .map((activity) => {
  579.               return `<li data-comment-id="${activity.id}" data-comment-subject="${activity.subject}" data-comment-created_by="${activity.created_by}" data-comment-completed_at="${activity.completed_at}">${activity.comment}</li>`;
  580.             })
  581.             .join('')}</ul>`
  582.         }
  583.       </li>`,
  584.       )
  585.       .join('')}</ul>`;
  586.  
  587.     setSelectedDocument({
  588.       ...selectedDocument,
  589.       noteRefs: selectedDocument.noteRefs.map((note) =>
  590.         note.id === noteId ? { ...note, note_activities: [] } : note,
  591.       ),
  592.       details: updatedDetails,
  593.     });
  594.   };
  595.  
  596.   const showDeleteConfirm = (noteId) => {
  597.     modal.confirm({
  598.       title: t('document.form.deleteModalTitle'),
  599.       icon: <ExclamationCircleOutlined />,
  600.       content: t('document.form.deleteModalContent'),
  601.       okText: t('button.delete'),
  602.       okType: 'danger',
  603.       cancelText: t('button.cancel'),
  604.       onOk() {
  605.         handleDeleteNote(noteId);
  606.       },
  607.     });
  608.   };
  609.  
  610.   const handleDeleteNote = (noteId) => {
  611.     const existingNotes = decodeDetails(selectedDocument.details) || [];
  612.  
  613.     //const updatedNoteRefs = selectedDocument.noteRefs.filter((note) => note.id !== noteId);
  614.  
  615.     const updatedExistingNotes = existingNotes.filter((note) => note.id !== noteId);
  616.  
  617.     const updatedDetails = `<ul>${updatedExistingNotes
  618.       .map(
  619.         (note) => `<li data-note-id="${note.id}" data-note-text="${note.note}">${note.note}
  620.         ${
  621.           !isEmpty(note.files) &&
  622.           `<ul>${note.files
  623.             .map((file) => {
  624.               return `<li data-file-name="${file.name}" data-file-description="${
  625.                file.description || ''
  626.              }" data-file-url="${file.url}" data-file-mime_type="${file.mime_type}">${
  627.                 file.name
  628.               }</li>`;
  629.             })
  630.             .join('')}</ul>`
  631.         }
  632.         ${
  633.           !isEmpty(note.note_activities) &&
  634.           `<ul>${note.note_activities
  635.             .map((activity) => {
  636.               return `<li data-comment-id="${activity.id}" data-comment-subject="${activity.subject}" data-comment-created_by="${activity.created_by}" data-comment-completed_at="${activity.completed_at}">${activity.comment}</li>`;
  637.             })
  638.             .join('')}</ul>`
  639.         }
  640.       </li>`,
  641.       )
  642.       .join('')}</ul>`;
  643.  
  644.     setSelectedDocument({
  645.       ...selectedDocument,
  646.       noteRefs: selectedDocument.noteRefs.filter((note) => note.id !== noteId),
  647.       details: updatedDetails,
  648.     });
  649.   };
  650.  
  651.   const formatedActivities = (note) => {
  652.     return note.note_activities
  653.       ?.slice()
  654.       .sort((a, b) => new Date(b.completed_at) - new Date(a.completed_at))
  655.       .map((activity) => ({
  656.         dot: <CommentOutlined />,
  657.         children: (
  658.           <div>
  659.             <div>{activity.comment}</div>
  660.             <div className='mt-3 text-xs'>
  661.               {new Date(activity.completed_at).toLocaleString()} - {activity.created_by}
  662.             </div>
  663.           </div>
  664.         ),
  665.       }));
  666.   };
  667.  
  668.   return (
  669.     <Form
  670.       name='document'
  671.       form={form}
  672.       labelCol={{
  673.         xs: { span: 24 },
  674.         md: { span: 6 },
  675.       }}
  676.       wrapperCol={{
  677.         xs: { span: 24 },
  678.         md: { span: 18 },
  679.       }}
  680.       labelAlign='left'
  681.       layout='horizontal'
  682.       initialValues={{
  683.         remember: true,
  684.       }}
  685.       onValuesChange={handleFormChange}
  686.       onFinish={handleSubmit}
  687.       autoComplete='on'
  688.       className='max-w-[60rem]'
  689.     >
  690.       <Modal
  691.         title={t('document.form.modalTitle')}
  692.         open={isModalOpen}
  693.         onOk={handleOk}
  694.         okText='Import'
  695.         onCancel={handleModalCancel}
  696.         width='50vw'
  697.         bodyStyle={{
  698.           maxHeight: '70vh',
  699.           overflowY: 'auto',
  700.         }}
  701.       >
  702.         <Input
  703.           placeholder={t('document.form.searchNotes')}
  704.           value={searchTerm}
  705.           onChange={(e) => setSearchTerm(e.target.value)}
  706.           allowClear
  707.           style={{ marginBottom: '1rem' }}
  708.         />
  709.         <Collapse
  710.           accordion
  711.           activeKey={activeKey}
  712.           onChange={handleCollapseChange}
  713.           expandIconPosition='end'
  714.         >
  715.           {filteredItems.map((item) => (
  716.             <Collapse.Panel
  717.               header={item.label}
  718.               key={item.key}
  719.               style={{
  720.                 maxHeight: '300px',
  721.                 overflowY: 'auto',
  722.               }}
  723.             >
  724.               {item.children}
  725.             </Collapse.Panel>
  726.           ))}
  727.         </Collapse>
  728.       </Modal>
  729.  
  730.       <Title level={2}>{t('document.form.headerInfo')}</Title>
  731.       <Form.Item
  732.         label={t('document.form.documentInputLabel')}
  733.         tooltip={t('document.form.documentInputTooltip')}
  734.         name='name'
  735.         className='custom-form-item'
  736.         validateTrigger='onBlur'
  737.         initialValue={document?.name || ''}
  738.         rules={[
  739.           {
  740.             required: true,
  741.             message: 'Please input the document name!',
  742.           },
  743.         ]}
  744.       >
  745.         <Input />
  746.       </Form.Item>
  747.       <Form.Item
  748.         label={t('document.form.headerInputLabel')}
  749.         tooltip={t('document.form.headerInputTooltip')}
  750.         name='header'
  751.         className='custom-form-item'
  752.         validateTrigger='onBlur'
  753.         initialValue={document?.header || ''}
  754.       >
  755.         <ReactQuill
  756.           theme='snow'
  757.           placeholder={'Your header text goes here...'}
  758.           value={header}
  759.           onChange={(content) => handleQuillChange('header', content)}
  760.           modules={modules}
  761.         />
  762.       </Form.Item>
  763.       <Divider />
  764.  
  765.       <div className='flex justify-between'>
  766.         <Title level={2} className='inline-block w-full'>
  767.           {t('document.form.details')}
  768.         </Title>
  769.         <div className='my-6 flex w-full justify-end'>
  770.           <Button type='primary' onClick={showModal}>
  771.             {t('button.importNotes')}
  772.           </Button>
  773.         </div>
  774.       </div>
  775.  
  776.       <DragDropContext
  777.         onDragEnd={(result) => {
  778.           if (!result.destination) return;
  779.  
  780.           const reorderedNotes = Array.from(importedNotes);
  781.           const [removed] = reorderedNotes.splice(result.source.index, 1);
  782.           reorderedNotes.splice(result.destination.index, 0, removed);
  783.  
  784.           // Ažuriraj importedNotes
  785.           setImportedNotes(reorderedNotes);
  786.  
  787.           // Ažuriraj vrijednosti forme
  788.           const updatedFields = reorderedNotes.reduce((acc, note) => {
  789.             acc[`header-${note.id}`] = note.note || '';
  790.             return acc;
  791.           }, {});
  792.           form.setFieldsValue(updatedFields);
  793.  
  794.           // Ažuriraj selectedDocument sa novim redoslijedom
  795.           setSelectedDocument((prevDocument) => ({
  796.             ...prevDocument,
  797.             noteRefs: reorderedNotes.map((note) => ({ id: note.id, ...note })),
  798.           }));
  799.         }}
  800.       >
  801.         <Droppable droppableId='notesList'>
  802.           {(provided) => (
  803.             <div {...provided.droppableProps} ref={provided.innerRef}>
  804.               {importedNotes.map((note, index) => (
  805.                 <Draggable key={note.id} draggableId={note.id} index={index}>
  806.                   {(provided) => (
  807.                     <div
  808.                       ref={provided.innerRef}
  809.                       {...provided.draggableProps}
  810.                       {...provided.dragHandleProps}
  811.                       style={{
  812.                         ...provided.draggableProps.style,
  813.                         marginBottom: '1rem',
  814.                         padding: '10px',
  815.                         background: '#f0f0f0',
  816.                         borderRadius: '5px',
  817.                       }}
  818.                     >
  819.                       {/* Gumb za brisanje */}
  820.                       <div className='mb-2 flex items-center justify-end'>
  821.                         <Button
  822.                           type='text'
  823.                           danger
  824.                           onClick={() => showDeleteConfirm(note.id)}
  825.                           icon={<DeleteOutlined />}
  826.                         >
  827.                           {t('button.delete')}
  828.                         </Button>
  829.                       </div>
  830.  
  831.                       {/* ReactQuill za unos teksta */}
  832.                       <Form.Item
  833.                         label={note.subject}
  834.                         tooltip={t('document.form.headerInputTooltip')}
  835.                         name={`header-${note.id}`}
  836.                         className='custom-form-item'
  837.                         validateTrigger='onBlur'
  838.                         initialValue={note.note || ''}
  839.                       >
  840.                         <ReactQuill
  841.                           theme='snow'
  842.                           placeholder='Your text goes here...'
  843.                           value={note.note || ''}
  844.                           onChange={(content) => handleQuillChange(note.id, content)}
  845.                           modules={modules}
  846.                         />
  847.                       </Form.Item>
  848.  
  849.                       {/* Prikazivanje datoteka */}
  850.                       {!isEmpty(treeData.find((data) => data.noteId === note.id)?.files || []) && (
  851.                         <Form.Item label={t('document.form.attachedFilesInputLabel')} key={note.id}>
  852.                           <Tree
  853.                             className='-ml-6'
  854.                             treeData={treeData.find((data) => data.noteId === note.id)?.files || []}
  855.                             height={300}
  856.                             onSelect={(fileId) => handleFileClick(fileId, note.id)}
  857.                           />
  858.                         </Form.Item>
  859.                       )}
  860.  
  861.                       {/* Prikazivanje komentara */}
  862.                       {!isEmpty(formatedActivities(note)) && (
  863.                         <>
  864.                           <div className='mb-2 flex items-center justify-end'>
  865.                             <Button
  866.                               type='text'
  867.                               danger
  868.                               onClick={() => showCommentDeleteConfirm(note.id)}
  869.                               icon={<DeleteOutlined />}
  870.                             >
  871.                               {t('button.delete')}
  872.                             </Button>
  873.                           </div>
  874.                           <Form.Item label='Comments' key={note.id}>
  875.                             <Timeline
  876.                               className='max-h-[15rem] overflow-y-auto px-5 pt-1'
  877.                               items={formatedActivities(note)}
  878.                             />
  879.                           </Form.Item>
  880.                         </>
  881.                       )}
  882.                     </div>
  883.                   )}
  884.                 </Draggable>
  885.               ))}
  886.               {provided.placeholder}
  887.             </div>
  888.           )}
  889.         </Droppable>
  890.       </DragDropContext>
  891.  
  892.       <Divider />
  893.  
  894.       <Title level={2}>{t('document.form.footerInfo')}</Title>
  895.       <Form.Item
  896.         label={t('document.form.footerInputLabel')}
  897.         tooltip={t('document.form.footerInputTooltip')}
  898.         name='footer'
  899.         className='custom-form-item'
  900.         validateTrigger='onBlur'
  901.         initialValue={document?.footer || ''}
  902.       >
  903.         <ReactQuill
  904.           theme='snow'
  905.           placeholder='Your footer text goes here...'
  906.           value={footer}
  907.           onChange={(content) => handleQuillChange('footer', content)}
  908.           modules={modules}
  909.         />
  910.       </Form.Item>
  911.  
  912.       <Form.Item
  913.         wrapperCol={{
  914.           xs: { offset: 0, span: 24 },
  915.           md: { offset: 6, span: 18 },
  916.         }}
  917.       >
  918.         <Button
  919.           type='primary'
  920.           htmlType='submit'
  921.           className='w-full md:w-auto'
  922.           loading={isLoading}
  923.           disabled={!hasChanges}
  924.         >
  925.           {isEditing ? t('button.update') : t('button.create')}
  926.         </Button>
  927.         <Button htmlType='button' className='mt-2 w-full md:ml-3 md:w-auto' onClick={handleCancel}>
  928.           {t('button.cancel')}
  929.         </Button>
  930.       </Form.Item>
  931.     </Form>
  932.   );
  933. };
  934.  
  935. export default withLoadStatus(DocumentForm);
  936.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement