diff --git a/.env.example b/.env.example
index b71ae142b..1a059072b 100644
--- a/.env.example
+++ b/.env.example
@@ -17,6 +17,8 @@ SPONSOR_USERS_API_SCOPES="show-medata/read show-medata/write access-requests/rea
EMAIL_SCOPES="clients/read templates/read templates/write emails/read"
FILE_UPLOAD_SCOPES="files/upload"
SCOPES="profile openid offline_access ${SPONSOR_USERS_API_SCOPES} ${PURCHASES_API_SCOPES} ${EMAIL_SCOPES} ${FILE_UPLOAD_SCOPES} ${SCOPES_BASE_REALM}/summits/delete-event ${SCOPES_BASE_REALM}/summits/write ${SCOPES_BASE_REALM}/summits/write-event ${SCOPES_BASE_REALM}/summits/read/all ${SCOPES_BASE_REALM}/summits/read ${SCOPES_BASE_REALM}/summits/publish-event ${SCOPES_BASE_REALM}/members/read ${SCOPES_BASE_REALM}/members/read/me ${SCOPES_BASE_REALM}/speakers/write ${SCOPES_BASE_REALM}/attendees/write ${SCOPES_BASE_REALM}/members/write ${SCOPES_BASE_REALM}/organizations/write ${SCOPES_BASE_REALM}/organizations/read ${SCOPES_BASE_REALM}/summits/write-presentation-materials ${SCOPES_BASE_REALM}/summits/registration-orders/update ${SCOPES_BASE_REALM}/summits/registration-orders/delete ${SCOPES_BASE_REALM}/summits/registration-orders/create/offline ${SCOPES_BASE_REALM}/summits/badge-scans/read entity-updates/publish ${SCOPES_BASE_REALM}/audit-logs/read"
+SPONSOR_PAGES_API_URL=https://sponsor-pages-api.dev.fnopen.com
+SPONSOR_PAGES_SCOPES=page-template/read page-template/write
GOOGLE_API_KEY=
ALLOWED_USER_GROUPS="super-admins administrators summit-front-end-administrators summit-room-administrators track-chairs-admins sponsors"
APP_CLIENT_NAME = "openstack"
diff --git a/src/actions/page-template-actions.js b/src/actions/page-template-actions.js
new file mode 100644
index 000000000..363230881
--- /dev/null
+++ b/src/actions/page-template-actions.js
@@ -0,0 +1,242 @@
+/**
+ * Copyright 2024 OpenStack Foundation
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * */
+
+import T from "i18n-react/dist/i18n-react";
+import {
+ getRequest,
+ putRequest,
+ postRequest,
+ deleteRequest,
+ createAction,
+ stopLoading,
+ startLoading,
+ authErrorHandler,
+ escapeFilterValue
+} from "openstack-uicore-foundation/lib/utils/actions";
+import { getAccessTokenSafely } from "../utils/methods";
+import {
+ DEFAULT_CURRENT_PAGE,
+ DEFAULT_ORDER_DIR,
+ DEFAULT_PER_PAGE
+} from "../utils/constants";
+import { snackbarErrorHandler, snackbarSuccessHandler } from "./base-actions";
+
+export const ADD_PAGE_TEMPLATE = "ADD_PAGE_TEMPLATE";
+export const PAGE_TEMPLATE_ADDED = "PAGE_TEMPLATE_ADDED";
+export const PAGE_TEMPLATE_DELETED = "PAGE_TEMPLATE_DELETED";
+export const PAGE_TEMPLATE_UPDATED = "PAGE_TEMPLATE_UPDATED";
+export const RECEIVE_PAGE_TEMPLATE = "RECEIVE_PAGE_TEMPLATE";
+export const RECEIVE_PAGE_TEMPLATES = "RECEIVE_PAGE_TEMPLATES";
+export const REQUEST_PAGE_TEMPLATES = "REQUEST_PAGE_TEMPLATES";
+export const RESET_PAGE_TEMPLATE_FORM = "RESET_PAGE_TEMPLATE_FORM";
+export const UPDATE_PAGE_TEMPLATE = "UPDATE_PAGE_TEMPLATE";
+export const PAGE_TEMPLATE_ARCHIVED = "PAGE_TEMPLATE_ARCHIVED";
+export const PAGE_TEMPLATE_UNARCHIVED = "PAGE_TEMPLATE_UNARCHIVED";
+
+export const getPageTemplates =
+ (
+ term = null,
+ page = DEFAULT_CURRENT_PAGE,
+ perPage = DEFAULT_PER_PAGE,
+ order = "id",
+ orderDir = DEFAULT_ORDER_DIR,
+ hideArchived = false
+ ) =>
+ async (dispatch) => {
+ const accessToken = await getAccessTokenSafely();
+ const filter = [];
+
+ dispatch(startLoading());
+
+ if (term) {
+ const escapedTerm = escapeFilterValue(term);
+ filter.push(`name=@${escapedTerm},code=@${escapedTerm}`);
+ }
+
+ const params = {
+ page,
+ expand: "modules",
+ fields:
+ "id,code,name,modules,is_archived,modules.kind,modules.id,modules.content",
+ relations: "modules,modules.none",
+ per_page: perPage,
+ access_token: accessToken
+ };
+
+ if (hideArchived) filter.push("is_archived==0");
+
+ if (filter.length > 0) {
+ params["filter[]"] = filter;
+ }
+
+ // order
+ if (order != null && orderDir != null) {
+ const orderDirSign = orderDir === 1 ? "" : "-";
+ params.order = `${orderDirSign}${order}`;
+ }
+
+ return getRequest(
+ createAction(REQUEST_PAGE_TEMPLATES),
+ createAction(RECEIVE_PAGE_TEMPLATES),
+ `${window.SPONSOR_PAGES_API_URL}/api/v1/page-templates`,
+ authErrorHandler,
+ { order, orderDir, page, perPage, term, hideArchived }
+ )(params)(dispatch).then(() => {
+ dispatch(stopLoading());
+ });
+ };
+
+export const getPageTemplate = (formTemplateId) => async (dispatch) => {
+ const accessToken = await getAccessTokenSafely();
+
+ dispatch(startLoading());
+
+ const params = {
+ access_token: accessToken,
+ expand: "materials,meta_fields,meta_fields.values"
+ };
+
+ return getRequest(
+ null,
+ createAction(RECEIVE_PAGE_TEMPLATE),
+ `${window.SPONSOR_PAGES_API_URL}/api/v1/page-templates/${formTemplateId}`,
+ authErrorHandler
+ )(params)(dispatch).then(() => {
+ dispatch(stopLoading());
+ });
+};
+
+export const deletePageTemplate = (formTemplateId) => async (dispatch) => {
+ const accessToken = await getAccessTokenSafely();
+
+ dispatch(startLoading());
+
+ const params = {
+ access_token: accessToken
+ };
+
+ return deleteRequest(
+ null,
+ createAction(PAGE_TEMPLATE_DELETED)({ formTemplateId }),
+ `${window.SPONSOR_PAGES_API_URL}/api/v1/page-templates/${formTemplateId}`,
+ null,
+ authErrorHandler
+ )(params)(dispatch).then(() => {
+ dispatch(stopLoading());
+ });
+};
+
+export const resetPageTemplateForm = () => (dispatch) => {
+ dispatch(createAction(RESET_PAGE_TEMPLATE_FORM)({}));
+};
+
+const normalizeEntity = (entity) => {
+ const normalizedEntity = { ...entity };
+
+ normalizedEntity.modules = [];
+
+ return normalizedEntity;
+};
+
+export const savePageTemplate = (entity) => async (dispatch, getState) => {
+ const accessToken = await getAccessTokenSafely();
+ const params = {
+ access_token: accessToken
+ };
+
+ dispatch(startLoading());
+
+ const normalizedEntity = normalizeEntity(entity);
+
+ if (entity.id) {
+ return putRequest(
+ createAction(UPDATE_PAGE_TEMPLATE),
+ createAction(PAGE_TEMPLATE_UPDATED),
+ `${window.SPONSOR_PAGES_API_URL}/api/v1/page-templates/${entity.id}`,
+ normalizedEntity,
+ snackbarErrorHandler,
+ entity
+ )(params)(dispatch)
+ .then(() => {
+ dispatch(
+ snackbarSuccessHandler({
+ title: T.translate("general.success"),
+ html: T.translate("page_template_list.page_crud.page_saved")
+ })
+ );
+ getPageTemplates()(dispatch, getState);
+ })
+ .catch((err) => {
+ console.error(err);
+ })
+ .finally(() => {
+ dispatch(stopLoading());
+ });
+ }
+
+ return postRequest(
+ createAction(ADD_PAGE_TEMPLATE),
+ createAction(PAGE_TEMPLATE_ADDED),
+ `${window.SPONSOR_PAGES_API_URL}/api/v1/page-templates`,
+ normalizedEntity,
+ snackbarErrorHandler,
+ entity
+ )(params)(dispatch)
+ .then(() => {
+ dispatch(
+ snackbarSuccessHandler({
+ title: T.translate("general.success"),
+ html: T.translate("page_template_list.page_crud.page_created")
+ })
+ );
+ getPageTemplates()(dispatch, getState);
+ })
+ .catch((err) => {
+ console.error(err);
+ })
+ .finally(() => {
+ dispatch(stopLoading());
+ });
+};
+
+/* ************************************** ARCHIVE ************************************** */
+
+export const archivePageTemplate = (pageTemplateId) => async (dispatch) => {
+ const accessToken = await getAccessTokenSafely();
+ const params = { access_token: accessToken };
+
+ return putRequest(
+ null,
+ createAction(PAGE_TEMPLATE_ARCHIVED),
+ `${window.SPONSOR_PAGES_API_URL}/api/v1/page-templates/${pageTemplateId}/archive`,
+ null,
+ snackbarErrorHandler
+ )(params)(dispatch);
+};
+
+export const unarchivePageTemplate = (pageTemplateId) => async (dispatch) => {
+ const accessToken = await getAccessTokenSafely();
+ const params = { access_token: accessToken };
+
+ dispatch(startLoading());
+
+ return deleteRequest(
+ null,
+ createAction(PAGE_TEMPLATE_UNARCHIVED)({ pageTemplateId }),
+ `${window.SPONSOR_PAGES_API_URL}/api/v1/page-templates/${pageTemplateId}/archive`,
+ null,
+ snackbarErrorHandler
+ )(params)(dispatch).then(() => {
+ dispatch(stopLoading());
+ });
+};
diff --git a/src/app.js b/src/app.js
index edf4be49d..eb5b5e2b2 100644
--- a/src/app.js
+++ b/src/app.js
@@ -81,6 +81,7 @@ window.MARKETING_API_BASE_URL = process.env.MARKETING_API_BASE_URL;
window.EMAIL_API_BASE_URL = process.env.EMAIL_API_BASE_URL;
window.PURCHASES_API_URL = process.env.PURCHASES_API_URL;
window.SPONSOR_USERS_API_URL = process.env.SPONSOR_USERS_API_URL;
+window.SPONSOR_PAGES_API_URL = process.env.SPONSOR_PAGES_API_URL;
window.FILE_UPLOAD_API_BASE_URL = process.env.FILE_UPLOAD_API_BASE_URL;
window.SIGNAGE_BASE_URL = process.env.SIGNAGE_BASE_URL;
window.INVENTORY_API_BASE_URL = process.env.INVENTORY_API_BASE_URL;
diff --git a/src/components/menu/index.js b/src/components/menu/index.js
index 61177a4a4..83e492498 100644
--- a/src/components/menu/index.js
+++ b/src/components/menu/index.js
@@ -61,6 +61,10 @@ const getGlobalItems = () => [
{
name: "form_templates",
linkUrl: "form-templates"
+ },
+ {
+ name: "page_templates",
+ linkUrl: "page-templates"
}
]
},
diff --git a/src/i18n/en.json b/src/i18n/en.json
index 8a04b78c8..8993da7e9 100644
--- a/src/i18n/en.json
+++ b/src/i18n/en.json
@@ -187,7 +187,8 @@
"submission_invitations": "Submission Invitations",
"sponsors_inventory": "Sponsors",
"form_templates": "Form Templates",
- "inventory": "Inventory"
+ "inventory": "Inventory",
+ "page_templates": "Pages"
},
"schedule": {
"schedule": "Schedule",
@@ -3828,5 +3829,35 @@
"seat_type": "Select a Seat Type",
"status": "Select a Status"
}
+ },
+ "page_template_list": {
+ "page_templates": "Page Templates",
+ "alert_info": "You can create or archive Pages from the list. To edit a Page click on the item's Edit botton.",
+ "code": "Code",
+ "name": "Name",
+ "info_mod": "Info Mod",
+ "upload_mod": "Upload Mod",
+ "download_mod": "Download Mod",
+ "hide_archived": "Hide archived pages",
+ "no_pages": "No pages found.",
+ "add_new": "New Page",
+ "add_template": "Using Template",
+ "delete_form_template_warning": "Are you sure you want to delete form template ",
+ "using_duplicate": "Using Duplicate",
+ "add_form_template": "New Form",
+ "add_using_global_template": "Using Global Template",
+ "placeholders": {
+ "search": "Search"
+ },
+ "page_crud": {
+ "title": "Create New Page",
+ "add_info": "Add Info",
+ "add_doc": "Add Document Download",
+ "add_media": "Add Media Request",
+ "no_modules": "No modules added yet.",
+ "save": "Save Page",
+ "page_saved": "Page saved successfully.",
+ "page_created": "Page created successfully."
+ }
}
}
diff --git a/src/layouts/form-template-item-layout.js b/src/layouts/form-template-item-layout.js
index 2ee8c4a40..f3526ebde 100644
--- a/src/layouts/form-template-item-layout.js
+++ b/src/layouts/form-template-item-layout.js
@@ -16,8 +16,8 @@ import { Switch, Route, withRouter } from "react-router-dom";
import T from "i18n-react/dist/i18n-react";
import { Breadcrumb } from "react-breadcrumbs";
import Restrict from "../routes/restrict";
-import EditFormTemplateItemPage from "../pages/sponsors_inventory/edit-form-template-item-page";
-import FormTemplateItemListPage from "../pages/sponsors_inventory/form-template-item-list-page";
+import EditFormTemplateItemPage from "../pages/sponsors-global/form-templates/edit-form-template-item-page";
+import FormTemplateItemListPage from "../pages/sponsors-global/form-templates/form-template-item-list-page";
import NoMatchPage from "../pages/no-match-page";
const FormTemplateItemLayout = ({ match }) => (
diff --git a/src/layouts/form-template-layout.js b/src/layouts/form-template-layout.js
index ead05f5d2..7a9f037ad 100644
--- a/src/layouts/form-template-layout.js
+++ b/src/layouts/form-template-layout.js
@@ -16,8 +16,8 @@ import { Switch, Route, withRouter } from "react-router-dom";
import T from "i18n-react/dist/i18n-react";
import { Breadcrumb } from "react-breadcrumbs";
import Restrict from "../routes/restrict";
-import FormTemplateListPage from "../pages/sponsors_inventory/form-template-list-page";
-import EditFormTemplatePage from "../pages/sponsors_inventory/edit-form-template-page";
+import FormTemplateListPage from "../pages/sponsors-global/form-templates/form-template-list-page";
+import EditFormTemplatePage from "../pages/sponsors-global/form-templates/edit-form-template-page";
import FormTemplateItemLayout from "./form-template-item-layout";
import NoMatchPage from "../pages/no-match-page";
diff --git a/src/layouts/inventory-item-layout.js b/src/layouts/inventory-item-layout.js
index 025166cd3..b012fbe99 100644
--- a/src/layouts/inventory-item-layout.js
+++ b/src/layouts/inventory-item-layout.js
@@ -16,8 +16,8 @@ import { Switch, Route, withRouter } from "react-router-dom";
import T from "i18n-react/dist/i18n-react";
import { Breadcrumb } from "react-breadcrumbs";
import Restrict from "../routes/restrict";
-import InventoryListPage from "../pages/sponsors_inventory/inventory-list-page";
-import EditInventoryItemPage from "../pages/sponsors_inventory/edit-inventory-item-page";
+import InventoryListPage from "../pages/sponsors-global/inventory/inventory-list-page";
+import EditInventoryItemPage from "../pages/sponsors-global/inventory/edit-inventory-item-page";
import NoMatchPage from "../pages/no-match-page";
const InventoryItemLayout = ({ match }) => (
diff --git a/src/layouts/page-template-layout.js b/src/layouts/page-template-layout.js
new file mode 100644
index 000000000..445381daa
--- /dev/null
+++ b/src/layouts/page-template-layout.js
@@ -0,0 +1,55 @@
+/**
+ * Copyright 2024 OpenStack Foundation
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * */
+
+import React from "react";
+import { Switch, Route, withRouter } from "react-router-dom";
+import T from "i18n-react/dist/i18n-react";
+import { Breadcrumb } from "react-breadcrumbs";
+import Restrict from "../routes/restrict";
+import NoMatchPage from "../pages/no-match-page";
+import EditPageTemplatePage from "../pages/sponsors-global/page-templates/edit-page-template-page";
+import PageTemplateListPage from "../pages/sponsors-global/page-templates/page-template-list-page";
+
+const PageTemplateLayout = ({ match }) => (
+
+
+
+
+
+
+
+
+
+);
+
+export default Restrict(withRouter(PageTemplateLayout), "page-template");
diff --git a/src/layouts/primary-layout.js b/src/layouts/primary-layout.js
index 29f46f0b4..086398890 100644
--- a/src/layouts/primary-layout.js
+++ b/src/layouts/primary-layout.js
@@ -29,6 +29,7 @@ import MediaFileTypeLayout from "./media-file-type-layout";
import SponsoredProjectLayout from "./sponsored-project-layout";
import TagLayout from "./tag-layout";
import SponsorshipLayout from "./sponsorship-layout";
+import PageTemplateLayout from "./page-template-layout";
const PrimaryLayout = ({ match, currentSummit, location, member }) => {
let extraClass = "container";
@@ -65,6 +66,7 @@ const PrimaryLayout = ({ match, currentSummit, location, member }) => {
+
{
const {
diff --git a/src/pages/sponsors_inventory/edit-form-template-page.js b/src/pages/sponsors-global/form-templates/edit-form-template-page.js
similarity index 95%
rename from src/pages/sponsors_inventory/edit-form-template-page.js
rename to src/pages/sponsors-global/form-templates/edit-form-template-page.js
index 495987f62..f026badd7 100644
--- a/src/pages/sponsors_inventory/edit-form-template-page.js
+++ b/src/pages/sponsors-global/form-templates/edit-form-template-page.js
@@ -15,7 +15,7 @@ import React, { useEffect } from "react";
import { connect } from "react-redux";
import { Breadcrumb } from "react-breadcrumbs";
import T from "i18n-react/dist/i18n-react";
-import FormTemplateForm from "../../components/forms/form-template-form";
+import FormTemplateForm from "../../../components/forms/form-template-form";
import {
getFormTemplate,
resetFormTemplateForm,
@@ -23,7 +23,7 @@ import {
deleteFormTemplateMetaFieldType,
deleteFormTemplateMetaFieldTypeValue,
deleteFormTemplateMaterial
-} from "../../actions/form-template-actions";
+} from "../../../actions/form-template-actions";
const EditFormTemplatePage = (props) => {
const {
diff --git a/src/pages/sponsors_inventory/popup/form-template-from-duplicate-popup.js b/src/pages/sponsors-global/form-templates/form-template-from-duplicate-popup.js
similarity index 100%
rename from src/pages/sponsors_inventory/popup/form-template-from-duplicate-popup.js
rename to src/pages/sponsors-global/form-templates/form-template-from-duplicate-popup.js
diff --git a/src/pages/sponsors_inventory/form-template-item-list-page.js b/src/pages/sponsors-global/form-templates/form-template-item-list-page.js
similarity index 95%
rename from src/pages/sponsors_inventory/form-template-item-list-page.js
rename to src/pages/sponsors-global/form-templates/form-template-item-list-page.js
index 9eebcc662..16b6acf04 100644
--- a/src/pages/sponsors_inventory/form-template-item-list-page.js
+++ b/src/pages/sponsors-global/form-templates/form-template-item-list-page.js
@@ -27,7 +27,7 @@ import AddIcon from "@mui/icons-material/Add";
import IconButton from "@mui/material/IconButton";
import Tooltip from "@mui/material/Tooltip";
import ImageIcon from "@mui/icons-material/Image";
-import MuiTable from "../../components/mui/table/mui-table";
+import MuiTable from "../../../components/mui/table/mui-table";
import {
cloneFromInventoryItem,
deleteFormTemplateItem,
@@ -39,12 +39,12 @@ import {
deleteItemImage,
unarchiveFormTemplateItem,
archiveFormTemplateItem
-} from "../../actions/form-template-item-actions";
-import { getFormTemplate } from "../../actions/form-template-actions";
-import AddFormTemplateItemDialog from "./popup/add-form-template-item-popup";
-import SponsorItemDialog from "./popup/sponsor-inventory-popup";
-import { getInventoryItems } from "../../actions/inventory-item-actions";
-import { DEFAULT_CURRENT_PAGE } from "../../utils/constants";
+} from "../../../actions/form-template-item-actions";
+import { getFormTemplate } from "../../../actions/form-template-actions";
+import AddFormTemplateItemDialog from "./add-form-template-item-popup";
+import SponsorItemDialog from "./sponsor-inventory-popup";
+import { getInventoryItems } from "../../../actions/inventory-item-actions";
+import { DEFAULT_CURRENT_PAGE } from "../../../utils/constants";
const FormTemplateItemListPage = ({
formTemplateId,
diff --git a/src/pages/sponsors_inventory/form-template-list-page.js b/src/pages/sponsors-global/form-templates/form-template-list-page.js
similarity index 96%
rename from src/pages/sponsors_inventory/form-template-list-page.js
rename to src/pages/sponsors-global/form-templates/form-template-list-page.js
index e8882fe04..a149553b0 100644
--- a/src/pages/sponsors_inventory/form-template-list-page.js
+++ b/src/pages/sponsors-global/form-templates/form-template-list-page.js
@@ -37,12 +37,12 @@ import {
resetFormTemplateForm,
saveFormTemplate,
unarchiveFormTemplate
-} from "../../actions/form-template-actions";
-import MuiTable from "../../components/mui/table/mui-table";
-import FormTemplateDialog from "./popup/form-template-popup";
-import history from "../../history";
-import FormTemplateFromDuplicateDialog from "./popup/form-template-from-duplicate-popup";
-import { DEFAULT_CURRENT_PAGE } from "../../utils/constants";
+} from "../../../actions/form-template-actions";
+import MuiTable from "../../../components/mui/table/mui-table";
+import FormTemplateDialog from "./form-template-popup";
+import history from "../../../history";
+import FormTemplateFromDuplicateDialog from "./form-template-from-duplicate-popup";
+import { DEFAULT_CURRENT_PAGE } from "../../../utils/constants";
const FormTemplateListPage = ({
formTemplates,
diff --git a/src/pages/sponsors_inventory/popup/form-template-popup.js b/src/pages/sponsors-global/form-templates/form-template-popup.js
similarity index 99%
rename from src/pages/sponsors_inventory/popup/form-template-popup.js
rename to src/pages/sponsors-global/form-templates/form-template-popup.js
index 88b97044d..d47e94488 100644
--- a/src/pages/sponsors_inventory/popup/form-template-popup.js
+++ b/src/pages/sponsors-global/form-templates/form-template-popup.js
@@ -20,7 +20,7 @@ import CloseIcon from "@mui/icons-material/Close";
import { useFormik, FormikProvider, FieldArray } from "formik";
import * as yup from "yup";
import showConfirmDialog from "../../../components/mui/showConfirmDialog";
-import MetaFieldValues from "./meta-field-values";
+import MetaFieldValues from "../shared/meta-field-values";
import MuiFormikTextField from "../../../components/mui/formik-inputs/mui-formik-textfield";
import FormikTextEditor from "../../../components/inputs/formik-text-editor";
import MuiFormikSelect from "../../../components/mui/formik-inputs/mui-formik-select";
diff --git a/src/pages/sponsors_inventory/popup/sponsor-inventory-popup.js b/src/pages/sponsors-global/form-templates/sponsor-inventory-popup.js
similarity index 99%
rename from src/pages/sponsors_inventory/popup/sponsor-inventory-popup.js
rename to src/pages/sponsors-global/form-templates/sponsor-inventory-popup.js
index e59f3a254..4718d499d 100644
--- a/src/pages/sponsors_inventory/popup/sponsor-inventory-popup.js
+++ b/src/pages/sponsors-global/form-templates/sponsor-inventory-popup.js
@@ -28,7 +28,7 @@ import {
METAFIELD_TYPES
} from "../../../utils/constants";
import showConfirmDialog from "../../../components/mui/showConfirmDialog";
-import MetaFieldValues from "./meta-field-values";
+import MetaFieldValues from "../shared/meta-field-values";
import MuiFormikTextField from "../../../components/mui/formik-inputs/mui-formik-textfield";
import useScrollToError from "../../../hooks/useScrollToError";
import MuiFormikSelect from "../../../components/mui/formik-inputs/mui-formik-select";
diff --git a/src/pages/sponsors_inventory/edit-inventory-item-page.js b/src/pages/sponsors-global/inventory/edit-inventory-item-page.js
similarity index 95%
rename from src/pages/sponsors_inventory/edit-inventory-item-page.js
rename to src/pages/sponsors-global/inventory/edit-inventory-item-page.js
index cecac7cc0..f51c73a81 100644
--- a/src/pages/sponsors_inventory/edit-inventory-item-page.js
+++ b/src/pages/sponsors-global/inventory/edit-inventory-item-page.js
@@ -15,7 +15,7 @@ import React, { useEffect } from "react";
import { connect } from "react-redux";
import { Breadcrumb } from "react-breadcrumbs";
import T from "i18n-react/dist/i18n-react";
-import InventoryItemForm from "../../components/forms/inventory-item-form";
+import InventoryItemForm from "../../../components/forms/inventory-item-form";
import {
getInventoryItem,
resetInventoryItemForm,
@@ -23,7 +23,7 @@ import {
deleteInventoryItemMetaFieldType,
deleteInventoryItemMetaFieldTypeValue,
deleteInventoryItemImage
-} from "../../actions/inventory-item-actions";
+} from "../../../actions/inventory-item-actions";
const EditInventoryItemPage = (props) => {
const {
diff --git a/src/pages/sponsors_inventory/inventory-list-page.js b/src/pages/sponsors-global/inventory/inventory-list-page.js
similarity index 96%
rename from src/pages/sponsors_inventory/inventory-list-page.js
rename to src/pages/sponsors-global/inventory/inventory-list-page.js
index 344bae974..6475634a2 100644
--- a/src/pages/sponsors_inventory/inventory-list-page.js
+++ b/src/pages/sponsors-global/inventory/inventory-list-page.js
@@ -39,10 +39,10 @@ import {
resetInventoryItemForm,
saveInventoryItem,
unarchiveInventoryItem
-} from "../../actions/inventory-item-actions";
-import MuiTable from "../../components/mui/table/mui-table";
-import SponsorInventoryDialog from "./popup/sponsor-inventory-popup";
-import { DEFAULT_CURRENT_PAGE } from "../../utils/constants";
+} from "../../../actions/inventory-item-actions";
+import MuiTable from "../../../components/mui/table/mui-table";
+import SponsorInventoryDialog from "../form-templates/sponsor-inventory-popup";
+import { DEFAULT_CURRENT_PAGE } from "../../../utils/constants";
const InventoryListPage = ({
inventoryItems,
diff --git a/src/pages/sponsors-global/page-templates/edit-page-template-page.js b/src/pages/sponsors-global/page-templates/edit-page-template-page.js
new file mode 100644
index 000000000..ac2ff1c3e
--- /dev/null
+++ b/src/pages/sponsors-global/page-templates/edit-page-template-page.js
@@ -0,0 +1,85 @@
+/**
+ * Copyright 2024 OpenStack Foundation
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * */
+
+import React, { useEffect } from "react";
+import { connect } from "react-redux";
+import { Breadcrumb } from "react-breadcrumbs";
+import T from "i18n-react/dist/i18n-react";
+import FormTemplateForm from "../../../components/forms/form-template-form";
+import {
+ getFormTemplate,
+ resetFormTemplateForm,
+ saveFormTemplate,
+ deleteFormTemplateMetaFieldType,
+ deleteFormTemplateMetaFieldTypeValue,
+ deleteFormTemplateMaterial
+} from "../../../actions/form-template-actions";
+
+const EditPageTemplatePage = (props) => {
+ const {
+ match,
+ entity,
+ errors,
+ getFormTemplate,
+ resetFormTemplateForm,
+ saveFormTemplate,
+ deleteFormTemplateMetaFieldType,
+ deleteFormTemplateMetaFieldTypeValue,
+ deleteFormTemplateMaterial
+ } = props;
+ const formTemplateId = match.params.form_template_id;
+
+ useEffect(() => {
+ if (!formTemplateId) {
+ resetFormTemplateForm();
+ } else {
+ getFormTemplate(formTemplateId);
+ }
+ }, [formTemplateId, getFormTemplate, resetFormTemplateForm]);
+
+ const title = entity.id
+ ? T.translate("general.edit")
+ : T.translate("general.add");
+ const breadcrumb = entity.id ? entity.name : T.translate("general.new");
+
+ return (
+
+
+
+ {title} {T.translate("edit_form_template.form_template")}
+
+
+
+
+ );
+};
+
+const mapStateToProps = ({ currentFormTemplateState }) => ({
+ ...currentFormTemplateState
+});
+
+export default connect(mapStateToProps, {
+ getFormTemplate,
+ resetFormTemplateForm,
+ saveFormTemplate,
+ deleteFormTemplateMetaFieldType,
+ deleteFormTemplateMetaFieldTypeValue,
+ deleteFormTemplateMaterial
+})(EditPageTemplatePage);
diff --git a/src/pages/sponsors-global/page-templates/page-template-list-page.js b/src/pages/sponsors-global/page-templates/page-template-list-page.js
new file mode 100644
index 000000000..19821bdfc
--- /dev/null
+++ b/src/pages/sponsors-global/page-templates/page-template-list-page.js
@@ -0,0 +1,268 @@
+/**
+ * Copyright 2024 OpenStack Foundation
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * */
+
+import React, { useEffect, useState } from "react";
+import {
+ Alert,
+ Button,
+ Checkbox,
+ FormControlLabel,
+ FormGroup,
+ Grid2
+} from "@mui/material";
+import Box from "@mui/material/Box";
+import AddIcon from "@mui/icons-material/Add";
+import { connect } from "react-redux";
+import T from "i18n-react/dist/i18n-react";
+import {
+ archivePageTemplate,
+ getPageTemplates,
+ savePageTemplate,
+ unarchivePageTemplate
+} from "../../../actions/page-template-actions";
+import MuiTable from "../../../components/mui/table/mui-table";
+import SearchInput from "../../../components/mui/search-input";
+import { DEFAULT_CURRENT_PAGE } from "../../../utils/constants";
+import PageTemplatePopup from "./page-template-popup";
+
+const PageTemplateListPage = ({
+ pageTemplates,
+ currentPage,
+ perPage,
+ term,
+ order,
+ orderDir,
+ hideArchived,
+ totalPageTemplates,
+ getPageTemplates,
+ archivePageTemplate,
+ unarchivePageTemplate,
+ savePageTemplate
+}) => {
+ const [pageTemplateId, setPageTemplateId] = useState(null);
+
+ useEffect(() => {
+ getPageTemplates();
+ }, []);
+
+ const handlePageChange = (page) => {
+ getPageTemplates(term, page, perPage, order, orderDir, hideArchived);
+ };
+
+ const handlePerPageChange = (newPerPage) => {
+ getPageTemplates(
+ term,
+ DEFAULT_CURRENT_PAGE,
+ newPerPage,
+ order,
+ orderDir,
+ hideArchived
+ );
+ };
+
+ const handleSort = (key, dir) => {
+ getPageTemplates(term, currentPage, perPage, key, dir, hideArchived);
+ };
+
+ const handleSearch = (searchTerm) => {
+ getPageTemplates(
+ searchTerm,
+ currentPage,
+ perPage,
+ order,
+ orderDir,
+ hideArchived
+ );
+ };
+
+ const handleHideArchived = (ev) => {
+ getPageTemplates(
+ term,
+ currentPage,
+ perPage,
+ order,
+ orderDir,
+ ev.target.checked
+ );
+ };
+
+ const handleNewPageTemplate = () => {
+ setPageTemplateId("new");
+ };
+
+ const handleClonePageTemplate = () => {
+ console.log("CLONE PAGE");
+ };
+
+ const handleArchive = (item) =>
+ item.is_archived
+ ? unarchivePageTemplate(item.id)
+ : archivePageTemplate(item.id);
+
+ const handleEdit = (row) => {
+ console.log("EDIT", row);
+ };
+
+ const handleDelete = (row) => {
+ console.log("DELETE", row);
+ };
+
+ const columns = [
+ {
+ columnKey: "code",
+ header: T.translate("page_template_list.code"),
+ sortable: true
+ },
+ {
+ columnKey: "name",
+ header: T.translate("page_template_list.name"),
+ sortable: true
+ },
+ {
+ columnKey: "info_mod",
+ header: T.translate("page_template_list.info_mod"),
+ sortable: false
+ },
+ {
+ columnKey: "upload_mod",
+ header: T.translate("page_template_list.upload_mod"),
+ sortable: false
+ },
+ {
+ columnKey: "download_mod",
+ header: T.translate("page_template_list.download_mod"),
+ sortable: false
+ }
+ ];
+
+ const tableOptions = {
+ sortCol: order,
+ sortDir: orderDir
+ };
+
+ return (
+
+
+ {T.translate("page_template_list.page_templates")} ({totalPageTemplates}
+ )
+
+
+ {T.translate("page_template_list.alert_info")}
+
+
+
+
+ {totalPageTemplates} pages
+
+
+
+ }
+ label={T.translate("page_template_list.hide_archived")}
+ />
+
+
+
+
+ }
+ >
+ {T.translate("page_template_list.add_template")}
+
+ }
+ >
+ {T.translate("page_template_list.add_new")}
+
+
+
+
+
+ {pageTemplates.length === 0 && (
+
+ {T.translate("page_template_list.no_pages")}
+
+ )}
+
+ {pageTemplates.length > 0 && (
+
+
+
+ )}
+
+
setPageTemplateId(null)}
+ onSave={savePageTemplate}
+ />
+
+ );
+};
+
+const mapStateToProps = ({ pageTemplateListState }) => ({
+ ...pageTemplateListState
+});
+
+export default connect(mapStateToProps, {
+ getPageTemplates,
+ archivePageTemplate,
+ unarchivePageTemplate,
+ savePageTemplate
+})(PageTemplateListPage);
diff --git a/src/pages/sponsors-global/page-templates/page-template-popup.js b/src/pages/sponsors-global/page-templates/page-template-popup.js
new file mode 100644
index 000000000..6fc8f89c6
--- /dev/null
+++ b/src/pages/sponsors-global/page-templates/page-template-popup.js
@@ -0,0 +1,154 @@
+import React from "react";
+import T from "i18n-react/dist/i18n-react";
+import { connect } from "react-redux";
+import PropTypes from "prop-types";
+import {
+ Box,
+ Button,
+ Dialog,
+ DialogActions,
+ DialogContent,
+ DialogTitle,
+ Divider,
+ Grid2,
+ IconButton,
+ Typography
+} from "@mui/material";
+import AddIcon from "@mui/icons-material/Add";
+import CloseIcon from "@mui/icons-material/Close";
+import { FormikProvider, useFormik } from "formik";
+import * as yup from "yup";
+import MuiFormikTextField from "../../../components/mui/formik-inputs/mui-formik-textfield";
+
+const PageTemplatePopup = ({ pageTemplate, open, onClose, onSave }) => {
+ const handleClose = () => {
+ onClose();
+ };
+
+ const handleAddInfo = () => {
+ console.log("ADD INFO");
+ };
+
+ const handleAddDocument = () => {
+ console.log("ADD DOCUMENT");
+ };
+
+ const handleAddMedia = () => {
+ console.log("ADD MEDIA");
+ };
+
+ const formik = useFormik({
+ initialValues: {
+ ...pageTemplate
+ },
+ validationSchema: yup.object().shape({
+ code: yup.string().required(T.translate("validation.required")),
+ name: yup.string().required(T.translate("validation.required"))
+ }),
+ enableReinitialize: true,
+ onSubmit: (values) => {
+ onSave(values);
+ }
+ });
+
+ return (
+
+ );
+};
+
+PageTemplatePopup.propTypes = {
+ open: PropTypes.bool.isRequired,
+ onClose: PropTypes.func.isRequired,
+ onSave: PropTypes.func.isRequired
+};
+
+const mapStateToProps = ({ currentPageTemplateState }) => ({
+ ...currentPageTemplateState
+});
+
+export default connect(mapStateToProps, {})(PageTemplatePopup);
diff --git a/src/pages/sponsors_inventory/popup/meta-field-values.js b/src/pages/sponsors-global/shared/meta-field-values.js
similarity index 100%
rename from src/pages/sponsors_inventory/popup/meta-field-values.js
rename to src/pages/sponsors-global/shared/meta-field-values.js
diff --git a/src/pages/sponsors/components/additional-input.js b/src/pages/sponsors/components/additional-input.js
index 6386f6795..96b9ed74e 100644
--- a/src/pages/sponsors/components/additional-input.js
+++ b/src/pages/sponsors/components/additional-input.js
@@ -14,7 +14,7 @@ import {
import T from "i18n-react";
import DeleteIcon from "@mui/icons-material/Delete";
import AddIcon from "@mui/icons-material/Add";
-import MetaFieldValues from "../../sponsors_inventory/popup/meta-field-values";
+import MetaFieldValues from "../../sponsors-global/shared/meta-field-values";
import {
METAFIELD_TYPES,
METAFIELD_TYPES_WITH_OPTIONS
diff --git a/src/reducers/sponsors_inventory/page-template-list-reducer.js b/src/reducers/sponsors_inventory/page-template-list-reducer.js
new file mode 100644
index 000000000..e2ff363d0
--- /dev/null
+++ b/src/reducers/sponsors_inventory/page-template-list-reducer.js
@@ -0,0 +1,124 @@
+/**
+ * Copyright 2024 OpenStack Foundation
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * */
+
+import { LOGOUT_USER } from "openstack-uicore-foundation/lib/security/actions";
+import {
+ REQUEST_PAGE_TEMPLATES,
+ RECEIVE_PAGE_TEMPLATES,
+ PAGE_TEMPLATE_DELETED,
+ PAGE_TEMPLATE_ARCHIVED,
+ PAGE_TEMPLATE_UNARCHIVED
+} from "../../actions/page-template-actions";
+
+const DEFAULT_STATE = {
+ pageTemplates: [],
+ term: null,
+ order: "name",
+ orderDir: 1,
+ currentPage: 1,
+ lastPage: 1,
+ perPage: 10,
+ totalPageTemplates: 0,
+ hideArchived: false
+};
+
+const pageTemplateListReducer = (state = DEFAULT_STATE, action = {}) => {
+ const { type, payload } = action;
+ switch (type) {
+ case LOGOUT_USER: {
+ return DEFAULT_STATE;
+ }
+ case REQUEST_PAGE_TEMPLATES: {
+ const { order, orderDir, page, perPage, ...rest } = payload;
+
+ if (
+ order !== state.order ||
+ orderDir !== state.orderDir ||
+ page !== state.currentPage
+ ) {
+ // if the change was in page or order, keep selection
+ return {
+ ...state,
+ order,
+ orderDir,
+ currentPage: page,
+ ...rest
+ };
+ }
+
+ return {
+ ...state,
+ order,
+ orderDir,
+ pageTemplates: [],
+ currentPage: page,
+ perPage,
+ ...rest
+ };
+ }
+ case RECEIVE_PAGE_TEMPLATES: {
+ const { current_page, total, last_page } = payload.response;
+
+ const pageTemplates = payload.response.data.map((a) => ({
+ id: a.id,
+ code: a.code,
+ name: a.name,
+ info_mod: a.modules.filter((m) => m.kind === "Info").length,
+ upload_mod: a.modules.filter((m) => m.kind === "Upload").length,
+ download_mod: a.modules.filter((m) => m.kind === "Download").length,
+ is_archived: a.is_archived
+ }));
+
+ return {
+ ...state,
+ pageTemplates,
+ currentPage: current_page,
+ totalPageTemplates: total,
+ lastPage: last_page
+ };
+ }
+ case PAGE_TEMPLATE_DELETED: {
+ const { pageTemplateId } = payload;
+ return {
+ ...state,
+ pageTemplates: state.pageTemplates.filter(
+ (a) => a.id !== pageTemplateId
+ )
+ };
+ }
+ case PAGE_TEMPLATE_ARCHIVED: {
+ const updatedFormTemplate = payload.response;
+
+ const updatedPageTemplates = state.pageTemplates.map((item) =>
+ item.id === updatedFormTemplate.id
+ ? { ...item, is_archived: true }
+ : item
+ );
+ return { ...state, pageTemplates: updatedPageTemplates };
+ }
+ case PAGE_TEMPLATE_UNARCHIVED: {
+ const updatedFormTemplateId = payload;
+
+ const updatedPageTemplates = state.pageTemplates.map((item) =>
+ item.id === updatedFormTemplateId
+ ? { ...item, is_archived: false }
+ : item
+ );
+ return { ...state, pageTemplates: updatedPageTemplates };
+ }
+ default:
+ return state;
+ }
+};
+
+export default pageTemplateListReducer;
diff --git a/src/store.js b/src/store.js
index 64762f8ac..e61a3c172 100644
--- a/src/store.js
+++ b/src/store.js
@@ -158,6 +158,7 @@ import formTemplateReducer from "./reducers/sponsors_inventory/form-template-red
import formTemplateListReducer from "./reducers/sponsors_inventory/form-template-list-reducer.js";
import formTemplateItemReducer from "./reducers/sponsors_inventory/form-template-item-reducer.js";
import formTemplateItemListReducer from "./reducers/sponsors_inventory/form-template-item-list-reducer.js";
+import pageTemplateListReducer from "./reducers/sponsors_inventory/page-template-list-reducer.js";
import sponsorSettingsReducer from "./reducers/sponsor_settings/sponsor-settings-reducer";
import eventRSVPListReducer from "./reducers/rsvps/event-rsvp-list-reducer.js";
import eventRSVPInvitationListReducer from "./reducers/rsvps/event-rsvp-invitation-list-reducer.js";
@@ -322,7 +323,8 @@ const reducers = persistCombineReducers(config, {
currentFormTemplateListState: formTemplateListReducer,
currentFormTemplateItemState: formTemplateItemReducer,
currentFormTemplateItemListState: formTemplateItemListReducer,
- sponsorSettingsState: sponsorSettingsReducer
+ sponsorSettingsState: sponsorSettingsReducer,
+ pageTemplateListState: pageTemplateListReducer
});
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;