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..998932b7b --- /dev/null +++ b/src/actions/page-template-actions.js @@ -0,0 +1,251 @@ +/** + * 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, + showMessage, + showSuccessMessage, + authErrorHandler, + escapeFilterValue +} from "openstack-uicore-foundation/lib/utils/actions"; +import history from "../history"; +import { getAccessTokenSafely } from "../utils/methods"; +import { + DEFAULT_CURRENT_PAGE, + DEFAULT_ORDER_DIR, + DEFAULT_PER_PAGE +} from "../utils/constants"; +import { snackbarErrorHandler } 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.meta_fields = normalizedEntity.meta_fields?.filter( + (mf) => mf.name + ); + normalizedEntity.materials = normalizedEntity.materials?.filter( + (mat) => mat.file_path + ); + return normalizedEntity; +}; + +export const savePageTemplate = (entity) => async (dispatch) => { + const accessToken = await getAccessTokenSafely(); + const params = { + access_token: accessToken, + expand: "materials,meta_fields,meta_fields.values" + }; + + 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, + authErrorHandler, + entity + )(params)(dispatch) + .then(() => { + dispatch( + showSuccessMessage( + T.translate("edit_form_template.form_template_saved") + ) + ); + }) + .catch((err) => { + console.error(err); + }) + .finally(() => { + dispatch(stopLoading()); + }); + } + + const success_message = { + title: T.translate("general.done"), + html: T.translate("edit_form_template.form_template_created"), + type: "success" + }; + + return postRequest( + createAction(ADD_PAGE_TEMPLATE), + createAction(PAGE_TEMPLATE_ADDED), + `${window.SPONSOR_PAGES_API_URL}/api/v1/page-templates`, + normalizedEntity, + authErrorHandler, + entity + )(params)(dispatch) + .then(() => { + dispatch( + showMessage(success_message, () => { + history.push("/app/page-templates"); + }) + ); + }) + .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..c38c72f8a 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,25 @@ "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" + } } } diff --git a/src/layouts/page-template-layout.js b/src/layouts/page-template-layout.js new file mode 100644 index 000000000..ab4fc6e13 --- /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_inventory/edit-page-template-page"; +import PageTemplateListPage from "../pages/sponsors_inventory/page-template-list-page"; + +const PageTemplateLayout = ({ match }) => ( +