diff --git a/.env.example b/.env.example index 9c466c3b5..e3779db3f 100644 --- a/.env.example +++ b/.env.example @@ -12,14 +12,16 @@ PRINT_APP_URL=https://badge-print-app.dev.fnopen.com PUB_API_BASE_URL= OS_BASE_URL= SCOPES_BASE_REALM=${API_BASE_URL} -PURCHASES_API_SCOPES=purchases-show-medata/read purchases-show-medata/write show-form/read show-form/write customized-form/write customized-form/read carts/read carts/write +PURCHASES_API_SCOPES="purchases-show-medata/read purchases-show-medata/write show-form/read show-form/write customized-form/write customized-form/read carts/read carts/write" SPONSOR_USERS_API_SCOPES="show-medata/read show-medata/write access-requests/read access-requests/write sponsor-users/read sponsor-users/write groups/read groups/write" 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" +SCOPES="profile openid offline_access ${SPONSOR_USERS_API_SCOPES} ${PURCHASES_API_SCOPES} ${EMAIL_SCOPES} ${FILE_UPLOAD_SCOPES} ${SPONSOR_PAGES_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" GOOGLE_API_KEY= ALLOWED_USER_GROUPS="super-admins administrators summit-front-end-administrators summit-room-administrators track-chairs-admins sponsors" -APP_CLIENT_NAME = "openstack" +APP_CLIENT_NAME="openstack" PUBLIC_STORAGES="S3 SWIFT" OAUTH2_FLOW="code" TIMEINTERVALSINCE1970_API_URL=https://timeintervalsince1970.dev.fnopen.com 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 83342cc97..cb99477c3 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 6a64d6453..e6ca66683 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", @@ -3880,5 +3881,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 ce33a9298..d43285d96 100644 --- a/src/layouts/form-template-item-layout.js +++ b/src/layouts/form-template-item-layout.js @@ -16,7 +16,7 @@ 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 FormTemplateItemListPage from "../pages/sponsors_inventory/form-template-item-list-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 5bfe300c6..d055b96e3 100644 --- a/src/layouts/form-template-layout.js +++ b/src/layouts/form-template-layout.js @@ -16,7 +16,7 @@ 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 FormTemplateListPage from "../pages/sponsors-global/form-templates/form-template-list-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 7a28ddf1c..9bacaeeff 100644 --- a/src/layouts/inventory-item-layout.js +++ b/src/layouts/inventory-item-layout.js @@ -15,7 +15,7 @@ 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 InventoryListPage from "../pages/sponsors-global/inventory/inventory-list-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 }) => ( +