diff --git a/CHANGELOG.md b/CHANGELOG.md index 8d938deb..a4681a2f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,9 @@ All notable changes to this project will be documented in this file. - [#236](https://github.com/os2display/display-admin-client/pull/236) - Add help text to activation code form. - Fixed warnings raised when compiling. +- [#234](https://github.com/os2display/display-admin-client/pull/234) + - Cleaned up code flow in playlist saving. + - Changed to chaining relations puts. - [#235](https://github.com/os2display/display-admin-client/pull/235) - Added roles to users list. - [#233](https://github.com/os2display/display-admin-client/pull/233) diff --git a/src/components/playlist/campaign.spec.js b/src/components/playlist/campaign.spec.js index 70d40699..e08beb72 100644 --- a/src/components/playlist/campaign.spec.js +++ b/src/components/playlist/campaign.spec.js @@ -76,26 +76,6 @@ describe("Campaign pages work", () => { cy.get("#slides-section").find("tbody").should("not.exist"); }); - it("It displays success toast on save", () => { - // Mock successful response on post - cy.intercept("PUT", "**/playlists/*", { - statusCode: 201, - fixture: "playlists/playlist-successful.json", - }); - - // Mock successful response on get - cy.intercept("GET", "**/playlists/*", { - fixture: "playlists/playlist-successful.json", - }); - - cy.visit("/campaign/edit/123"); - - // Displays success toast and redirects - cy.get(".Toastify").find(".Toastify__toast--success").should("not.exist"); - cy.get("#save_playlist").click(); - cy.get(".Toastify").find(".Toastify__toast--success").contains("gemt"); - }); - it("It display error toast on save error", () => { // Mock error response on post cy.intercept("PUT", "**/playlists/*", { diff --git a/src/components/playlist/playlist-campaign-manager.jsx b/src/components/playlist/playlist-campaign-manager.jsx index c1166c5b..390c0f8f 100644 --- a/src/components/playlist/playlist-campaign-manager.jsx +++ b/src/components/playlist/playlist-campaign-manager.jsx @@ -4,6 +4,7 @@ import { useNavigate, useLocation } from "react-router-dom"; import set from "lodash.set"; import PropTypes from "prop-types"; import dayjs from "dayjs"; +import { useDispatch } from "react-redux"; import idFromUrl from "../util/helpers/id-from-url"; import PlaylistCampaignForm from "./playlist-campaign-form"; import PlaylistForm from "./playlist-form"; @@ -14,10 +15,8 @@ import { } from "../util/list/toast-component/display-toast"; import { usePutV1PlaylistsByIdMutation, - usePutV1ScreensByIdCampaignsMutation, - usePutV1PlaylistsByIdSlidesMutation, - usePutV1ScreenGroupsByIdCampaignsMutation, usePostV1PlaylistsMutation, + api, } from "../../redux/api/api.generated"; /** @@ -42,6 +41,7 @@ function PlaylistCampaignManager({ slideId, location, }) { + const dispatch = useDispatch(); const { t } = useTranslation("common", { keyPrefix: "playlist-campaign-manager", }); @@ -58,6 +58,7 @@ function PlaylistCampaignManager({ const [slidesToAdd, setSlidesToAdd] = useState([]); const [screensToAdd, setScreensToAdd] = useState([]); const [groupsToAdd, setGroupsToAdd] = useState([]); + const [savingRelations, setSavingRelations] = useState(false); const [ PutV1Playlists, @@ -73,33 +74,6 @@ function PlaylistCampaignManager({ { data, error: saveErrorPost, isSuccess: isSaveSuccessPost }, ] = usePostV1PlaylistsMutation(); - const [ - PutV1ScreensByIdCampaigns, - { - isLoading: savingScreens, - error: saveErrorScreens, - isSuccess: isSaveSuccessScreens, - }, - ] = usePutV1ScreensByIdCampaignsMutation(); - - const [ - PutV1ScreenGroupsByIdCampaigns, - { - isLoading: savingGroups, - error: saveErrorGroups, - isSuccess: isSaveSuccessGroups, - }, - ] = usePutV1ScreenGroupsByIdCampaignsMutation(); - - const [ - PutV1PlaylistsByIdSlides, - { - isLoading: savingSlides, - error: saveErrorSlides, - isSuccess: isSaveSuccessSlides, - }, - ] = usePutV1PlaylistsByIdSlidesMutation(); - /** Set loaded data into form state. */ useEffect(() => { if (initialState) { @@ -133,101 +107,118 @@ function PlaylistCampaignManager({ } }, [sharedParams]); - // Slides are saved successfully, display a message - useEffect(() => { - if (isSaveSuccessSlides) { - setSlidesToAdd([]); - displaySuccess(t(`${location}.success-messages.saved-slides`)); - } - }, [isSaveSuccessSlides]); - - // Screens are saved successfully, display a message - useEffect(() => { - if (isSaveSuccessScreens) { - setScreensToAdd([]); - displaySuccess(t(`${location}.success-messages.saved-screens`)); - } - }, [isSaveSuccessScreens]); - - // Groups are saved successfully, display a message - useEffect(() => { - if (isSaveSuccessGroups) { - setGroupsToAdd([]); - displaySuccess(t(`${location}.success-messages.saved-groups`)); - } - }, [isSaveSuccessGroups]); - - /** - * @param {Array} list - The list to save. - * @returns {Array} - If a list is ready to be saved. - */ - function readyToSave(list) { - return list && list.length > 0; - } + const bindScreens = () => { + return new Promise((resolve, reject) => { + // If not campaign, do not bind screens. + if (!formStateObject?.isCampaign) { + resolve(); + return; + } - /** When the screen is saved, the slide will be saved. */ - useEffect(() => { - if (readyToSave(screensToAdd)) { setLoadingMessage(t(`${location}.loading-messages.saving-screens`)); - PutV1ScreensByIdCampaigns({ - id: id || idFromUrl(data["@id"]), - body: JSON.stringify(screensToAdd), - }); - } - }, [isSaveSuccessPut, isSaveSuccessPost]); + dispatch( + api.endpoints.putV1ScreensByIdCampaigns.initiate({ + id: id || idFromUrl(data["@id"]), + body: JSON.stringify(screensToAdd), + }) + ) + .then((response) => { + if (response.error) { + displayError( + t(`${location}.error-messages.save-screens-error`), + response.error + ); + reject(response.error); + } else { + displaySuccess(t(`${location}.success-messages.saved-screens`)); + resolve(); + } + }) + .catch((e) => { + displayError(t(`${location}.error-messages.save-screens-error`), e); + reject(e); + }); + }); + }; + + const bindScreenGroups = () => { + return new Promise((resolve, reject) => { + // If not campaign, do not bind screen groups. + if (!formStateObject?.isCampaign) { + resolve(); + return; + } - /** When the group is saved, the slide will be saved. */ - useEffect(() => { - if (readyToSave(groupsToAdd)) { setLoadingMessage(t(`${location}.loading-messages.saving-groups`)); - PutV1ScreenGroupsByIdCampaigns({ - id: id || idFromUrl(data["@id"]), - body: JSON.stringify(groupsToAdd), - }); - } - }, [isSaveSuccessPut, isSaveSuccessPost]); - /** When the playlist is saved, the slide will be saved. */ - useEffect(() => { - if (readyToSave(slidesToAdd)) { - setLoadingMessage(t(`${location}.loading-messages.saving-slides`)); - PutV1PlaylistsByIdSlides({ - id: id || idFromUrl(data["@id"]), - body: JSON.stringify(slidesToAdd), - }); - } - }, [isSaveSuccessPut, isSaveSuccessPost]); + dispatch( + api.endpoints.putV1ScreenGroupsByIdCampaigns.initiate({ + id: id || idFromUrl(data["@id"]), + body: JSON.stringify(groupsToAdd), + }) + ) + .then((response) => { + if (response.error) { + displayError( + t(`${location}.error-messages.save-group-error`), + response.error + ); + reject(response.error); + } else { + displaySuccess(t(`${location}.success-messages.saved-groups`)); + resolve(); + } + }) + .catch((e) => { + displayError(t(`${location}.error-messages.save-group-error`), e); + reject(e); + }); + }); + }; - // Slides are not saved successfully, display a message - useEffect(() => { - if (saveErrorSlides) { - displayError( - t(`${location}.error-messages.save-slides-error`), - saveErrorSlides - ); - } - }, [saveErrorSlides]); + const bindSlides = () => { + return new Promise((resolve, reject) => { + setLoadingMessage(t(`${location}.loading-messages.saving-slides`)); - // Screens are not saved successfully, display a message - useEffect(() => { - if (saveErrorScreens) { - displayError( - t(`${location}.error-messages.save-screens-error`), - saveErrorScreens - ); - } - }, [saveErrorScreens]); + dispatch( + api.endpoints.putV1PlaylistsByIdSlides.initiate({ + id: id || idFromUrl(data["@id"]), + body: JSON.stringify(slidesToAdd), + }) + ) + .then((response) => { + if (response.error) { + displayError( + t(`${location}.error-messages.save-slides-error`), + response.error + ); + reject(response.error); + } else { + displaySuccess(t(`${location}.success-messages.saved-slides`)); + resolve(); + } + }) + .catch((e) => { + displayError(t(`${location}.error-messages.save-slides-error`), e); + reject(e); + }); + }); + }; - // Groups are not saved successfully, display a message + // When playlist has been saved, bind relations. useEffect(() => { - if (saveErrorGroups) { - displayError( - t(`${location}.error-messages.save-group-error`), - saveErrorGroups - ); + if (isSaveSuccessPut === true || isSaveSuccessPost === true) { + setSavingRelations(true); + bindScreens() + .then(() => bindScreenGroups().then(() => bindSlides())) + .finally(() => { + setSavingRelations(false); + displaySuccess(t(`${location}.success-messages.saved`)); + navigate(`/${location}/list`); + }); } - }, [saveErrorGroups]); + }, [isSaveSuccessPut, isSaveSuccessPost]); /** Slides are not saved successfully, display a message */ useEffect(() => { @@ -236,25 +227,6 @@ function PlaylistCampaignManager({ } }, [loadingError]); - /** If the slide is saved, display the success message and navigate to list */ - useEffect(() => { - if ( - (isSaveSuccessPost || isSaveSuccessPut) && - slidesToAdd.length === 0 && - groupsToAdd.length === 0 && - screensToAdd.length === 0 - ) { - displaySuccess(t(`${location}.success-messages.saved`)); - navigate(`/${location}/list`); - } - }, [ - isSaveSuccessPost, - isSaveSuccessPut, - slidesToAdd, - groupsToAdd, - screensToAdd, - ]); - /** If the slide is saved with error, display the error message */ useEffect(() => { if (saveErrorPut || saveErrorPost) { @@ -275,43 +247,6 @@ function PlaylistCampaignManager({ setFormStateObject(localFormStateObject); }; - /** Sets slides to save. */ - function handleSaveSlides() { - const { slides } = formStateObject; - if (Array.isArray(slides)) { - setSlidesToAdd( - slides.map((slide, index) => { - return { slide: idFromUrl(slide), weight: index }; - }) - ); - } - } - - /** Sets screens to save. */ - function handleSaveScreens() { - const { screens } = formStateObject; - - if (Array.isArray(screens)) { - setScreensToAdd( - screens.map((screen) => { - return { screen: idFromUrl(screen) }; - }) - ); - } - } - - /** Sets groups to save. */ - function handleSaveGroups() { - const { groups } = formStateObject; - if (Array.isArray(groups)) { - setGroupsToAdd( - groups.map((group) => { - return { screengroup: idFromUrl(group) }; - }) - ); - } - } - /** Handles submit. */ const handleSubmit = () => { setLoadingMessage(t(`${location}.loading-messages.saving-playlist`)); @@ -350,6 +285,7 @@ function PlaylistCampaignManager({ }; setLoadingMessage(t(`${location}.loading-messages.saving`)); + if (saveMethod === "POST") { PostV1Playlist({ playlistPlaylistInput: JSON.stringify(saveData), @@ -362,15 +298,37 @@ function PlaylistCampaignManager({ } if (Array.isArray(formStateObject.slides)) { - handleSaveSlides(); + const { slides } = formStateObject; + if (Array.isArray(slides)) { + setSlidesToAdd( + slides.map((slide, index) => { + return { slide: idFromUrl(slide), weight: index }; + }) + ); + } } if (Array.isArray(formStateObject.screens)) { - handleSaveScreens(); + const { screens } = formStateObject; + + if (Array.isArray(screens)) { + setScreensToAdd( + screens.map((screen) => { + return { screen: idFromUrl(screen) }; + }) + ); + } } if (Array.isArray(formStateObject.groups)) { - handleSaveGroups(); + const { groups } = formStateObject; + if (Array.isArray(groups)) { + setGroupsToAdd( + groups.map((group) => { + return { screengroup: idFromUrl(group) }; + }) + ); + } } }; @@ -383,13 +341,7 @@ function PlaylistCampaignManager({ headerText={`${headerText}: ${ formStateObject && formStateObject.title }`} - isLoading={ - savingPlaylists || - savingSlides || - isLoading || - savingScreens || - savingGroups - } + isLoading={savingPlaylists || savingRelations || isLoading} loadingMessage={loadingMessage} handleInput={handleInput} handleSubmit={handleSubmit} diff --git a/src/components/playlist/playlist-gantt-chart.jsx b/src/components/playlist/playlist-gantt-chart.jsx index c9486c54..25f3b2fa 100644 --- a/src/components/playlist/playlist-gantt-chart.jsx +++ b/src/components/playlist/playlist-gantt-chart.jsx @@ -85,7 +85,7 @@ function PlaylistGanttChart({ slides }) { PlaylistGanttChart.propTypes = { slides: PropTypes.arrayOf( - PropTypes.shape({ name: PropTypes.string, id: PropTypes.number }) + PropTypes.shape({ name: PropTypes.string, id: PropTypes.string }) ).isRequired, }; export default PlaylistGanttChart; diff --git a/src/components/util/drag-and-drop-table/drag-and-drop-table.jsx b/src/components/util/drag-and-drop-table/drag-and-drop-table.jsx index 08d98950..9dd1ebc8 100644 --- a/src/components/util/drag-and-drop-table/drag-and-drop-table.jsx +++ b/src/components/util/drag-and-drop-table/drag-and-drop-table.jsx @@ -171,7 +171,7 @@ function DragAndDropTable({ DragAndDropTable.propTypes = { data: PropTypes.arrayOf( - PropTypes.shape({ name: PropTypes.string, id: PropTypes.number }) + PropTypes.shape({ name: PropTypes.string, id: PropTypes.string }) ).isRequired, columns: ColumnProptypes.isRequired, name: PropTypes.string.isRequired, diff --git a/src/components/util/table/table.jsx b/src/components/util/table/table.jsx index 70448872..723fc23d 100644 --- a/src/components/util/table/table.jsx +++ b/src/components/util/table/table.jsx @@ -15,7 +15,7 @@ import PaginationButton from "../forms/multiselect-dropdown/pagination-button"; * @returns {object} The table. */ function Table({ columns, data, label, callback, totalItems }) { - const showButton = totalItems && totalItems > data.length; + const showButton = Number.isInteger(totalItems) && totalItems > data.length; return (
diff --git a/src/redux/api.js b/src/redux/api.js index 0f4a37c7..060ee357 100644 --- a/src/redux/api.js +++ b/src/redux/api.js @@ -73,7 +73,7 @@ generatedApi.enhanceEndpoints({ }, putV1PlaylistsById: { providesTags: ["Playlist"], - invalidatesTags: ["Playlist"], + invalidatesTags: ["Playlist", "Screen"], }, deleteV1PlaylistsById: { providesTags: ["Playlist"],