diff --git a/src/components/Badge/LiteralBadge.jsx b/src/components/Badge/LiteralBadge.jsx new file mode 100644 index 000000000..c27d03f59 --- /dev/null +++ b/src/components/Badge/LiteralBadge.jsx @@ -0,0 +1,16 @@ +import React from 'react'; +import Tags from '../Tags/Common/Tags'; +import TooltipStyled from '../Tooltip/TooltipStyled'; +import './literalBadge.css'; + +const LiteralBadge = ({ tagTitle, promptText = 'promptText' }) => { + return ( +
+ + {tagTitle} + +
+ ); +}; + +export default LiteralBadge; diff --git a/src/components/Badge/literalBadge.css b/src/components/Badge/literalBadge.css new file mode 100644 index 000000000..992651208 --- /dev/null +++ b/src/components/Badge/literalBadge.css @@ -0,0 +1,16 @@ +.literal-badge { + position: absolute; + top: 9px; + right: 0; + color: red; +} + +.literal-badge .tags-wrapper { + height: 24px; + padding: 4px 8px 4px 8px; + gap: 10px; + border-radius: 4px; + border: 1px solid #1b3de6; + color: #0f0e98; + text-transform: capitalize; +} diff --git a/src/components/BilingualInput/BilingualInput.jsx b/src/components/BilingualInput/BilingualInput.jsx index d95615e8e..c194cd8aa 100644 --- a/src/components/BilingualInput/BilingualInput.jsx +++ b/src/components/BilingualInput/BilingualInput.jsx @@ -3,6 +3,7 @@ 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(); @@ -12,9 +13,9 @@ function BilingualInput(props) { let defaultTab = props?.defaultTab ?? 'fr'; // Adjust tabs unless brand new entity - if (props.fieldData) { - let enContent = props.fieldData.en; - let frContent = props.fieldData.fr; + if (props?.fieldData) { + let enContent = props?.fieldData.en; + let frContent = props?.fieldData.fr; // Change default tab to 'en' if only english if (enContent && !frContent) { @@ -40,20 +41,44 @@ function BilingualInput(props) { } } + 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], + children: ( +
+ {props.children[0]} + {props?.fallbackStatus?.fr?.tagDisplayStatus && ( + + )} +
+ ), }, { label: labelEn, key: 'en', forceRender: true, - children: props.children[1], + children: ( +
+ {props.children[1]} + {props?.fallbackStatus?.en?.tagDisplayStatus && ( + + )} +
+ ), }, ]; + return ( child?.key === contentLanguage.FRENCH); - else if (calendarContentLanguage === contentLanguage.ENGLISH) - return children?.props?.children?.filter((child) => child?.key === contentLanguage.ENGLISH); - else if (calendarContentLanguage === contentLanguage.BILINGUAL) return children; - else return children; + // 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/List/SelectionItem/SelectionItem.jsx b/src/components/List/SelectionItem/SelectionItem.jsx index 428ace2ad..144ddb923 100644 --- a/src/components/List/SelectionItem/SelectionItem.jsx +++ b/src/components/List/SelectionItem/SelectionItem.jsx @@ -9,6 +9,7 @@ import { getUserDetails } from '../../../redux/reducer/userSlice'; import ArtsDataLink from '../../Tags/ArtsDataLink/ArtsDataLink'; import SmallButton from '../../Button/SmallButton'; import ReadOnlyProtectedComponent from '../../../layout/ReadOnlyProtectedComponent'; +import LiteralBadge from '../../Badge/LiteralBadge'; function SelectionItem(props) { const { @@ -30,9 +31,16 @@ function SelectionItem(props) { onEdit, edit, creatorId, + fallbackConfig, } = props; const { t } = useTranslation(); const { user } = useSelector(getUserDetails); + + const promptText = + fallbackConfig?.en?.fallbackLiteralKey === '?' || fallbackConfig?.fr?.fallbackLiteralKey === '?' + ? t('common.forms.languageLiterals.unKnownLanguagePromptText') + : t('common.forms.languageLiterals.knownLanguagePromptText'); + return (
{name} + {(fallbackConfig?.fr?.tagDisplayStatus || fallbackConfig?.en?.tagDisplayStatus) && ( + + )} } description={ diff --git a/src/components/List/SelectionItem/selectionItem.css b/src/components/List/SelectionItem/selectionItem.css index aa117f33a..f331add96 100644 --- a/src/components/List/SelectionItem/selectionItem.css +++ b/src/components/List/SelectionItem/selectionItem.css @@ -23,7 +23,15 @@ font-weight: 700; font-size: 16px; color: #222732; + display: flex; + gap: 8px; + align-items: center; } + +.selection-item-list-wrapper .selection-item-title .literal-badge { + position: inherit; +} + .selection-item-list-wrapper .ant-list-item-action-split { display: none; } diff --git a/src/components/Modal/Confirm/Confirm.jsx b/src/components/Modal/Confirm/Confirm.jsx index 5bbeba59a..75f6d3e4b 100644 --- a/src/components/Modal/Confirm/Confirm.jsx +++ b/src/components/Modal/Confirm/Confirm.jsx @@ -2,9 +2,15 @@ import { Modal } from 'antd'; import { ExclamationCircleOutlined } from '@ant-design/icons'; import './confirm.css'; -export const Confirm = ({ title, content, onAction, okText, cancelText }) => { +export const Confirm = ({ title, content, onAction, okText, cancelText, className }) => { const { confirm } = Modal; + let modalClassName = ['global-delete-modal-container']; + if (className) { + modalClassName.push(className); + modalClassName = modalClassName.join(' '); + } + return confirm({ title: ( <> @@ -22,7 +28,7 @@ export const Confirm = ({ title, content, onAction, okText, cancelText }) => { okText: okText, cancelText: cancelText, centered: true, - className: 'global-delete-modal-container', + className: modalClassName, closable: true, header: null, onOk() { diff --git a/src/components/Modal/QuickCreateOrganization/QuickCreateOrganization.jsx b/src/components/Modal/QuickCreateOrganization/QuickCreateOrganization.jsx index eff4ec7fe..b4f96a48f 100644 --- a/src/components/Modal/QuickCreateOrganization/QuickCreateOrganization.jsx +++ b/src/components/Modal/QuickCreateOrganization/QuickCreateOrganization.jsx @@ -21,6 +21,7 @@ import Outlined from '../../Button/Outlined'; import { useNavigate, useParams } from 'react-router-dom'; import { PathName } from '../../../constants/pathName'; import QuickCreateSaving from '../QuickCreateSaving/QuickCreateSaving'; +import { eventPublishState } from '../../../constants/eventPublishState'; const { TextArea } = Input; @@ -60,7 +61,7 @@ function QuickCreateOrganization(props) { useEffect(() => { if (event.length > 0) { - saveAsDraftHandler(event[0], true) + saveAsDraftHandler(event[0], true, eventPublishState.DRAFT) .then((res) => { setLoaderModalOpen(false); if (res) { diff --git a/src/components/Modal/QuickCreatePerson/QuickCreatePerson.jsx b/src/components/Modal/QuickCreatePerson/QuickCreatePerson.jsx index 1f926393f..e00b60ced 100644 --- a/src/components/Modal/QuickCreatePerson/QuickCreatePerson.jsx +++ b/src/components/Modal/QuickCreatePerson/QuickCreatePerson.jsx @@ -26,6 +26,7 @@ import Outlined from '../../Button/Outlined'; import { useNavigate, useParams } from 'react-router-dom'; import { PathName } from '../../../constants/pathName'; import QuickCreateSaving from '../QuickCreateSaving/QuickCreateSaving'; +import { eventPublishState } from '../../../constants/eventPublishState'; const { TextArea } = Input; @@ -63,7 +64,7 @@ function QuickCreatePerson(props) { useEffect(() => { if (event.length > 0) { - saveAsDraftHandler(event[0], true) + saveAsDraftHandler(event[0], true, eventPublishState.DRAFT) .then((res) => { setLoaderModalOpen(false); if (res) { diff --git a/src/components/Modal/QuickCreatePlace/QuickCreatePlace.jsx b/src/components/Modal/QuickCreatePlace/QuickCreatePlace.jsx index 8566bdbbe..cc9be418b 100644 --- a/src/components/Modal/QuickCreatePlace/QuickCreatePlace.jsx +++ b/src/components/Modal/QuickCreatePlace/QuickCreatePlace.jsx @@ -30,6 +30,7 @@ import { PathName } from '../../../constants/pathName'; import QuickCreateSaving from '../QuickCreateSaving/QuickCreateSaving'; import { sourceOptions } from '../../../constants/sourceOptions'; import { entitiesClass } from '../../../constants/entitiesClass'; +import { eventPublishState } from '../../../constants/eventPublishState'; const { TextArea } = Input; @@ -61,7 +62,7 @@ function QuickCreatePlace(props) { const [event, setEvent] = useState([]); useEffect(() => { if (event.length > 0) { - saveAsDraftHandler(event[0], true) + saveAsDraftHandler(event[0], true, eventPublishState.DRAFT) .then((res) => { setLoaderModalOpen(false); if (res) { diff --git a/src/components/Select/selectOption.settings.js b/src/components/Select/selectOption.settings.js index 46b6928e2..31c95d320 100644 --- a/src/components/Select/selectOption.settings.js +++ b/src/components/Select/selectOption.settings.js @@ -1,5 +1,6 @@ import { sourceOptions } from '../../constants/sourceOptions'; import { contentLanguageBilingual } from '../../utils/bilingual'; +import { languageFallbackSetup } from '../../utils/languageFallbackSetup'; import SelectionItem from '../List/SelectionItem'; import { EnvironmentOutlined } from '@ant-design/icons'; @@ -23,7 +24,7 @@ export const taxonomyOptions = (data, user, mappedToField, calendarContentLangua return options; }; -export const placesOptions = (data, user, calendarContentLanguage, source = sourceOptions.CMS) => { +export const placesOptions = (data, user, calendarContentLanguage, source = sourceOptions.CMS, currentCalendarData) => { let options = data?.map((place) => { return { label: ( @@ -84,6 +85,14 @@ export const placesOptions = (data, user, calendarContentLanguage, source = sour uri: place?.uri, type: place?.type, creatorId: place?.creator?.userId ?? place?.createdByUserId, + fallBackStatus: currentCalendarData + ? languageFallbackSetup({ + currentCalendarData, + fieldData: place?.name, + languageFallbacks: currentCalendarData?.languageFallbacks, + isFieldsDirty: true, + }) + : null, }; }); return options; diff --git a/src/components/TreeSelectOption/treeSelectOption.settings.js b/src/components/TreeSelectOption/treeSelectOption.settings.js index 140b84987..a34b213f9 100644 --- a/src/components/TreeSelectOption/treeSelectOption.settings.js +++ b/src/components/TreeSelectOption/treeSelectOption.settings.js @@ -4,6 +4,7 @@ import Icon, { UserOutlined } from '@ant-design/icons'; import { ReactComponent as Organizations } from '../../assets/icons/organisations.svg'; import { taxonomyClass } from '../../constants/taxonomyClass'; import { sourceOptions } from '../../constants/sourceOptions'; +import { languageFallbackSetup } from '../../utils/languageFallbackSetup'; const handleMultilevelTreeSelect = (children, user, calendarContentLanguage, parentLabel) => { return children?.map((child) => { @@ -75,7 +76,13 @@ export const treeTaxonomyOptions = (data, user, mappedToField, isDynamicField, c return options; }; -export const treeEntitiesOption = (data, user, calendarContentLanguage, source = sourceOptions.CMS) => { +export const treeEntitiesOption = ( + data, + user, + calendarContentLanguage, + source = sourceOptions.CMS, + currentCalendarData, +) => { let options = data?.map((entity) => { return { label: ( @@ -145,6 +152,14 @@ export const treeEntitiesOption = (data, user, calendarContentLanguage, source = uri: entity?.uri, source: source, creatorId: entity?.creator?.userId, + fallBackStatus: currentCalendarData + ? languageFallbackSetup({ + currentCalendarData, + fieldData: entity?.name, + languageFallbacks: currentCalendarData?.languageFallbacks, + isFieldsDirty: true, + }) + : null, }; }); return options; diff --git a/src/constants/formFields.js b/src/constants/formFields.js index d04f11254..3aa0dd11b 100644 --- a/src/constants/formFields.js +++ b/src/constants/formFields.js @@ -93,10 +93,16 @@ export const formFieldValue = [ validations, required, mappedField, + form, }) => { if (datatype === dataTypes.MULTI_LINGUAL) return ( - + { + if (!currentCalendarData) return; + + const status = languageFallbackSetup({ + currentCalendarData, + fieldData: children?.props?.fieldData, + languageFallbacks: currentCalendarData.languageFallbacks, + isFieldsDirty: isFieldsDirty, + }); + + // Only update fallbackStatus if it has actually changed + if (JSON.stringify(status) !== JSON.stringify(fallbackStatus)) { + setFallbackStatus(status); + } + }, [children, isFieldsDirty]); + + useEffect(() => { + const combinedName = children?.props?.children + ?.map((child) => { + if (child?.props?.name) return child?.props?.name; + if (child?.props?.formName) return child?.props?.formName; + else return ''; + }) + .join('-'); + + const modifiedActiveFallbackFieldsInfo = { + [combinedName]: fallbackStatus, + ...activeFallbackFieldsInfo, + }; + + if (fallbackStatus?.fr?.tagDisplayStatus || fallbackStatus?.en?.tagDisplayStatus) { + dispatch(setActiveFallbackFieldsInfo({ data: modifiedActiveFallbackFieldsInfo, method: 'add' })); + } else if (isFieldsDirty?.en || isFieldsDirty?.fr) { + // eslint-disable-next-line no-unused-vars + const { [combinedName]: _, ...rest } = activeFallbackFieldsInfo; + dispatch(setActiveFallbackFieldsInfo({ data: rest, method: 'remove' })); + } + }, [fallbackStatus]); + + const modifiedChildren = useMemo(() => { + if (!children || !fallbackStatus) return children; + + return React.Children.map(children, (child) => { + let modifiedChild = child; + + if (child && child.props && child.props.children) { + modifiedChild = cloneElement(child, { + ...child.props, + fallbackStatus: fallbackStatus, + children: React.Children.map(child.props.children, (innerChild) => { + let modifiedInnerChild = innerChild; + if (innerChild?.key === contentLanguage.FRENCH && !innerChild?.props?.initialValue) { + modifiedInnerChild = cloneElement(innerChild, { + ...innerChild.props, + className: 'bilingual-child-with-badge', + initialValue: fallbackStatus['fr']?.fallbackLiteralValue, + }); + } else if (innerChild?.key === contentLanguage.ENGLISH && !innerChild?.props?.initialValue) { + modifiedInnerChild = cloneElement(innerChild, { + ...innerChild.props, + className: 'bilingual-child-with-badge', + initialValue: fallbackStatus['en']?.fallbackLiteralValue, + }); + } + return modifiedInnerChild; + }), + }); + } + return modifiedChild; + }); + }, [children, fallbackStatus]); + + return { + fallbackStatus, + modifiedChildren, + }; +} + +export default useModifyChildernWithFallbackLanguage; diff --git a/src/locales/en/translationEn.json b/src/locales/en/translationEn.json index 9e0206ede..1fbdff971 100644 --- a/src/locales/en/translationEn.json +++ b/src/locales/en/translationEn.json @@ -130,6 +130,12 @@ "addMoreDetails": "Add more details to your event", "allDone": "All done", "featuredEvent": "Featured event", + "fallbackConfirm": { + "contentPart1": "You haven’t updated all the text that doesn’t match the language(s) on your website.", + "contentPart2": "Do you want to publish as is?", + "title": "Publish event", + "publish": "Publish" + }, "language": { "title": "Event title", "titleLabel": "English", @@ -1074,12 +1080,20 @@ }, "common": { "tabEnglish": "English", - "tabFrench": "French", "noneFound": "None Found", + "tabFrench": "French", "unsavedChanges": "You have unsaved changes. Are you sure you want to discard them?", "keepEditing": "Keep editing", "copied": "Copied to clipboard", "discard": "Discard", + "dismiss": "Dismiss", + "forms": { + "languageLiterals": { + "knownLanguagePromptText": "This text does not match the language on your website.", + "unKnownLanguagePromptText": "The language of this text is not defined.", + "bannerTitle": "Some text does not match the language on your website. Review and update as needed." + } + }, "server": { "status": { "503": { diff --git a/src/locales/fr/transalationFr.json b/src/locales/fr/transalationFr.json index 0b6e60393..81665f48c 100644 --- a/src/locales/fr/transalationFr.json +++ b/src/locales/fr/transalationFr.json @@ -130,6 +130,12 @@ "addMoreDetails": "Ajouter plus de détails à votre événement", "allDone": "Terminé", "featuredEvent": "Événement vedette", + "fallbackConfirm": { + "contentPart1": "Vous n'avez pas mis à jour tous les textes qui ne correspondent pas à la langue de votre site web. ", + "contentPart2": "Voulez-vous publier tel quel ? ", + "title": "Publier l'événement", + "publish": "Publier" + }, "dataSource": "Data source", "question": { "firstPart": "Est-ce le bon événement ? Si non, ", @@ -1076,6 +1082,14 @@ "keepEditing": "Poursuivre l'édition", "discard": "Jeter", "copied": "Copié dans le presse-papiers", + "dismiss": "J'ai compris", + "forms": { + "languageLiterals": { + "knownLanguagePromptText": "Ce texte ne correspond pas à la langue de votre site web.", + "unKnownLanguagePromptText": "La langue de ce texte n'est pas définie.", + "bannerTitle": "Certains textes ne correspondent pas à la langue de votre site web. Révisez et mettez à jour si nécessaire." + } + }, "server": { "status": { "503": { diff --git a/src/pages/Dashboard/AddEvent/AddEvent.jsx b/src/pages/Dashboard/AddEvent/AddEvent.jsx index 4ec5a568c..42db75b02 100644 --- a/src/pages/Dashboard/AddEvent/AddEvent.jsx +++ b/src/pages/Dashboard/AddEvent/AddEvent.jsx @@ -1,6 +1,7 @@ import React, { useEffect, useState, useRef, useCallback } from 'react'; import './addEvent.css'; import { Form, Row, Col, Input, message, Button, notification } from 'antd'; +import { Confirm } from '../../../components/Modal/Confirm/Confirm'; import { SyncOutlined, InfoCircleOutlined, @@ -20,8 +21,9 @@ import { useGetEventQuery, useUpdateEventStateMutation } from '../../../services import { PathName } from '../../../constants/pathName'; import Outlined from '../../../components/Button/Outlined'; import PrimaryButton from '../../../components/Button/Primary'; -import { useTranslation } from 'react-i18next'; -import { useSelector } from 'react-redux'; +import OutlinedButton from '../../..//components/Button/Outlined'; +import { Translation, useTranslation } from 'react-i18next'; +import { useDispatch, useSelector } from 'react-redux'; import { getUserDetails } from '../../../redux/reducer/userSlice'; import { userRoles } from '../../../constants/userRoles'; import PublishState from '../../../components/Dropdown/PublishState/PublishState'; @@ -94,12 +96,20 @@ import Alert from '../../../components/Alert'; import ChangeTypeLayout from '../../../layout/ChangeTypeLayout/ChangeTypeLayout'; import { getEmbedUrl, validateVimeoURL, validateYouTubeURL } from '../../../utils/getEmbedVideoUrl'; import { sameAsTypes } from '../../../constants/sameAsTypes'; +import { + clearActiveFallbackFieldsInfo, + getActiveFallbackFieldsInfo, + getLanguageLiteralBannerDisplayStatus, + setLanguageLiteralBannerDisplayStatus, +} from '../../../redux/reducer/languageLiteralSlice'; +import { removeUneditedFallbackValues } from '../../../utils/removeUneditedFallbackValues'; const { TextArea } = Input; function AddEvent() { const navigate = useNavigate(); const location = useLocation(); + const dispatch = useDispatch(); const [form] = Form.useForm(); Form.useWatch('startTime', form); Form.useWatch('endTime', form); @@ -108,6 +118,8 @@ function AddEvent() { let [searchParams] = useSearchParams(); let duplicateId = searchParams.get('duplicateId'); const { user } = useSelector(getUserDetails); + const activeFallbackFieldsInfo = useSelector(getActiveFallbackFieldsInfo); + const languageLiteralBannerDisplayStatus = useSelector(getLanguageLiteralBannerDisplayStatus); const { t } = useTranslation(); const [ currentCalendarData, // eslint-disable-next-line no-unused-vars @@ -309,429 +321,544 @@ function AddEvent() { }); return promise; }; + const saveAsDraftHandler = (event, toggle = false, type = eventPublishState.PUBLISHED) => { event?.preventDefault(); const previousShowDialog = showDialog; setShowDialog(false); - var promise = new Promise(function (resolve, reject) { - form - .validateFields([ - ...new Set([ - 'french', - 'english', - 'datePicker', - 'dateRangePicker', - 'datePickerWrapper', - 'startDateRecur', - 'contactWebsiteUrl', - 'eventLink', - 'videoLink', - 'facebookLink', - ...(eventId && eventData?.publishState === eventPublishState.PUBLISHED && type !== eventPublishState.DRAFT - ? validateFields - : []), - ]), - ]) - .then(() => { - var values = form.getFieldsValue(true); - var startDateTime, - endDateTime, - additionalType = [], - audience = [], - contactPoint, - accessibility = [], - accessibilityNote, - keywords = [], - locationId, - offerConfiguration, - organizers = [], - performers = [], - collaborators = [], - dynamicFields = [], - recurringEvent, - description = {}, - inLanguage = []; + const action = ({ clear = false, previousShowDialog, toggle, type }) => { + var promise = new Promise(function (resolve, reject) { + form + .validateFields([ + ...new Set([ + 'french', + 'english', + 'datePicker', + 'dateRangePicker', + 'datePickerWrapper', + 'startDateRecur', + 'contactWebsiteUrl', + 'eventLink', + 'videoLink', + 'facebookLink', + ...(eventId && eventData?.publishState === eventPublishState.PUBLISHED && type !== eventPublishState.DRAFT + ? validateFields + : []), + ]), + ]) + .then(() => { + let fallbackStatus = activeFallbackFieldsInfo; + if (clear) { + dispatch(setLanguageLiteralBannerDisplayStatus(false)); + dispatch(clearActiveFallbackFieldsInfo()); + fallbackStatus = {}; + } + var values = form.getFieldsValue(true); + var startDateTime, + endDateTime, + additionalType = [], + audience = [], + contactPoint, + accessibility = [], + accessibilityNote, + keywords = [], + locationId, + offerConfiguration, + organizers = [], + performers = [], + collaborators = [], + dynamicFields = [], + recurringEvent, + description = {}, + name = {}, + inLanguage = []; - let eventObj; + let eventObj; - // Use a regular expression to remove


tags at the end + const englishVirtualLocation = removeUneditedFallbackValues({ + values: values?.englishVirtualLocation?.trim(), + activeFallbackFieldsInfo: fallbackStatus, + fieldName: 'frenchVirtualLocation-englishVirtualLocation', + property: 'en', + }); - if (dateType === dateTypes.SINGLE) { - if (values?.startTime) startDateTime = dateTimeConverter(values?.datePicker, values?.startTime); - else - startDateTime = moment - .tz(values?.datePicker, eventData?.scheduleTimezone ?? 'Canada/Eastern') - .format('YYYY-MM-DD'); - if (values?.endTime) endDateTime = dateTimeConverter(values?.datePicker, values?.endTime); - } - if (dateType === dateTypes.RANGE) { - if (values?.startTime) startDateTime = dateTimeConverter(values?.dateRangePicker[0], values?.startTime); - else - startDateTime = moment - .tz(values?.dateRangePicker[0], eventData?.scheduleTimezone ?? 'Canada/Eastern') - .format('YYYY-MM-DD'); - if (values?.endTime) endDateTime = dateTimeConverter(values?.dateRangePicker[1], values?.endTime); - else - endDateTime = moment - .tz(values?.dateRangePicker[1], eventData?.scheduleTimezone ?? 'Canada/Eastern') - .format('YYYY-MM-DD'); - } - if (dateType === dateTypes.MULTIPLE) { - const recurEvent = { - frequency: values.frequency, - startDate: - form.getFieldsValue().frequency !== 'CUSTOM' - ? moment(values.startDateRecur[0]).format('YYYY-MM-DD') - : undefined, - endDate: - form.getFieldsValue().frequency !== 'CUSTOM' - ? moment(values.startDateRecur[1]).format('YYYY-MM-DD') - : undefined, - startTime: - form.getFieldsValue().frequency !== 'CUSTOM' - ? values.startTimeRecur - ? moment(values.startTimeRecur).format('HH:mm') - : undefined - : undefined, - endTime: - form.getFieldsValue().frequency !== 'CUSTOM' && values.endTimeRecur - ? moment(values.endTimeRecur).format('HH:mm') - : undefined, - weekDays: values.frequency === 'WEEKLY' ? values.daysOfWeek : undefined, - customDates: form.getFieldsValue().frequency === 'CUSTOM' ? form.getFieldsValue().customDates : undefined, - }; - recurringEvent = recurEvent; - } - if (values?.eventType) { - additionalType = values?.eventType?.map((eventTypeId) => { - return { - entityId: eventTypeId, - }; + const frenchVirtualLocation = removeUneditedFallbackValues({ + values: values?.frenchVirtualLocation?.trim(), + activeFallbackFieldsInfo: fallbackStatus, + fieldName: 'frenchVirtualLocation-englishVirtualLocation', + property: 'fr', }); - } - if (values?.targetAudience) { - audience = values?.targetAudience?.map((audienceId) => { - return { - entityId: audienceId, - }; + const englishContactTitle = removeUneditedFallbackValues({ + values: values?.englishContactTitle?.trim(), + activeFallbackFieldsInfo: fallbackStatus, + fieldName: 'frenchContactTitle-englishContactTitle', + property: 'en', }); - } - if (values?.inLanguage) { - inLanguage = values?.inLanguage?.map((inLanguageId) => { - return { - entityId: inLanguageId, - }; + const frenchContactTitle = removeUneditedFallbackValues({ + values: values?.frenchContactTitle?.trim(), + activeFallbackFieldsInfo: fallbackStatus, + fieldName: 'frenchContactTitle-englishContactTitle', + property: 'fr', }); - } - if (values?.locationPlace || values?.locationPlace?.length > 0) { - let place; - if ( - locationPlace?.source === sourceOptions.CMS || - locationPlace?.source === externalSourceOptions.FOOTLIGHT - ) - place = { - entityId: values?.locationPlace, - }; - else if (locationPlace?.source === sourceOptions.ARTSDATA) - place = { - uri: values?.locationPlace, - }; - locationId = { - place, - }; - } - if (values?.frenchVirtualLocation || values?.englishVirtualLocation || values?.virtualLocationOnlineLink) { - locationId = { - ...locationId, - virtualLocation: { - name: { - en: values?.englishVirtualLocation?.trim(), - fr: values?.frenchVirtualLocation?.trim(), - }, - description: {}, - dynamicFields: [], - url: { - uri: urlProtocolCheck(values?.virtualLocationOnlineLink), - }, - }, - }; - } - if ( - values?.frenchContactTitle || - values?.englishContactTitle || - values?.contactWebsiteUrl || - values?.contactEmail || - values?.contactPhoneNumber - ) { - contactPoint = { - name: { - en: values?.englishContactTitle?.trim(), - fr: values?.frenchContactTitle?.trim(), - }, - url: { - uri: urlProtocolCheck(values?.contactWebsiteUrl), - }, - email: values?.contactEmail, - telephone: values?.contactPhoneNumber, - }; - } - if (values?.eventAccessibility) { - accessibility = values?.eventAccessibility?.map((accessibilityId) => { - return { - entityId: accessibilityId, - }; - }); - } - - if (values?.englishAccessibilityNote || values?.frenchAccessibilityNote) { - accessibilityNote = { - ...(values?.englishAccessibilityNote && { en: values?.englishAccessibilityNote?.trim() }), - ...(values?.frenchAccessibilityNote && { fr: values?.frenchAccessibilityNote?.trim() }), - }; - } - if (values?.keywords?.length > 0) { - keywords = values?.keywords; - } + const englishTicketNote = removeUneditedFallbackValues({ + values: values?.englishTicketNote?.trim(), + activeFallbackFieldsInfo: fallbackStatus, + fieldName: 'frenchTicketNote-englishTicketNote', + property: 'en', + }); - if (ticketType) { - offerConfiguration = { - category: ticketType, - ...((values?.englishTicketNote || values?.frenchTicketNote) && { - name: { - en: values?.englishTicketNote?.trim(), - fr: values?.frenchTicketNote?.trim(), - }, - }), - ...(ticketType === offerTypes.PAYING && - values?.prices?.length > 0 && - values?.prices[0] && { - prices: values?.prices?.filter((element) => element != null || element != undefined), - }), - priceCurrency: 'CAD', - ...(ticketType === offerTypes.PAYING && - values?.ticketLink && - values?.ticketLinkType == ticketLinkOptions[0].value && { - url: { - uri: urlProtocolCheck(values?.ticketLink), - }, - }), - ...(ticketType === offerTypes.PAYING && - values?.ticketLink && - values?.ticketLinkType == ticketLinkOptions[1].value && { - email: values?.ticketLink, - }), - ...(ticketType === offerTypes.REGISTER && - values?.registerLink && - values?.ticketLinkType === ticketLinkOptions[0].value && { - url: { - uri: urlProtocolCheck(values?.registerLink), - }, - }), - ...(ticketType === offerTypes.REGISTER && - values?.registerLink && - values?.ticketLinkType === ticketLinkOptions[1].value && { - email: values?.registerLink, - }), - }; - } + const frenchTicketNote = removeUneditedFallbackValues({ + values: values?.frenchTicketNote?.trim(), + activeFallbackFieldsInfo: fallbackStatus, + fieldName: 'frenchTicketNote-englishTicketNote', + property: 'fr', + }); + const descriptionFr = removeUneditedFallbackValues({ + values: values?.frenchEditor?.trim(), + activeFallbackFieldsInfo: fallbackStatus, + fieldName: 'frenchEditor-englishEditor', + property: 'fr', + }); + const descriptionEn = removeUneditedFallbackValues({ + values: values?.englishEditor?.trim(), + activeFallbackFieldsInfo: fallbackStatus, + fieldName: 'frenchEditor-englishEditor', + property: 'en', + }); + const nameFr = removeUneditedFallbackValues({ + values: values?.french?.trim(), + activeFallbackFieldsInfo: fallbackStatus, + fieldName: 'french-english', + property: 'fr', + }); + const nameEn = removeUneditedFallbackValues({ + values: values?.english?.trim(), + activeFallbackFieldsInfo: fallbackStatus, + fieldName: 'french-english', + property: 'en', + }); + // Use a regular expression to remove


tags at the end - if (values?.organizers) { - organizers = values?.organizers?.map((organizer) => { - if (organizer?.source === sourceOptions.CMS || organizer?.source === externalSourceOptions.FOOTLIGHT) + if (dateType === dateTypes.SINGLE) { + if (values?.startTime) startDateTime = dateTimeConverter(values?.datePicker, values?.startTime); + else + startDateTime = moment + .tz(values?.datePicker, eventData?.scheduleTimezone ?? 'Canada/Eastern') + .format('YYYY-MM-DD'); + if (values?.endTime) endDateTime = dateTimeConverter(values?.datePicker, values?.endTime); + } + if (dateType === dateTypes.RANGE) { + if (values?.startTime) startDateTime = dateTimeConverter(values?.dateRangePicker[0], values?.startTime); + else + startDateTime = moment + .tz(values?.dateRangePicker[0], eventData?.scheduleTimezone ?? 'Canada/Eastern') + .format('YYYY-MM-DD'); + if (values?.endTime) endDateTime = dateTimeConverter(values?.dateRangePicker[1], values?.endTime); + else + endDateTime = moment + .tz(values?.dateRangePicker[1], eventData?.scheduleTimezone ?? 'Canada/Eastern') + .format('YYYY-MM-DD'); + } + if (dateType === dateTypes.MULTIPLE) { + const recurEvent = { + frequency: values.frequency, + startDate: + form.getFieldsValue().frequency !== 'CUSTOM' + ? moment(values.startDateRecur[0]).format('YYYY-MM-DD') + : undefined, + endDate: + form.getFieldsValue().frequency !== 'CUSTOM' + ? moment(values.startDateRecur[1]).format('YYYY-MM-DD') + : undefined, + startTime: + form.getFieldsValue().frequency !== 'CUSTOM' + ? values.startTimeRecur + ? moment(values.startTimeRecur).format('HH:mm') + : undefined + : undefined, + endTime: + form.getFieldsValue().frequency !== 'CUSTOM' && values.endTimeRecur + ? moment(values.endTimeRecur).format('HH:mm') + : undefined, + weekDays: values.frequency === 'WEEKLY' ? values.daysOfWeek : undefined, + customDates: + form.getFieldsValue().frequency === 'CUSTOM' ? form.getFieldsValue().customDates : undefined, + }; + recurringEvent = recurEvent; + } + if (values?.eventType) { + additionalType = values?.eventType?.map((eventTypeId) => { return { - entityId: organizer?.value, - type: organizer?.type, + entityId: eventTypeId, }; - else if (organizer?.source === sourceOptions.ARTSDATA) + }); + } + if (values?.targetAudience) { + audience = values?.targetAudience?.map((audienceId) => { return { - uri: organizer?.uri, - type: organizer?.type, + entityId: audienceId, }; - }); - } - - if (values?.performers) { - performers = values?.performers?.map((performer) => { - if (performer?.source === sourceOptions.CMS || performer?.source === externalSourceOptions.FOOTLIGHT) + }); + } + if (values?.inLanguage) { + inLanguage = values?.inLanguage?.map((inLanguageId) => { return { - entityId: performer?.value, - type: performer?.type, + entityId: inLanguageId, }; - else if (performer?.source === sourceOptions.ARTSDATA) - return { - uri: performer?.uri, - type: performer?.type, + }); + } + if (values?.locationPlace || values?.locationPlace?.length > 0) { + let place; + if ( + locationPlace?.source === sourceOptions.CMS || + locationPlace?.source === externalSourceOptions.FOOTLIGHT + ) + place = { + entityId: values?.locationPlace, }; - }); - } + else if (locationPlace?.source === sourceOptions.ARTSDATA) + place = { + uri: values?.locationPlace, + }; + locationId = { + place, + }; + } + if (values?.frenchVirtualLocation || values?.englishVirtualLocation || values?.virtualLocationOnlineLink) { + const name = {}; + + if (frenchVirtualLocation) name['fr'] = frenchVirtualLocation; + if (englishVirtualLocation) name['en'] = englishVirtualLocation; + + locationId = { + ...locationId, + virtualLocation: { + name, + description: {}, + dynamicFields: [], + url: { + uri: urlProtocolCheck(values?.virtualLocationOnlineLink), + }, + }, + }; + } + if ( + values?.frenchContactTitle || + values?.englishContactTitle || + values?.contactWebsiteUrl || + values?.contactEmail || + values?.contactPhoneNumber + ) { + const name = {}; - if (values?.supporters) { - collaborators = values?.supporters?.map((supporter) => { - if (supporter?.source === sourceOptions.CMS || supporter?.source === externalSourceOptions.FOOTLIGHT) + if (frenchContactTitle) name['fr'] = frenchContactTitle; + if (englishContactTitle) name['en'] = englishContactTitle; + + contactPoint = { + name, + url: { + uri: urlProtocolCheck(values?.contactWebsiteUrl), + }, + email: values?.contactEmail, + telephone: values?.contactPhoneNumber, + }; + } + if (values?.eventAccessibility) { + accessibility = values?.eventAccessibility?.map((accessibilityId) => { return { - entityId: supporter?.value, - type: supporter?.type, + entityId: accessibilityId, }; - else if (supporter?.source === sourceOptions.ARTSDATA) + }); + } + + if (values?.englishAccessibilityNote || values?.frenchAccessibilityNote) { + accessibilityNote = { + ...(values?.englishAccessibilityNote && { en: values?.englishAccessibilityNote?.trim() }), + ...(values?.frenchAccessibilityNote && { fr: values?.frenchAccessibilityNote?.trim() }), + }; + } + + if (values?.keywords?.length > 0) { + keywords = values?.keywords; + } + + if (ticketType) { + const name = {}; + + if (englishTicketNote) name['en'] = englishTicketNote; + if (frenchTicketNote) name['fr'] = frenchTicketNote; + + offerConfiguration = { + category: ticketType, + ...((values?.englishTicketNote || values?.frenchTicketNote) && { + name, + }), + ...(ticketType === offerTypes.PAYING && + values?.prices?.length > 0 && + values?.prices[0] && { + prices: values?.prices?.filter((element) => element != null || element != undefined), + }), + priceCurrency: 'CAD', + ...(ticketType === offerTypes.PAYING && + values?.ticketLink && + values?.ticketLinkType == ticketLinkOptions[0].value && { + url: { + uri: urlProtocolCheck(values?.ticketLink), + }, + }), + ...(ticketType === offerTypes.PAYING && + values?.ticketLink && + values?.ticketLinkType == ticketLinkOptions[1].value && { + email: values?.ticketLink, + }), + ...(ticketType === offerTypes.REGISTER && + values?.registerLink && + values?.ticketLinkType === ticketLinkOptions[0].value && { + url: { + uri: urlProtocolCheck(values?.registerLink), + }, + }), + ...(ticketType === offerTypes.REGISTER && + values?.registerLink && + values?.ticketLinkType === ticketLinkOptions[1].value && { + email: values?.registerLink, + }), + }; + } + + if (values?.organizers) { + organizers = values?.organizers?.map((organizer) => { + if (organizer?.source === sourceOptions.CMS || organizer?.source === externalSourceOptions.FOOTLIGHT) + return { + entityId: organizer?.value, + type: organizer?.type, + }; + else if (organizer?.source === sourceOptions.ARTSDATA) + return { + uri: organizer?.uri, + type: organizer?.type, + }; + }); + } + + if (values?.performers) { + performers = values?.performers?.map((performer) => { + if (performer?.source === sourceOptions.CMS || performer?.source === externalSourceOptions.FOOTLIGHT) + return { + entityId: performer?.value, + type: performer?.type, + }; + else if (performer?.source === sourceOptions.ARTSDATA) + return { + uri: performer?.uri, + type: performer?.type, + }; + }); + } + + if (values?.supporters) { + collaborators = values?.supporters?.map((supporter) => { + if (supporter?.source === sourceOptions.CMS || supporter?.source === externalSourceOptions.FOOTLIGHT) + return { + entityId: supporter?.value, + type: supporter?.type, + }; + else if (supporter?.source === sourceOptions.ARTSDATA) + return { + uri: supporter?.uri, + type: supporter?.type, + }; + }); + } + if (values?.dynamicFields) { + dynamicFields = Object.keys(values?.dynamicFields)?.map((dynamicField) => { return { - uri: supporter?.uri, - type: supporter?.type, + taxonomyId: dynamicField, + conceptIds: values?.dynamicFields[dynamicField], }; - }); - } - if (values?.dynamicFields) { - dynamicFields = Object.keys(values?.dynamicFields)?.map((dynamicField) => { - return { - taxonomyId: dynamicField, - conceptIds: values?.dynamicFields[dynamicField], + }); + } + + if (descriptionFr && descriptionFr !== '


') + description = { + fr: descriptionFr, + }; + if (descriptionEn && descriptionEn !== '


') + description = { + ...description, + en: descriptionEn, }; - }); - } - if (values?.frenchEditor) - description = { - fr: values?.frenchEditor, - }; - if (values?.englishEditor) - description = { - ...description, - en: values?.englishEditor, - }; + if (nameEn) name['en'] = nameEn; + if (nameFr) name['fr'] = nameFr; - eventObj = { - name: { - en: values?.english?.trim(), - fr: values?.french?.trim(), - }, - ...(values?.startTime && { startDateTime }), - ...(!values?.startTime && { startDate: startDateTime }), - ...(values?.endTime && { endDateTime }), - ...(!values?.endTime && { endDate: endDateTime }), - eventStatus: values?.eventStatus, - ...((values?.englishEditor || values?.frenchEditor) && { description }), - ...(values?.eventAccessibility && { - accessibility, - }), - ...(accessibilityNote && { accessibilityNote }), - additionalType, - audience, + eventObj = { + name: !(Object.keys(name).length > 0) ? { ...name, ...eventData?.name } : name, + ...(values?.startTime && { startDateTime }), + ...(!values?.startTime && { startDate: startDateTime }), + ...(values?.endTime && { endDateTime }), + ...(!values?.endTime && { endDate: endDateTime }), + eventStatus: values?.eventStatus, + ...((values?.englishEditor || values?.frenchEditor) && { description }), + ...(values?.eventAccessibility && { + accessibility, + }), + ...(accessibilityNote && { accessibilityNote }), + additionalType, + audience, - url: { - uri: urlProtocolCheck(values?.eventLink), - }, + url: { + uri: urlProtocolCheck(values?.eventLink), + }, - ...(values?.facebookLink && { facebookUrl: urlProtocolCheck(values?.facebookLink) }), - ...(values?.videoLink && { videoUrl: urlProtocolCheck(values?.videoLink) }), - ...(contactPoint && { contactPoint }), - ...(locationId && { locationId }), - ...(keywords && { keywords }), - ...(ticketType && { offerConfiguration }), - ...(values?.organizers && { organizers }), - ...(values?.performers && { performers }), - ...(values?.supporters && { collaborators }), - ...(values?.dynamicFields && { dynamicFields }), - ...(dateTypes.MULTIPLE && { recurringEvent }), - inLanguage, - isFeatured: values?.isFeatured, - }; + ...(values?.facebookLink && { facebookUrl: urlProtocolCheck(values?.facebookLink) }), + ...(values?.videoLink && { videoUrl: urlProtocolCheck(values?.videoLink) }), + ...(contactPoint && { contactPoint }), + ...(locationId && { locationId }), + ...(keywords && { keywords }), + ...(ticketType && { offerConfiguration }), + ...(values?.organizers && { organizers }), + ...(values?.performers && { performers }), + ...(values?.supporters && { collaborators }), + ...(values?.dynamicFields && { dynamicFields }), + ...(dateTypes.MULTIPLE && { recurringEvent }), + inLanguage, + isFeatured: values?.isFeatured, + }; - let imageCrop = form.getFieldValue('imageCrop'); - imageCrop = { - large: { - xCoordinate: imageCrop?.large?.x, - yCoordinate: imageCrop?.large?.y, - height: imageCrop?.large?.height, - width: imageCrop?.large?.width, - }, - thumbnail: { - xCoordinate: imageCrop?.thumbnail?.x, - yCoordinate: imageCrop?.thumbnail?.y, - height: imageCrop?.thumbnail?.height, - width: imageCrop?.thumbnail?.width, - }, - original: { - entityId: imageCrop?.original?.entityId, - height: imageCrop?.original?.height, - width: imageCrop?.original?.width, - }, - }; + let imageCrop = form.getFieldValue('imageCrop'); + imageCrop = { + large: { + xCoordinate: imageCrop?.large?.x, + yCoordinate: imageCrop?.large?.y, + height: imageCrop?.large?.height, + width: imageCrop?.large?.width, + }, + thumbnail: { + xCoordinate: imageCrop?.thumbnail?.x, + yCoordinate: imageCrop?.thumbnail?.y, + height: imageCrop?.thumbnail?.height, + width: imageCrop?.thumbnail?.width, + }, + original: { + entityId: imageCrop?.original?.entityId, + height: imageCrop?.original?.height, + width: imageCrop?.original?.width, + }, + }; - if (values?.dragger?.length > 0 && values?.dragger[0]?.originFileObj) { - const formdata = new FormData(); - formdata.append('file', values?.dragger[0].originFileObj); - formdata && - addImage({ data: formdata, calendarId }) - .unwrap() - .then((response) => { - // let entityId = response?.data?.original?.entityId; - if (featureFlags.imageCropFeature) { - let entityId = response?.data?.original?.entityId; - imageCrop = { - ...imageCrop, - original: { - entityId, - height: response?.data?.height, - width: response?.data?.width, - }, - }; - } else - imageCrop = { - ...imageCrop, - original: { - ...imageCrop?.original, - entityId: response?.data?.original?.entityId, - height: response?.data?.height, - width: response?.data?.width, - }, - }; - eventObj['image'] = imageCrop; - addUpdateEventApiHandler(eventObj, toggle) - .then((id) => resolve(id)) - .catch((error) => { - reject(error); - console.log(error); - }); - }) + if (values?.dragger?.length > 0 && values?.dragger[0]?.originFileObj) { + const formdata = new FormData(); + formdata.append('file', values?.dragger[0].originFileObj); + formdata && + addImage({ data: formdata, calendarId }) + .unwrap() + .then((response) => { + // let entityId = response?.data?.original?.entityId; + if (featureFlags.imageCropFeature) { + let entityId = response?.data?.original?.entityId; + imageCrop = { + ...imageCrop, + original: { + entityId, + height: response?.data?.height, + width: response?.data?.width, + }, + }; + } else + imageCrop = { + ...imageCrop, + original: { + ...imageCrop?.original, + entityId: response?.data?.original?.entityId, + height: response?.data?.height, + width: response?.data?.width, + }, + }; + eventObj['image'] = imageCrop; + addUpdateEventApiHandler(eventObj, toggle) + .then((id) => resolve(id)) + .catch((error) => { + reject(error); + console.log(error); + }); + }) + .catch((error) => { + console.log(error); + const element = document.getElementsByClassName('draggerWrap'); + element && element[0]?.scrollIntoView({ block: 'center', behavior: 'smooth' }); + }); + } else { + if (values?.draggerWrap) { + if (values?.dragger && values?.dragger?.length == 0) eventObj['image'] = null; + else eventObj['image'] = imageCrop; + } + + addUpdateEventApiHandler(eventObj, toggle) + .then((id) => resolve(id)) .catch((error) => { + reject(error); console.log(error); - const element = document.getElementsByClassName('draggerWrap'); - element && element[0]?.scrollIntoView({ block: 'center', behavior: 'smooth' }); }); - } else { - if (values?.draggerWrap) { - if (values?.dragger && values?.dragger?.length == 0) eventObj['image'] = null; - else eventObj['image'] = imageCrop; } - - addUpdateEventApiHandler(eventObj, toggle) - .then((id) => resolve(id)) + }) + .catch((error) => { + console.log(error); + reject(error); + setShowDialog(previousShowDialog); + message.warning({ + duration: 10, + maxCount: 1, + key: 'event-save-as-warning', + content: ( + <> + {t('dashboard.events.addEditEvent.validations.errorDraft')}   +
+ + {languageLiteralBannerDisplayStatus && ( + + + + + + { + dispatch(setLanguageLiteralBannerDisplayStatus(false)); + dispatch(clearActiveFallbackFieldsInfo({})); + }} + /> + } + /> + + + + + + )} {fields?.map((section, index) => { @@ -911,7 +976,11 @@ function CreateNewOrganization() { +

diff --git a/src/pages/Dashboard/CreateNewPerson/CreateNewPerson.jsx b/src/pages/Dashboard/CreateNewPerson/CreateNewPerson.jsx index 00a1da752..96eb48f8a 100644 --- a/src/pages/Dashboard/CreateNewPerson/CreateNewPerson.jsx +++ b/src/pages/Dashboard/CreateNewPerson/CreateNewPerson.jsx @@ -10,6 +10,7 @@ import Icon, { import { useTranslation } from 'react-i18next'; import { useLocation, useNavigate, useOutletContext, useParams, useSearchParams } from 'react-router-dom'; import { LeftOutlined } from '@ant-design/icons'; +import OutlinedButton from '../../..//components/Button/Outlined'; import PrimaryButton from '../../../components/Button/Primary'; import { ReactComponent as OrganizationLogo } from '../../../assets/icons/organization-light.svg'; import { featureFlags } from '../../../utils/featureFlags'; @@ -17,7 +18,7 @@ import FeatureFlag from '../../../layout/FeatureFlag/FeatureFlag'; import { entitiesClass } from '../../../constants/entitiesClass'; import Card from '../../../components/Card/Common/Event'; import { formCategory, formFieldValue, returnFormDataWithFields } from '../../../constants/formFields'; -import { useSelector } from 'react-redux'; +import { useDispatch, useSelector } from 'react-redux'; import { getUserDetails } from '../../../redux/reducer/userSlice'; import { bilingual, contentLanguageBilingual } from '../../../utils/bilingual'; import { taxonomyClass } from '../../../constants/taxonomyClass'; @@ -41,13 +42,21 @@ import { Prompt, usePrompt } from '../../../hooks/usePrompt'; import { getExternalSourceId } from '../../../utils/getExternalSourceId'; import { useGetEntitiesByIdQuery, useLazyGetEntityDependencyDetailsQuery } from '../../../services/entities'; import { sameAsTypes } from '../../../constants/sameAsTypes'; -import SelectionItem from '../../../components/List/SelectionItem'; import moment from 'moment'; +import SelectionItem from '../../../components/List/SelectionItem'; +import { + clearActiveFallbackFieldsInfo, + getActiveFallbackFieldsInfo, + getLanguageLiteralBannerDisplayStatus, + setLanguageLiteralBannerDisplayStatus, +} from '../../../redux/reducer/languageLiteralSlice'; +import Alert from '../../../components/Alert'; function CreateNewPerson() { const timestampRef = useRef(Date.now()).current; const [form] = Form.useForm(); const { t } = useTranslation(); + const dispatch = useDispatch(); const navigate = useNavigate(); const location = useLocation(); const [ @@ -59,6 +68,8 @@ function CreateNewPerson() { isReadOnly, ] = useOutletContext(); setContentBackgroundColor('#F9FAFF'); + const languageLiteralBannerDisplayStatus = useSelector(getLanguageLiteralBannerDisplayStatus); + const activeFallbackFieldsInfo = useSelector(getActiveFallbackFieldsInfo); const { user } = useSelector(getUserDetails); const { calendarId } = useParams(); let [searchParams] = useSearchParams(); @@ -346,6 +357,31 @@ function CreateNewPerson() { }); }; + useEffect(() => { + dispatch(clearActiveFallbackFieldsInfo()); + }, []); + + useEffect(() => { + let shouldDisplay = true; + + for (let key in activeFallbackFieldsInfo) { + if (Object.prototype.hasOwnProperty.call(activeFallbackFieldsInfo, key)) { + const tagDisplayStatus = + activeFallbackFieldsInfo[key]?.en?.tagDisplayStatus || activeFallbackFieldsInfo[key]?.fr?.tagDisplayStatus; + if (tagDisplayStatus) { + shouldDisplay = false; + break; + } + } + } + + if (!shouldDisplay) { + dispatch(setLanguageLiteralBannerDisplayStatus(true)); + } else { + dispatch(setLanguageLiteralBannerDisplayStatus(false)); + } + }, [activeFallbackFieldsInfo]); + useEffect(() => { if (calendarId && currentCalendarData) { if (personData) { @@ -500,6 +536,35 @@ function CreateNewPerson() {
+ + {languageLiteralBannerDisplayStatus && ( + + + + + + { + dispatch(setLanguageLiteralBannerDisplayStatus(false)); + dispatch(clearActiveFallbackFieldsInfo({})); + }} + /> + } + /> + + + + + + )} {fields?.map((section, index) => { @@ -520,7 +585,11 @@ function CreateNewPerson() { { + dispatch(clearActiveFallbackFieldsInfo()); + }, []); + + useEffect(() => { + let shouldDisplay = true; + for (let key in activeFallbackFieldsInfo) { + if (Object.prototype.hasOwnProperty.call(activeFallbackFieldsInfo, key)) { + const tagDisplayStatus = + activeFallbackFieldsInfo[key]?.en?.tagDisplayStatus || activeFallbackFieldsInfo[key]?.fr?.tagDisplayStatus; + if (tagDisplayStatus) { + shouldDisplay = false; + break; + } + } + } + + if (!shouldDisplay) { + dispatch(setLanguageLiteralBannerDisplayStatus(true)); + } else { + dispatch(setLanguageLiteralBannerDisplayStatus(false)); + } + }, [activeFallbackFieldsInfo]); + useEffect(() => { if (selectedContainsPlaces) form.setFieldValue(formFieldNames.CONTAINS_PLACE, selectedContainsPlaces); }, [selectedContainsPlaces]); @@ -1051,6 +1086,34 @@ function CreateNewPlace() { + {languageLiteralBannerDisplayStatus && ( + + + + + + { + dispatch(setLanguageLiteralBannerDisplayStatus(false)); + dispatch(clearActiveFallbackFieldsInfo({})); + }} + /> + } + /> + + + + + + )} @@ -1064,7 +1127,11 @@ function CreateNewPlace() { )} - + - + - + - + - + - + - + - + { + state.status = action.payload; + }, + setActiveFallbackFieldsInfo: (state, action) => { + if (action.payload?.method == 'remove') state.activeFallbackFieldsInfo = action.payload.data; + else state.activeFallbackFieldsInfo = { ...state.activeFallbackFieldsInfo, ...action.payload.data }; + }, + clearActiveFallbackFieldsInfo: (state) => { + state.activeFallbackFieldsInfo = {}; + }, + }, +}); + +export const { setLanguageLiteralBannerDisplayStatus, setActiveFallbackFieldsInfo, clearActiveFallbackFieldsInfo } = + languageLiteralSlice.actions; + +export const getLanguageLiteralBannerDisplayStatus = (state) => state?.languageLiteral?.status; +export const getActiveFallbackFieldsInfo = (state) => state?.languageLiteral?.activeFallbackFieldsInfo; + +export default languageLiteralSlice.reducer; diff --git a/src/redux/store.js b/src/redux/store.js index f0c22be37..13a117978 100644 --- a/src/redux/store.js +++ b/src/redux/store.js @@ -19,6 +19,7 @@ import { peopleApi } from '../services/people'; import { postalAddressApi } from '../services/postalAddress'; import ErrorSliceReducer from './reducer/ErrorSlice'; import { externalSourceApi } from '../services/externalSource'; +import languageLiteralReducer from './reducer/languageLiteralSlice'; // import localforage from 'localforage'; const persistConfig = { @@ -46,6 +47,7 @@ const appReducer = combineReducers({ interfaceLanguage: interfaceLanguageReducer, selectedCalendar: selectedCalendarReducer, errors: ErrorSliceReducer, + languageLiteral: languageLiteralReducer, [loginApi.reducerPath]: loginApi.reducer, [usersApi.reducerPath]: usersApi.reducer, [eventsApi.reducerPath]: eventsApi.reducer, diff --git a/src/utils/languageFallbackSetup.js b/src/utils/languageFallbackSetup.js new file mode 100644 index 000000000..3ebe18847 --- /dev/null +++ b/src/utils/languageFallbackSetup.js @@ -0,0 +1,92 @@ +export function languageFallbackSetup({ currentCalendarData, fieldData, languageFallbacks = {}, isFieldsDirty }) { + let results = {}; + + if (!fieldData || Object.keys(languageFallbacks).length == 0 || Object.keys(fieldData).length == 0) return results; + + const fallbackLiteralKeysEn = languageFallbacks?.en; + const fallbackLiteralKeysFr = languageFallbacks?.fr; + + if (currentCalendarData.contentLanguage === 'BILINGUAL') { + if (!Object.hasOwnProperty.call(fieldData, 'en')) { + const fallbackInfo = fallbackLiteralKeysEn?.find((key) => Object.hasOwnProperty.call(fieldData, key)); + const fallbackErrorHandled = fallbackInfo + ? { key: fallbackInfo, value: fieldData[fallbackInfo] } + : Object.keys(fieldData).length > 0 + ? { key: '?', value: fieldData[Object.keys(fieldData)[0]] } + : { key: null, value: null }; + + results['en'] = { + tagDisplayStatus: !isFieldsDirty?.en ? true : false, + fallbackLiteralKey: fallbackErrorHandled?.key, + fallbackLiteralValue: fallbackErrorHandled?.value, + }; + } + + if (!Object.hasOwnProperty.call(fieldData, 'fr')) { + const fallbackInfo = fallbackLiteralKeysFr?.find((key) => Object.hasOwnProperty.call(fieldData, key)); + const fallbackErrorHandled = fallbackInfo + ? { key: fallbackInfo, value: fieldData[fallbackInfo] } + : Object.keys(fieldData).length > 0 + ? { key: '?', value: fieldData[Object.keys(fieldData)[0]] } + : { key: null, value: null }; + + results['fr'] = { + tagDisplayStatus: !isFieldsDirty?.fr ? true : false, + fallbackLiteralKey: fallbackErrorHandled?.key, + fallbackLiteralValue: fallbackErrorHandled?.value, + }; + } + } else if (currentCalendarData.contentLanguage === 'FRENCH') { + if (!Object.hasOwnProperty.call(fieldData, 'fr')) { + const fallbackInfo = fallbackLiteralKeysFr?.find((key) => Object.hasOwnProperty.call(fieldData, key)); + const fallbackErrorHandled = fallbackInfo + ? { key: fallbackInfo, value: fieldData[fallbackInfo] } + : Object.keys(fieldData).length > 0 + ? { key: '?', value: fieldData[Object.keys(fieldData)[0]] } + : { key: null, value: null }; + + results['fr'] = { + tagDisplayStatus: !isFieldsDirty?.fr ? true : false, + fallbackLiteralKey: fallbackErrorHandled?.key, + fallbackLiteralValue: fallbackErrorHandled?.value, + }; + } else { + const fallbackInfo = fallbackLiteralKeysEn?.find((key) => Object.hasOwnProperty.call(fieldData, key)); + + const fallbackErrorHandled = fallbackInfo + ? { key: fallbackInfo, value: fieldData[fallbackInfo] } + : Object.keys(fieldData).length > 0 + ? { key: '?', value: fieldData[Object.keys(fieldData)[0]] } + : { key: null, value: null }; + + results['fr'] = { + tagDisplayStatus: false, + fallbackLiteralKey: fallbackErrorHandled?.key, + fallbackLiteralValue: fallbackErrorHandled?.value, + }; + } + } else { + if (!Object.hasOwnProperty.call(fieldData, 'en')) { + const fallbackInfo = fallbackLiteralKeysEn?.find((key) => Object.hasOwnProperty.call(fieldData, key)); + const fallbackErrorHandled = fallbackInfo + ? { key: fallbackInfo, value: fieldData[fallbackInfo] } + : Object.keys(fieldData).length > 0 + ? { key: '?', value: fieldData[Object.keys(fieldData)[0]] } + : { key: null, value: null }; + + results['en'] = { + tagDisplayStatus: !isFieldsDirty?.en ? true : false, + fallbackLiteralKey: fallbackErrorHandled?.key, + fallbackLiteralValue: fallbackErrorHandled?.value, + }; + } else { + results['en'] = { + tagDisplayStatus: false, + fallbackLiteralKey: '', + fallbackLiteralValue: '', + }; + } + } + + return results; +} diff --git a/src/utils/removeUneditedFallbackValues.js b/src/utils/removeUneditedFallbackValues.js new file mode 100644 index 000000000..6308f900b --- /dev/null +++ b/src/utils/removeUneditedFallbackValues.js @@ -0,0 +1,11 @@ +export const removeUneditedFallbackValues = ({ values, activeFallbackFieldsInfo, fieldName, property }) => { + if (values == '' || !values) { + return; + } + if (!Object.prototype.hasOwnProperty.call(activeFallbackFieldsInfo, fieldName)) { + return values; + } + if (activeFallbackFieldsInfo[fieldName][property]?.tagDisplayStatus) { + return; + } else return values; +};