diff --git a/src/components/events/Series.tsx b/src/components/events/Series.tsx index 81155790ae..4dcce7f070 100644 --- a/src/components/events/Series.tsx +++ b/src/components/events/Series.tsx @@ -33,6 +33,7 @@ import { fetchSeriesThemes, showActionsSeries, } from "../../slices/seriesSlice"; +import { fetchSeriesDetailsTobiraNew } from "../../slices/seriesSlice"; // References for detecting a click outside of the container of the dropdown menu const containerAction = React.createRef(); @@ -127,6 +128,7 @@ const Series = () => { const showNewSeriesModal = async () => { await dispatch(fetchSeriesMetadata()); await dispatch(fetchSeriesThemes()); + await dispatch(fetchSeriesDetailsTobiraNew("/")); setNewSeriesModal(true); }; diff --git a/src/components/events/partials/ModalTabsAndPages/NewTobiraPage.tsx b/src/components/events/partials/ModalTabsAndPages/NewTobiraPage.tsx new file mode 100644 index 0000000000..e06a87f27f --- /dev/null +++ b/src/components/events/partials/ModalTabsAndPages/NewTobiraPage.tsx @@ -0,0 +1,398 @@ +import React, { useEffect, useState } from "react"; +import { useTranslation } from "react-i18next"; +import WizardNavigationButtons from "../../../shared/wizard/WizardNavigationButtons"; +import { FormikProps } from "formik"; +import Notifications from "../../../shared/Notifications"; +import { OurNotification, addNotification, removeNotificationByKey, removeNotificationWizardForm } from "../../../../slices/notificationSlice"; +import { useAppDispatch, useAppSelector } from "../../../../store"; +import { TobiraPage, fetchSeriesDetailsTobiraNew, setErrorTobiraPage, setTobiraPage } from "../../../../slices/seriesSlice"; +import { getSeriesTobiraPage, getSeriesTobiraPageError } from "../../../../selectors/seriesSeletctor"; +import { NOTIFICATION_CONTEXT } from "../../../../configs/modalConfig"; + +/** + * This component renders the theme page for new series in the new series wizard. + */ +type RequiredFormProps = { + breadcrumbs: TobiraPage[], + selectedPage: TobiraPage | undefined, +} + +const NewTobiraPage = ({ + formik, + nextPage, + previousPage, +}: { + formik: FormikProps, + nextPage: (values: T) => void, + previousPage: any //(values: T) => void, +}) => { + const { t } = useTranslation(); + const dispatch = useAppDispatch(); + + const [isValid, setIsValid] = useState(false); + const [editing, setEditing] = useState(false); + + const error = useAppSelector(state => getSeriesTobiraPageError(state)); + const currentPage = useAppSelector(state => getSeriesTobiraPage(state)); + + // Check if valid + useEffect(() => { + function check( + type: OurNotification["type"], + key: OurNotification["key"], + context: OurNotification["context"], + callback: () => boolean + ) { + var toggle = callback(); + if (toggle) { + dispatch(addNotification({ + type: type, + key: key, + duration: -1, + parameter: null, + context: context, + noDuplicates: true, + })); + } else { + dispatch(removeNotificationByKey({key, context})); + } + + if (toggle && type !== 'info') { + return false; + } + + return true; + } + + var valid = true; + + valid = valid && check('info', 'TOBIRA_OVERRIDE_NAME', NOTIFICATION_CONTEXT, function () { + return !!formik.values.selectedPage && !!formik.values.selectedPage.title; + }); + + if (!editing) { + dispatch(removeNotificationWizardForm()); + setIsValid(valid); + return; + } + + if (currentPage.children.length === 0) { + setIsValid(valid); + return; + } + var newPage = currentPage.children[currentPage.children.length - 1]; + + valid = valid && check('warning', 'TOBIRA_NO_PATH_SEGMENT', NOTIFICATION_CONTEXT, function () { + return !newPage.segment; + }); + + valid = valid && check('warning', 'TOBIRA_PATH_SEGMENT_INVALID', NOTIFICATION_CONTEXT, function () { + return !!newPage.segment && (newPage.segment.length <= 1 || [ + // eslint-disable-next-line no-control-regex + /[\u0000-\u001F\u007F-\u009F]/u, + /[\u00A0\u1680\u2000-\u200A\u2028\u2029\u202F\u205F\u3000]/u, + /[<>"[\\\]^`{|}#%/?]/u, + /^[-+~@_!$&;:.,=*'()]/u + ].some(function (regex) { + return regex.test(newPage.segment); + })); + }); + + valid = valid && check('warning', 'TOBIRA_PATH_SEGMENT_UNIQUE', NOTIFICATION_CONTEXT, function () { + return currentPage.children.some(function (child) { + return child !== newPage && child.segment === newPage.segment; + }); + }); + + setIsValid(valid); + }, [currentPage.children, dispatch, editing, formik.values.selectedPage]) + + const back = (index: number) => { + goto(formik.values.breadcrumbs.splice(index)[0]); + } + + const select = (page: TobiraPage | undefined) => { + if (!page || !page.new) { + stopEditing(); + } + if (!page || formik.values.selectedPage === page) { + formik.setFieldValue("selectedPage", undefined); + } else { + formik.setFieldValue("selectedPage", page); + } + } + + const updatePath = (page: TobiraPage, lastSegment: string) => { + return formik.values.breadcrumbs + .concat(page).map(function (page) { + return page.segment; + }) + .join('/') + /* eslint-disable */ + .replace(/([^\/]+$)/, lastSegment); + } + + const goto = (page: TobiraPage) => { + function goto(page: TobiraPage) { + select(undefined); + // setCurrentPage(page); + dispatch(setTobiraPage(page)); + formik.setFieldValue("breadcrumbs", [...formik.values.breadcrumbs, page]); + } + + // clearNotifications('series-tobira'); + dispatch(removeNotificationWizardForm()); + setErrorTobiraPage(null); + + if (page.new) { + goto(page) + } else { + //fetch tobira resource + dispatch(fetchSeriesDetailsTobiraNew(page.path)) + } + } + + useEffect(() => { + // After changing to another page + if ( formik.values.breadcrumbs.length === 0 || + (formik.values.breadcrumbs.length > 0 && + formik.values.breadcrumbs[formik.values.breadcrumbs.length - 1].path !== currentPage.path) + ) { + select(undefined); + formik.setFieldValue("breadcrumbs", [...formik.values.breadcrumbs, currentPage]); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [currentPage]); + + const addChild = () => { + setEditing(true); + var newPage: TobiraPage = { new: true, children: [], + title: "", + path: "", + subpages: "", + blocks: [], + segment: "", + }; + dispatch(setTobiraPage({ ...currentPage, children: [...currentPage.children, newPage]})); + select(newPage); + } + + const stopEditing = () => { + if (editing) { + dispatch(setTobiraPage({ + ...currentPage, + children: currentPage.children.filter((_, idx) => idx !== currentPage.children.length - 1) + })); + } + setEditing(false); + }; + + return ( + <> +
+
+ {/* Notifications */} + +
+
+
+ {t("EVENTS.SERIES.NEW.TOBIRA.CAPTION")} +
+
+
    +
  • +

    {t("EVENTS.SERIES.NEW.TOBIRA.DESCRIPTION")}

    +
  • +
