diff --git a/src/components/Accordion/CalendarAccordion/CalendarAccordion.jsx b/src/components/Accordion/CalendarAccordion/CalendarAccordion.jsx index 56621fd1c..0a7b20511 100644 --- a/src/components/Accordion/CalendarAccordion/CalendarAccordion.jsx +++ b/src/components/Accordion/CalendarAccordion/CalendarAccordion.jsx @@ -479,6 +479,7 @@ function CalendarAccordion(props) { } name={people?.name} description={people?.description} + calendarContentLanguage={calendarContentLanguage} bordered closable={readOnly ? false : true} itemWidth="100%" diff --git a/src/components/BilingualInput/BilingualInput.jsx b/src/components/BilingualInput/BilingualInput.jsx deleted file mode 100644 index 055107c2f..000000000 --- a/src/components/BilingualInput/BilingualInput.jsx +++ /dev/null @@ -1,98 +0,0 @@ -import React from 'react'; -import { Tabs } from 'antd'; -import { WarningOutlined } from '@ant-design/icons'; -import './bilingualInput.css'; -import { useTranslation } from 'react-i18next'; -import LiteralBadge from '../Badge/LiteralBadge'; - -function BilingualInput(props) { - const { t } = useTranslation(); - let labelFr = t('common.tabFrench'); - let labelEn = t('common.tabEnglish'); - - let defaultTab = props?.defaultTab ?? 'fr'; - - // Adjust tabs unless brand new entity - if (props?.fieldData) { - let enContent = props?.fieldData.en; - let frContent = props?.fieldData.fr; - - // Change default tab to 'en' if only english - if (enContent && !frContent) { - defaultTab = 'en'; - } - - // If field is empty add a warning - if (!frContent || frContent === '') { - labelFr = ( - <> - {labelFr}  - - - ); - } - if (!enContent || enContent === '') { - labelEn = ( - <> - {labelEn}  - - - ); - } - } - - const promptTextFr = - props?.fallbackStatus?.fr?.fallbackLiteralKey === '?' - ? t('common.forms.languageLiterals.unKnownLanguagePromptText') - : t('common.forms.languageLiterals.knownLanguagePromptText'); - const promptTextEn = - props?.fallbackStatus?.en?.fallbackLiteralKey === '?' - ? t('common.forms.languageLiterals.unKnownLanguagePromptText') - : t('common.forms.languageLiterals.knownLanguagePromptText'); - - const items = [ - { - label: {labelFr}, - key: 'fr', - forceRender: true, - children: ( -
- {props.children[0]} - {props?.fallbackStatus?.fr?.tagDisplayStatus && ( - - )} -
- ), - }, - { - label: {labelEn}, - key: 'en', - forceRender: true, - children: ( -
- {props.children[1]} - {props?.fallbackStatus?.en?.tagDisplayStatus && ( - - )} -
- ), - }, - ]; - - return ( - - ); -} - -export default BilingualInput; diff --git a/src/components/BilingualInput/index.js b/src/components/BilingualInput/index.js deleted file mode 100644 index 34994473a..000000000 --- a/src/components/BilingualInput/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from './BilingualInput'; diff --git a/src/components/BilingualTextEditor/BilingualTextEditor.jsx b/src/components/BilingualTextEditor/BilingualTextEditor.jsx deleted file mode 100644 index f788b5b1c..000000000 --- a/src/components/BilingualTextEditor/BilingualTextEditor.jsx +++ /dev/null @@ -1,157 +0,0 @@ -import React, { useRef, useState } from 'react'; -import { contentLanguage } from '../../constants/contentLanguage'; -import { useTranslation } from 'react-i18next'; -import ContentLanguageInput from '../ContentLanguageInput'; -import BilingualInput from '../BilingualInput'; -import TextEditor from '../TextEditor'; - -function BilingualTextEditor(props) { - const { name, data, placeholder, calendarContentLanguage, required } = props; - const { t } = useTranslation(); - const reactQuillRefFr = useRef(null); - const reactQuillRefEn = useRef(null); - const [descriptionMinimumWordCount] = useState(props?.descriptionMinimumWordCount ?? 1); - - return ( - - - ({ - validator() { - if ( - reactQuillRefFr?.current?.unprivilegedEditor?.getLength() > 1 || - reactQuillRefEn?.current?.unprivilegedEditor?.getLength() > 1 - ) { - return Promise.resolve(); - } else - return Promise.reject( - new Error( - calendarContentLanguage === contentLanguage.ENGLISH || - calendarContentLanguage === contentLanguage.FRENCH - ? t('dashboard.organization.createNew.validations.unilingualEmptyDescription') - : calendarContentLanguage === contentLanguage.BILINGUAL && - t('dashboard.organization.createNew.validations.emptyDescription', { - wordCount: descriptionMinimumWordCount, - }), - ), - ); - }, - }) - : [], - descriptionMinimumWordCount - ? () => ({ - validator() { - if ( - reactQuillRefFr?.current?.unprivilegedEditor - ?.getText() - .split(' ') - ?.filter((n) => n != '')?.length > descriptionMinimumWordCount - ) { - return Promise.resolve(); - } else if ( - reactQuillRefEn?.current?.unprivilegedEditor - ?.getText() - .split(' ') - ?.filter((n) => n != '')?.length > descriptionMinimumWordCount - ) - return Promise.resolve(); - else - return Promise.reject( - new Error( - calendarContentLanguage === contentLanguage.ENGLISH || - calendarContentLanguage === contentLanguage.FRENCH - ? t('dashboard.organization.createNew.validations.unilingualDescriptionShort') - : calendarContentLanguage === contentLanguage.BILINGUAL && - t('dashboard.organization.createNew.validations.frenchShort'), - ), - ); - }, - }) - : [], - ]} - /> - - ({ - validator() { - if ( - reactQuillRefFr?.current?.unprivilegedEditor?.getLength() > 1 || - reactQuillRefEn?.current?.unprivilegedEditor?.getLength() > 1 - ) { - return Promise.resolve(); - } else - return Promise.reject( - new Error( - calendarContentLanguage === contentLanguage.ENGLISH || - calendarContentLanguage === contentLanguage.FRENCH - ? t('dashboard.organization.createNew.validations.unilingualEmptyDescription') - : calendarContentLanguage === contentLanguage.BILINGUAL && - t('dashboard.organization.createNew.validations.emptyDescription', { - wordCount: descriptionMinimumWordCount, - }), - ), - ); - }, - }) - : [], - - descriptionMinimumWordCount - ? () => ({ - validator() { - if ( - reactQuillRefEn?.current?.unprivilegedEditor - ?.getText() - .split(' ') - ?.filter((n) => n != '')?.length > descriptionMinimumWordCount - ) { - return Promise.resolve(); - } else if ( - reactQuillRefFr?.current?.unprivilegedEditor - ?.getText() - .split(' ') - ?.filter((n) => n != '')?.length > descriptionMinimumWordCount - ) - return Promise.resolve(); - else - return Promise.reject( - new Error( - calendarContentLanguage === contentLanguage.ENGLISH || - calendarContentLanguage === contentLanguage.FRENCH - ? t('dashboard.organization.createNew.validations.unilingualDescriptionShort') - : calendarContentLanguage === contentLanguage.BILINGUAL && - t('dashboard.organization.createNew.validations.englishShort'), - ), - ); - }, - }) - : [], - ]} - /> - - - ); -} - -export default BilingualTextEditor; diff --git a/src/components/BilingualTextEditor/index.js b/src/components/BilingualTextEditor/index.js deleted file mode 100644 index b86e2a2a4..000000000 --- a/src/components/BilingualTextEditor/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from './BilingualTextEditor'; diff --git a/src/components/Card/MandatoryField/MandatoryField.jsx b/src/components/Card/MandatoryField/MandatoryField.jsx index 79a4e723b..9091ed1a6 100644 --- a/src/components/Card/MandatoryField/MandatoryField.jsx +++ b/src/components/Card/MandatoryField/MandatoryField.jsx @@ -80,8 +80,7 @@ function MandatoryField(props) { addToFields(field)} diff --git a/src/components/ContentLanguageInput/ContentLanguageInput.jsx b/src/components/ContentLanguageInput/ContentLanguageInput.jsx deleted file mode 100644 index 34918a097..000000000 --- a/src/components/ContentLanguageInput/ContentLanguageInput.jsx +++ /dev/null @@ -1,56 +0,0 @@ -import React from 'react'; -import { useOutletContext } from 'react-router-dom'; -import { contentLanguage } from '../../constants/contentLanguage'; -import LiteralBadge from '../Badge/LiteralBadge'; -import { useTranslation } from 'react-i18next'; -import useModifyChildernWithFallbackLanguage from '../../hooks/useModifyChildernWithFallbackLanguage'; - -function ContentLanguageInput(props) { - const { children, calendarContentLanguage } = props; - const { t } = useTranslation(); - - // eslint-disable-next-line no-unused-vars - const [currentCalendarData, _pageNumber, _setPageNumber, _getCalendar] = useOutletContext(); - - const { fallbackStatus, modifiedChildren } = useModifyChildernWithFallbackLanguage(props, currentCalendarData); - - if (!modifiedChildren) return children; - - if (calendarContentLanguage === contentLanguage.FRENCH) { - const promptText = - fallbackStatus?.fr?.fallbackLiteralKey === '?' - ? t('common.forms.languageLiterals.unKnownLanguagePromptText') - : t('common.forms.languageLiterals.knownLanguagePromptText'); - return ( - <> - {modifiedChildren[0]?.props?.children?.filter( - (child) => child?.key?.replace(/[.$]/g, '') === contentLanguage.FRENCH, - )} - {fallbackStatus?.fr?.tagDisplayStatus && ( - - )} - - // add literal badge to the fr children - ); - } else if (calendarContentLanguage === contentLanguage.ENGLISH) { - const promptText = - fallbackStatus?.en?.fallbackLiteralKey === '?' - ? t('common.forms.languageLiterals.unKnownLanguagePromptText') - : t('common.forms.languageLiterals.knownLanguagePromptText'); - return ( - <> - {modifiedChildren[0]?.props?.children?.filter( - (child) => child?.key?.replace(/[.$]/g, '') === contentLanguage.ENGLISH, - )} - {fallbackStatus?.en?.tagDisplayStatus && ( - - )} - - // add literal badge to the en children - ); - } else if (calendarContentLanguage === contentLanguage.BILINGUAL) return <>{modifiedChildren[0]}; - else return <>{modifiedChildren[0]}; - // for bilingual, return the children as is, literal is added in BilingualInput component -} - -export default ContentLanguageInput; diff --git a/src/components/ContentLanguageInput/index.js b/src/components/ContentLanguageInput/index.js deleted file mode 100644 index 379aae908..000000000 --- a/src/components/ContentLanguageInput/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from './ContentLanguageInput'; diff --git a/src/components/DraggableTree/DraggableTree.jsx b/src/components/DraggableTree/DraggableTree.jsx index 519add48b..8e8f26c7a 100644 --- a/src/components/DraggableTree/DraggableTree.jsx +++ b/src/components/DraggableTree/DraggableTree.jsx @@ -2,17 +2,21 @@ import React, { useState, useEffect } from 'react'; import { Form, Tree, Input } from 'antd'; import { useTranslation } from 'react-i18next'; import CustomModal from '../Modal/Common/CustomModal'; -import PrimaryButton from '../../components/Button/Primary'; +import PrimaryButton from '../Button/Primary'; import { EditOutlined } from '@ant-design/icons'; -import TextButton from '../../components/Button/Text'; +import TextButton from '../Button/Text'; import { useOutletContext } from 'react-router-dom'; -import { contentLanguage } from '../../constants/contentLanguage'; -import ContentLanguageInput from '../ContentLanguageInput'; -import Outlined from '../../components/Button/Outlined'; -import BilingualInput from '../BilingualInput'; +import { contentLanguage, contentLanguageKeyMap } from '../../constants/contentLanguage'; +import Outlined from '../Button/Outlined'; import './draggableTree.css'; -import LanguageFilter from './LanguageFilter'; import { Confirm } from '../Modal/Confirm/Confirm'; +import FormItem from 'antd/es/form/FormItem'; +import { capitalizeFirstLetter } from '../../utils/stringManipulations'; +import { contentLanguageBilingual } from '../../utils/bilingual'; +import { useSelector } from 'react-redux'; +import { getUserDetails } from '../../redux/reducer/userSlice'; +import CreateMultiLingualFormItems from '../../layout/CreateMultiLingualFormItems/CreateMultiLingualFormItems'; +import { placeHolderCollectionCreator } from '../../utils/MultiLingualFormItemSupportFunctions'; const DraggableTree = ({ data, @@ -21,8 +25,7 @@ const DraggableTree = ({ setAddNewPopup, deleteDisplayFlag, setDeleteDisplayFlag, - newConceptName, - setNewConceptName, + setEmptyConceptName, form, }) => { const { TextArea } = Input; @@ -30,80 +33,103 @@ const DraggableTree = ({ const [currentCalendarData] = useOutletContext(); const calendarContentLanguage = currentCalendarData?.contentLanguage; + const { user } = useSelector(getUserDetails); const { t } = useTranslation(); - const [treeData1, setTreeData1] = useState(); - const [treeData2, setTreeData2] = useState(); + const [treeDataCollection, setTreeDataCollection] = useState({}); const [forEditing, setForEditing] = useState(); const [selectedNode, setSetSelectedNode] = useState(); const [expandedKeys, setExpandedKeys] = useState(); - - const generateFormattedData = (data, isTree1) => { - return data.map((item) => ({ - key: item.key, - title: isTree1 ? ( -
- {item.name?.fr} - { - e.stopPropagation(); - setNewConceptName({ en: item.name?.en, fr: item.name?.fr }); - setSetSelectedNode(item); - editConceptHandler(item); - }}> - - -
- ) : ( -
- {item.name?.en} - { - e.stopPropagation(); - setSetSelectedNode(item); - setNewConceptName({ en: item.name?.en, fr: item.name?.fr }); - editConceptHandler(item); - }}> - - -
- ), - children: item.children ? generateFormattedData(item.children, isTree1) : undefined, - })); + const [newConceptName, setNewConceptName] = useState(); + + const generateFormattedData = (data, language) => { + const treeData = data.map((item) => { + let conceptNameCollection = {}; + calendarContentLanguage.forEach((lang) => { + const conceptNameInCurrentLanguage = item?.name[contentLanguageKeyMap[lang]]; + if (conceptNameInCurrentLanguage) { + conceptNameCollection[contentLanguageKeyMap[lang]] = conceptNameInCurrentLanguage; + } + }); + const requiredLanguageKey = contentLanguageKeyMap[language]; + const card = { + key: item.key, + name: contentLanguageBilingual({ + requiredLanguageKey, + data: item?.name, + interfaceLanguage: user.interfaceLanguage, + calendarContentLanguage, + }), + title: ( +
+ + {contentLanguageBilingual({ + requiredLanguageKey, + data: item?.name, + interfaceLanguage: user.interfaceLanguage, + calendarContentLanguage, + })} + + { + e.stopPropagation(); + setNewConceptName(conceptNameCollection); + setSetSelectedNode(item); + editConceptHandler(item); + }}> + + +
+ ), + children: item.children ? generateFormattedData(item.children, language) : undefined, + }; + return card; + }); + return treeData; }; - const combineBothTreeData = (dataFr, dataEn) => { + const combineBothTreeData = (dataSets) => { const combinedData = []; - - for (let index = 0; index < dataFr.length; index++) { - const elementFr = dataFr[index]; - const elementEn = dataEn[index]; - const savedElement = findItem(elementFr?.key); - const combinedElement = { - id: elementFr?.key, - key: elementFr?.key, - name: { - en: elementFr.title?.props?.children[0]?.props?.children, - fr: elementEn.title?.props?.children[0]?.props?.children, - }, - ...(savedElement?.isNew && { isNew: savedElement?.isNew }), + const dataSetKeyCollection = Object.keys(dataSets); + const firstTree = dataSets[dataSetKeyCollection[0]]; + + for (let index = 0; index < dataSets[dataSetKeyCollection[0]]?.length; index++) { + let combinedNames = {}; + let combinedElement = { + id: firstTree[index]?.key, + key: firstTree[index]?.key, + name: {}, children: [], }; - if (elementFr?.children?.length > 0) { - combinedElement.children = combineBothTreeData(elementFr.children, elementEn.children); + dataSetKeyCollection.forEach((conceptLanguageKey) => { + combinedNames[contentLanguageKeyMap[conceptLanguageKey]] = dataSets[conceptLanguageKey]?.[index]?.name; + }); + + combinedElement = { ...combinedElement, name: combinedNames }; + + if (firstTree[index]?.children?.length > 0) { + let childDataSets = {}; + dataSetKeyCollection.forEach((conceptLanguageKey) => { + childDataSets[conceptLanguageKey] = dataSets[conceptLanguageKey]?.[index]?.children; + }); + combinedElement.children = combineBothTreeData(childDataSets); } + const savedElement = findItem(combinedElement.key); + if (savedElement?.isNew) { + combinedElement.isNew = savedElement.isNew; + } combinedData.push(combinedElement); } - return combinedData; }; - const onDrop = ({ info, treeData, setTreeData, counterpartTreeData, setCounterpartTreeData, treeLanguage }) => { + const onDrop = ({ info }) => { const dropKey = info.node.key; const dragKey = info.dragNode.key; const dropPos = info.node.pos.split('-'); const dropPosition = info.dropPosition - Number(dropPos[dropPos.length - 1]); + let modifiedDataCollection = {}; const loop = (data, key, callback) => { for (let i = 0; i < data.length; i++) { @@ -116,69 +142,40 @@ const DraggableTree = ({ } }; - let dragObj; - loop(treeData, dragKey, (item, index, arr) => { - arr.splice(index, 1); - dragObj = item; - }); - - if (!info.dropToGap) { - loop(treeData, dropKey, (item) => { - item.children = item.children || []; - item.children.unshift(dragObj); + calendarContentLanguage.forEach((language) => { + let dragObj; + loop(treeDataCollection[language], dragKey, (item, index, arr) => { + arr.splice(index, 1); + dragObj = item; }); - } else if ((info.node.children || []).length > 0 && info.node.expanded && dropPosition === 1) { - loop(treeData, dropKey, (item) => { - item.children = item.children || []; - item.children.unshift(dragObj); - }); - } else { - let ar = []; - let i; - loop(treeData, dropKey, (_item, index, arr) => { - ar = arr; - i = index; - }); - if (dropPosition === -1) { - ar.splice(i, 0, dragObj); - } else { - ar.splice(i + 1, 0, dragObj); - } - } - let dragObj2; - loop(counterpartTreeData, dragKey, (item, index, arr) => { - arr.splice(index, 1); - dragObj2 = item; - }); - if (!info.dropToGap) { - loop(counterpartTreeData, dropKey, (item) => { - item.children = item.children || []; - item.children.unshift(dragObj2); - }); - } else if ((info.node.children || []).length > 0 && info.node.expanded && dropPosition === 1) { - loop(counterpartTreeData, dropKey, (item) => { - item.children = item.children || []; - item.children.unshift(dragObj2); - }); - } else { - let ar = []; - let i; - loop(counterpartTreeData, dropKey, (_item, index, arr) => { - ar = arr; - i = index; - }); - if (dropPosition === -1) { - ar.splice(i, 0, dragObj2); + if (!info.dropToGap) { + loop(treeDataCollection[language], dropKey, (item) => { + item.children = item.children || []; + item.children.unshift(dragObj); + }); + } else if ((info.node.children || []).length > 0 && info.node.expanded && dropPosition === 1) { + loop(treeDataCollection[language], dropKey, (item) => { + item.children = item.children || []; + item.children.unshift(dragObj); + }); } else { - ar.splice(i + 1, 0, dragObj2); + let ar = []; + let i; + loop(treeDataCollection[language], dropKey, (_item, index, arr) => { + ar = arr; + i = index; + }); + if (dropPosition === -1) { + ar.splice(i, 0, dragObj); + } else { + ar.splice(i + 1, 0, dragObj); + } } - } - setTreeData([...treeData]); - setCounterpartTreeData([...counterpartTreeData]); + modifiedDataCollection[language] = [...treeDataCollection[language]]; + }); - if (treeLanguage == contentLanguage.FRENCH) setData(combineBothTreeData(treeData, counterpartTreeData)); - else if (treeLanguage == contentLanguage.ENGLISH) setData(combineBothTreeData(counterpartTreeData, treeData)); + setData(combineBothTreeData(modifiedDataCollection)); }; const findItem = (key) => { @@ -202,10 +199,11 @@ const DraggableTree = ({ const editConceptHandler = (node) => { if (node) { - form.setFieldsValue({ - frenchconcept: node?.name?.fr, - englishconcept: node?.name?.enƒ, + let conceptNameCollection = {}; + calendarContentLanguage.forEach((language) => { + conceptNameCollection[contentLanguageKeyMap[language]] = node?.name[contentLanguageKeyMap[language]]; }); + form.setFieldValue('conceptName', conceptNameCollection); setAddNewPopup(true); setDeleteDisplayFlag(true); setForEditing(true); @@ -213,20 +211,23 @@ const DraggableTree = ({ }; const handleAddChildModalClose = () => { - setNewConceptName({ en: '', fr: '' }); - form.setFieldsValue({ - frenchconcept: '', - englishconcept: '', + setEmptyConceptName(); + let conceptNameCollection = {}; + calendarContentLanguage.forEach((language) => { + conceptNameCollection[contentLanguageKeyMap[language]] = ''; }); + form.setFieldValue('conceptName', conceptNameCollection); setSetSelectedNode(); setAddNewPopup(false); }; const handleAddChild = () => { + const conceptNameCollection = form.getFieldValue('conceptName') || {}; + if (forEditing) { const updatedNode = { ...selectedNode, - name: { en: newConceptName?.en, fr: newConceptName?.fr }, + name: conceptNameCollection, }; const updatedData = updateNodeInData(data, selectedNode?.key, updatedNode); setData(updatedData); @@ -235,7 +236,7 @@ const DraggableTree = ({ const newChildNode = { key: Date.now().toString(), id: Date.now().toString(), - name: { en: newConceptName?.en, fr: newConceptName?.fr }, + name: conceptNameCollection, children: [], isNew: true, }; @@ -251,7 +252,7 @@ const DraggableTree = ({ setData(updatedData); } } - setNewConceptName({ en: '', fr: '' }); + setEmptyConceptName(); handleAddChildModalClose(); setSetSelectedNode(); }; @@ -285,7 +286,7 @@ const DraggableTree = ({ const updatedData = deleteNodeFromData(data, selectedNode.key); setData(updatedData); setForEditing(false); - setNewConceptName({ en: '', fr: '' }); + setEmptyConceptName(); handleAddChildModalClose(); } else { setDeleteDisplayFlag(false); @@ -334,73 +335,46 @@ const DraggableTree = ({ }; useEffect(() => { - setTreeData1(generateFormattedData(data, true)); - setTreeData2(generateFormattedData(data, false)); - }, [data]); + if (!calendarContentLanguage) return; + let t = {}; + calendarContentLanguage.forEach((language) => { + t[language] = generateFormattedData(data, language); + }); + setTreeDataCollection(t); + }, [data, calendarContentLanguage]); return (
- - - - {t('dashboard.taxonomy.addNew.concepts.english')} - -
- - onDrop({ - info, - treeData: treeData1, - setTreeData: setTreeData1, - counterpartTreeData: treeData2, - setCounterpartTreeData: setTreeData2, - treeLanguage: contentLanguage.ENGLISH, - }) - } - onExpand={(key) => { - setExpandedKeys(key); - }} - treeData={treeData2} - /> -
-
-
- - - - - {t('dashboard.taxonomy.addNew.concepts.french')} - -
- - onDrop({ - info, - treeData: treeData2, - setTreeData: setTreeData2, - counterpartTreeData: treeData1, - setCounterpartTreeData: setTreeData1, - treeLanguage: contentLanguage.FRENCH, - }) - } - onExpand={(key) => { - setExpandedKeys(key); - }} - treeData={treeData1} - /> -
-
-
+ {calendarContentLanguage.map((language) => { + return ( + + + {t(`common.tab${capitalizeFirstLetter(language)}`)} + + +
+ + onDrop({ + info, + treeData: treeDataCollection[language], + treeLanguage: contentLanguage.ENGLISH, + }) + } + onExpand={(key) => { + setExpandedKeys(key); + }} + treeData={treeDataCollection[language]} + /> +
+
+ ); + })}
- - - ({ - validator(_, value) { - if (value || getFieldValue('englishconcept')) { - return Promise.resolve(); - } else - return Promise.reject(new Error(t('dashboard.taxonomy.addNew.validations.conceptName'))); - }, - }), - ]}> -