+
+ { !error && ( + <> +
+
+
+ {t("EVENTS.SERIES.NEW.TOBIRA.SELECT_PAGE")} +
+
+ {!!formik.values.breadcrumbs && formik.values.breadcrumbs.map((breadcrumb, key) => ( + + ))} +
+ + + + + + + + + + {!!currentPage.children && + currentPage.children.map((page, key) => ( + + + + + { editing && + + } + + ))} + { !editing && + + + + } + +
+ { + t( + "EVENTS.SERIES.NEW.TOBIRA.PAGE_TITLE" + ) /* Title */ + } + + { + t( + "EVENTS.SERIES.NEW.TOBIRA.PATH_SEGMENT" + ) /* Path segment */ + } + + { + t( + "EVENTS.SERIES.NEW.TOBIRA.SUBPAGES" + ) /* Subpages */ + } +
+ {!!page.new ? ( + + dispatch(setTobiraPage({ + ...currentPage, + children: currentPage.children.map((p, k) => { + if (k === key) { + let newPage = { + ...p, + title: e.target.value, + } + + formik.setFieldValue("selectedPage", newPage) + + return newPage; + } + return {...p}; + }) + })) + } + > + + ) : ( + + )} + + + {!!page.new ? ( + { + const payload = { + ...currentPage, + children: currentPage.children.map((p, k) => { + if (k === key) { + let newPage = { + ...p, + path: updatePath(p, e.target.value), + segment: e.target.value + } + + console.log(newPage.path) + console.log(e.target.value) + + formik.setFieldValue("selectedPage", newPage) + + return newPage; + } + return {...p}; + }) + } + dispatch(setTobiraPage(payload)) + }} + > + ) : ( + {page.segment} + )} + + + {((!page.new || isValid) && page.title) && + + } + + { page.new && + + } +
+ +
+
+
+ +
+ { (!!formik.values.selectedPage && isValid) ? ( +

+ {t('EVENTS.SERIES.NEW.TOBIRA.SELECTED_PAGE')}: + + {formik.values.selectedPage.path} + +

+ ) : ( +

+ {t("EVENTS.SERIES.NEW.TOBIRA.NO_PAGE_SELECTED")} +

+ )} +
+ + )} +
+
+
+
+ + {/* Button for navigation to next page and previous page */} + + + ); +}; + +export default NewTobiraPage; diff --git a/src/components/events/partials/ModalTabsAndPages/SeriesDetailsTobiraTab.tsx b/src/components/events/partials/ModalTabsAndPages/SeriesDetailsTobiraTab.tsx new file mode 100644 index 0000000000..97cc964870 --- /dev/null +++ b/src/components/events/partials/ModalTabsAndPages/SeriesDetailsTobiraTab.tsx @@ -0,0 +1,132 @@ +import { useTranslation } from "react-i18next"; +import Notifications from "../../../shared/Notifications"; +import { useAppDispatch, useAppSelector } from "../../../../store"; +import { getSeriesDetailsTobiraData, getSeriesDetailsTobiraDataError } from "../../../../selectors/seriesDetailsSelectors"; +import { addNotification } from "../../../../slices/notificationSlice"; +import { NOTIFICATION_CONTEXT } from "../../../../configs/modalConfig"; + +/** + * This component renders the theme page for new series in the new series wizard. + */ +const SeriesDetailsTobiraTab = ({ + seriesId +}: { + seriesId: string, +}) => { + const { t } = useTranslation(); + const dispatch = useAppDispatch(); + + const tobiraData = useAppSelector(state => getSeriesDetailsTobiraData(state)); + const error = useAppSelector(state => getSeriesDetailsTobiraDataError(state)); + + const directTobiraLink = tobiraData.baseURL + '/!s/:' + seriesId; + + const copyTobiraDirectLink = () => { + navigator.clipboard.writeText(directTobiraLink).then(function () { + dispatch(addNotification({ + type: "info", + key: "TOBIRA_COPIED_DIRECT_LINK", + duration: 3000, + parameter: null, + context: NOTIFICATION_CONTEXT + })); + }, function () { + dispatch(addNotification({ + type: "error", + key: "TOBIRA_FAILED_COPYING_DIRECT_LINK", + duration: 3000, + parameter: null, + context: NOTIFICATION_CONTEXT + })); + }); + } + + return ( +
+
+ {/* Notifications */} + +
+
+
+ {t("EVENTS.SERIES.DETAILS.TABS.TOBIRA")} +
+ { !error && + <> +
+ + {t("EVENTS.SERIES.DETAILS.TOBIRA.DIRECT_LINK")} + +