diff --git a/CHANGELOG.md b/CHANGELOG.md index 83324f2f..d169fce1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,31 +4,32 @@ All notable changes to this project will be documented in this file. ## [Unreleased] -* Gathered all repositories in one Symfony application. -* Changed to vite 7 and rolldown. -* Added ADRs 008 and 009. -* Cleaned up Github Actions workflows. -* Updated PHP dependencies. -* Added Playwright github action. -* Changed how templates are imported. -* Removed propTypes. -* Upgraded redux-toolkit and how api slices are generated. -* Fixed redux-toolkit cache handling. -* Added Taskfile -* Added update command. -* Added (Client) online-check to public. -* Updated developer documentation. -* Removed admin/access-config.json fetch -* Aligned with v. 2.5.2. -* Removed themes. -* Added command to migrate config.json files. +- Gathered all repositories in one Symfony application. +- Changed to vite 7 and rolldown. +- Added ADRs 008 and 009. +- Cleaned up Github Actions workflows. +- Updated PHP dependencies. +- Added Playwright github action. +- Changed how templates are imported. +- Removed propTypes. +- Upgraded redux-toolkit and how api slices are generated. +- Fixed redux-toolkit cache handling. +- Added Taskfile +- Added update command. +- Added (Client) online-check to public. +- Updated developer documentation. +- Removed admin/access-config.json fetch +- Aligned with v. 2.5.2. +- Removed themes. +- Added command to migrate config.json files. +- Fix data fetching bug ### NB! Prior to 3.x the project was split into separate repositories Therefore, changelogs were maintained for each repo. The v2 changelogs have been moved to the `docs/v2-changelogs/` folder. -* API: [docs/v2-changelogs/api.md](docs/v2-changelogs/api.md) -* Admin: [docs/v2-changelogs/admin.md](docs/v2-changelogs/admin.md) -* Template: [docs/v2-changelogs/template.md](docs/v2-changelogs/template.md) -* Client: [docs/v2-changelogs/client.md](docs/v2-changelogs/client.md) +- API: [docs/v2-changelogs/api.md](docs/v2-changelogs/api.md) +- Admin: [docs/v2-changelogs/admin.md](docs/v2-changelogs/admin.md) +- Template: [docs/v2-changelogs/template.md](docs/v2-changelogs/template.md) +- Client: [docs/v2-changelogs/client.md](docs/v2-changelogs/client.md) diff --git a/assets/admin/components/playlist-drag-and-drop/playlist-drag-and-drop.jsx b/assets/admin/components/playlist-drag-and-drop/playlist-drag-and-drop.jsx index 0a7c92fa..1735e166 100644 --- a/assets/admin/components/playlist-drag-and-drop/playlist-drag-and-drop.jsx +++ b/assets/admin/components/playlist-drag-and-drop/playlist-drag-and-drop.jsx @@ -33,21 +33,16 @@ function PlaylistDragAndDrop({ keyPrefix: "playlist-drag-and-drop", }); const [searchText, setSearchText] = useState(); - const [page, setPage] = useState(1); const [onlySharedPlaylists, setOnlySharedPlaylists] = useState(false); - const { - data: { - "hydra:member": playlists = null, - "hydra:totalItems": totalItems = 0, - } = {}, - } = useGetV2PlaylistsQuery({ - isCampaign: false, - title: searchText, - itemsPerPage: 30, - order: { createdAt: "desc" }, - sharedWithMe: onlySharedPlaylists, - }); + const { data: { "hydra:member": playlists = null } = {} } = + useGetV2PlaylistsQuery({ + isCampaign: false, + title: searchText, + itemsPerPage: 30, + order: { createdAt: "desc" }, + sharedWithMe: onlySharedPlaylists, + }); /** * Fetches data for the multi component @@ -94,9 +89,6 @@ function PlaylistDragAndDrop({ onDropped={handleChange} name={name} data={selectedPlaylists} - callback={() => setPage(page + 1)} - label={t("more-playlists")} - totalItems={totalItems} /> )} {selectedPlaylists?.length > 0 && ( diff --git a/assets/admin/components/playlist/campaign-form.jsx b/assets/admin/components/playlist/campaign-form.jsx index eae40777..4e2fe5fd 100644 --- a/assets/admin/components/playlist/campaign-form.jsx +++ b/assets/admin/components/playlist/campaign-form.jsx @@ -1,6 +1,6 @@ import { useTranslation } from "react-i18next"; import idFromUrl from "../util/helpers/id-from-url"; -import { useGetV2CampaignsByIdScreenGroupsQuery } from "../../../shared/redux/enhanced-api.ts"; +import { enhancedApi } from "../../../shared/redux/enhanced-api.ts"; import ContentBody from "../util/content-body/content-body"; import SelectScreensTable from "../util/multi-and-table/select-screens-table"; import SelectGroupsTable from "../util/multi-and-table/select-groups-table"; @@ -34,7 +34,9 @@ function CampaignForm({ campaign = null, handleInput }) { mappingId="screenGroup" handleChange={handleInput} name="groups" - getSelectedMethod={useGetV2CampaignsByIdScreenGroupsQuery} + getSelectedMethod={ + enhancedApi.endpoints.getV2CampaignsByIdScreenGroups.initiate + } id={idFromUrl(campaign["@id"])} /> diff --git a/assets/admin/components/screen/screen-form.jsx b/assets/admin/components/screen/screen-form.jsx index 40bed8d2..8f64516c 100644 --- a/assets/admin/components/screen/screen-form.jsx +++ b/assets/admin/components/screen/screen-form.jsx @@ -13,7 +13,7 @@ import MultiSelectComponent from "../util/forms/multiselect-dropdown/multi-dropd import idFromUrl from "../util/helpers/id-from-url"; import { useGetV2LayoutsQuery, - useGetV2ScreensByIdScreenGroupsQuery, + enhancedApi, } from "../../../shared/redux/enhanced-api.ts"; import FormCheckbox from "../util/forms/form-checkbox"; import Preview from "../preview/preview"; @@ -196,7 +196,9 @@ function ScreenForm({ handleChange={handleInput} name="inScreenGroups" id={groupId} - getSelectedMethod={useGetV2ScreensByIdScreenGroupsQuery} + getSelectedMethod={ + enhancedApi.endpoints.getV2ScreensByIdScreenGroups.initiate + } /> diff --git a/assets/admin/components/screen/util/grid-generation-and-select.jsx b/assets/admin/components/screen/util/grid-generation-and-select.jsx index eb59c46d..f9e9b614 100644 --- a/assets/admin/components/screen/util/grid-generation-and-select.jsx +++ b/assets/admin/components/screen/util/grid-generation-and-select.jsx @@ -2,10 +2,11 @@ import { useState, useEffect } from "react"; import { Tabs, Tab, Alert } from "react-bootstrap"; import Grid from "./grid"; import { useTranslation } from "react-i18next"; -import { useDispatch } from "react-redux"; import idFromUrl from "../../util/helpers/id-from-url"; import PlaylistDragAndDrop from "../../playlist-drag-and-drop/playlist-drag-and-drop"; import { enhancedApi } from "../../../../shared/redux/enhanced-api.ts"; +import useFetchDataHook from "../../util/fetch-data-hook.js"; +import mapToIds from "../../util/helpers/map-to-ids.js"; import "./grid.scss"; /** @@ -27,11 +28,18 @@ function GridGenerationAndSelect({ regions = [], }) { const { t } = useTranslation("common"); - const dispatch = useDispatch(); const [selectedRegion, setSelectedRegion] = useState( regions.length > 0 ? regions[0]["@id"] : "", ); const [selectedPlaylists, setSelectedPlaylists] = useState([]); + const { data: playlistsAndRegions } = useFetchDataHook( + enhancedApi.endpoints.getV2ScreensByIdRegionsAndRegionIdPlaylists.initiate, + mapToIds(regions), // returns and array with ids to fetch for all ids + { + id: screenId, // screen id is the id + }, + "regionId", // The key for the list of ids + ); /** * @param {object} props The props @@ -40,93 +48,72 @@ function GridGenerationAndSelect({ * @returns {Array} Mapped data */ function mapData({ value: inputPlaylists, id }) { - // Map to add region id to incoming data. - const localTarget = inputPlaylists.map((playlist) => { - return { - region: idFromUrl(id), - ...playlist, - }; - }); - // A copy, to be able to remove items. - let selectedPlaylistsCopy = [...selectedPlaylists]; - - // The following is used to determine if something has been removed from a list. - const regionPlaylists = selectedPlaylists - .filter(({ region }) => region === id) - .map(({ region }) => region); - - const selectedWithoutRegion = []; - - // Checks if an element has been removed from the list - if (inputPlaylists.length < regionPlaylists.length) { - selectedPlaylists.forEach((playlist) => { - if (!regionPlaylists.includes(playlist.region)) { - selectedWithoutRegion.push(playlist); - } - }); - // If a playlist is removed from a list, all the playlists in that region will be removed. - selectedPlaylistsCopy = selectedWithoutRegion; + // Region id form id url + const region = idFromUrl(id); + + // Add the region id to each inputted playlist + const playlistsWithRegion = inputPlaylists.map((playlist) => ({ + region, + ...playlist, + })); + + // Get the playlists that belong the same region from the selected playlists + const existingRegionPlaylists = selectedPlaylists.filter( + (playlist) => playlist.region === region, + ); + + // Check if any playlists from the existing region playlists are missing from + // The inputted playlists if so, they are removed from the list + const removedPlaylists = existingRegionPlaylists.some( + ({ "@id": existingId }) => + !inputPlaylists.find( + ({ "@id": incomingId }) => incomingId === existingId, + ), + ); + + // Start with the existing selected playlists + let updatedRegionPlaylists = [...selectedPlaylists]; + + // If any playlists were removed, filter out all playlists for this region + if (removedPlaylists) { + updatedRegionPlaylists = selectedPlaylists.filter( + (playlist) => playlist.region !== region, + ); } - // Removes duplicates. - const localSelectedPlaylists = [ - ...localTarget, - ...selectedPlaylistsCopy, + // Merge the updated region playlists with the input playlists, + // and remove any duplicate region and id combinations + const mappedData = [ + ...playlistsWithRegion, + ...updatedRegionPlaylists, ].filter( (playlist, index, self) => index === self.findIndex( - (secondPlaylist) => - secondPlaylist["@id"] === playlist["@id"] && - secondPlaylist.region === playlist.region, + ({ region, "@id": playlistId }) => + playlistId === playlist["@id"] && region === playlist.region, ), ); - return localSelectedPlaylists; + return mappedData; } + // On received data, map to fit the components + // We need region id to figure out which dropdown they should be placed in + // and weight (order) for sorting. useEffect(() => { - if (regions.length > 0) { - const promises = []; - regions.forEach(({ "@id": id }) => { - promises.push( - dispatch( - enhancedApi.endpoints.getV2ScreensByIdRegionsAndRegionIdPlaylists.initiate( - { - id: screenId, - regionId: idFromUrl(id), - page: 1, - itemsPerPage: 50, - }, - ), - ), - ); - }); - - Promise.allSettled(promises).then((results) => { - let playlists = []; - results.forEach( - ({ - value: { - originalArgs: { regionId }, - data: { "hydra:member": promisedPlaylists = null } = {}, - }, - }) => { - playlists = [ - ...playlists, - ...promisedPlaylists.map(({ playlist, weight }) => ({ - ...playlist, - weight, - region: regionId, - })), - ]; - }, - ); - playlists = playlists.sort((a, b) => a.weight - b.weight); - setSelectedPlaylists(playlists); - }); + if (playlistsAndRegions && playlistsAndRegions.length > 0) { + const playlists = playlistsAndRegions + .map(({ originalArgs: { regionId }, playlist, weight }) => ({ + ...playlist, + weight, + region: regionId, + })) + .sort((a, b) => a.weight - b.weight); + + setSelectedPlaylists(playlists); } - }, [regions]); + }, [playlistsAndRegions]); useEffect(() => { handleInput({ target: { value: selectedPlaylists, id: "playlists" } }); @@ -156,6 +143,7 @@ function GridGenerationAndSelect({ ); }; + // If there are no regions, the components should not spend time rendering. if (regions?.length === 0) return null; return ( @@ -171,37 +159,35 @@ function GridGenerationAndSelect({
- <> -

{t("screen-form.screen-region-playlists")}

- - {regions.map(({ title, "@id": id, type }) => ( - - region === idFromUrl(id), - )} - /> - {type === "touch-buttons" && ( - - {t("screen-form.touch-region-helptext")} - +

{t("screen-form.screen-region-playlists")}

+ + {regions.map(({ title, "@id": id, type }) => ( + + region === idFromUrl(id), )} - - ))} - - + /> + {type === "touch-buttons" && ( + + {t("screen-form.touch-region-helptext")} + + )} +
+ ))} +
); diff --git a/assets/admin/components/util/drag-and-drop-table/drag-and-drop-table.jsx b/assets/admin/components/util/drag-and-drop-table/drag-and-drop-table.jsx index 3b1fb223..ef080f55 100644 --- a/assets/admin/components/util/drag-and-drop-table/drag-and-drop-table.jsx +++ b/assets/admin/components/util/drag-and-drop-table/drag-and-drop-table.jsx @@ -1,10 +1,9 @@ -import { Row, Table, Col } from "react-bootstrap"; +import { Table } from "react-bootstrap"; import { DragDropContext, Draggable, Droppable } from "@hello-pangea/dnd"; import { useTranslation } from "react-i18next"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { faGripVertical } from "@fortawesome/free-solid-svg-icons"; import TableHeader from "../table/table-header"; -import PaginationButton from "../forms/multiselect-dropdown/pagination-button"; import "./drag-and-drop-table.scss"; /** @@ -14,20 +13,9 @@ import "./drag-and-drop-table.scss"; * @param {string} props.name The id of the form element * @param {Function} props.onDropped Callback for when an item is dropped and * the list is reordered. - * @param {Function} props.callback - The callback. - * @param {string} props.label - The label. - * @param {number} props.totalItems - Total data items. * @returns {object} The drag and drop table. */ -function DragAndDropTable({ - columns, - data, - name, - onDropped, - label, - callback, - totalItems, -}) { +function DragAndDropTable({ columns, data, name, onDropped }) { const { t } = useTranslation("common", { keyPrefix: "drag-and-drop-table", }); @@ -156,13 +144,6 @@ function DragAndDropTable({ - - - {totalItems > data.length && ( - - )} - - {t("help-text")} ); diff --git a/assets/admin/components/util/fetch-data-hook.js b/assets/admin/components/util/fetch-data-hook.js new file mode 100644 index 00000000..656cf9af --- /dev/null +++ b/assets/admin/components/util/fetch-data-hook.js @@ -0,0 +1,73 @@ +import { useState, useEffect } from "react"; +import { useDispatch } from "react-redux"; + +function useFetchDataHook(apiCall, ids, params = {}, key = "id") { + const [data, setData] = useState([]); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + const dispatch = useDispatch(); + + useEffect(() => { + if (!ids || ids.length === 0) return; + + async function fetchItems() { + setLoading(true); + + try { + let allItems = []; + let fetchedItems = []; + + for (const id of ids) { + let page = 1; + let totalItems = 1; // Will be overridden when we know the total amount. + + while (fetchedItems.length < totalItems) { + params[key] = id; + const { + data: { + "hydra:member": items = [], + "hydra:totalItems": hydraTotalItems = 0, + }, + originalArgs, + } = await dispatch( + apiCall({ + ...params, + page, + // The max items per page is 30: https://github.com/os2display/display-api-service/blob/develop/config/packages/api_platform.yaml#L11 + itemsPerPage: 30, + }), + ); + + // We don't like those darn infinite loops. + if (items.length === 0) { + break; + } + + // Sometimes we use the arguments from the api call + const itemsWithOriginalArgs = items.map((item) => ({ + ...item, + originalArgs, + })); + + totalItems = hydraTotalItems; + fetchedItems = fetchedItems.concat(itemsWithOriginalArgs); + page++; + } + allItems = allItems.concat(fetchedItems); + fetchedItems = []; + } + setData(allItems); + } catch (err) { + setError(err.message); + } finally { + setLoading(false); + } + } + + fetchItems(); + }, [apiCall]); // Should params beadded here to rerun on change? + + return { data, loading, error }; +} + +export default useFetchDataHook; diff --git a/assets/admin/components/util/forms/multiselect-dropdown/pagination-button.jsx b/assets/admin/components/util/forms/multiselect-dropdown/pagination-button.jsx deleted file mode 100644 index 694f443a..00000000 --- a/assets/admin/components/util/forms/multiselect-dropdown/pagination-button.jsx +++ /dev/null @@ -1,25 +0,0 @@ -import { Button } from "react-bootstrap"; - -/** - * A pagination button for multiselect dropdowns. - * - * @param {string} props The props. - * @param {Function} props.callback - The callback. - * @param {string} props.label - The label. - * @param {boolean} props.showButton - Show button. - * @returns {object} A pagination button. - */ -const PaginationButton = ({ - callback = () => {}, - label = "", - showButton = false, -}) => { - if (!showButton) return null; - return ( - - ); -}; - -export default PaginationButton; diff --git a/assets/admin/components/util/helpers/map-to-ids.js b/assets/admin/components/util/helpers/map-to-ids.js new file mode 100644 index 00000000..515f476c --- /dev/null +++ b/assets/admin/components/util/helpers/map-to-ids.js @@ -0,0 +1,7 @@ +import idFromUrl from "./id-from-url"; + +function mapToIds(array) { + return array.map((item) => idFromUrl(item["@id"])); +} + +export default mapToIds; diff --git a/assets/admin/components/util/multi-and-table/select-groups-table.jsx b/assets/admin/components/util/multi-and-table/select-groups-table.jsx index 451917aa..316c591a 100644 --- a/assets/admin/components/util/multi-and-table/select-groups-table.jsx +++ b/assets/admin/components/util/multi-and-table/select-groups-table.jsx @@ -7,6 +7,7 @@ import { useGetV2ScreenGroupsByIdScreensQuery, } from "../../../../shared/redux/enhanced-api.ts"; import GroupsDropdown from "../forms/multiselect-dropdown/groups/groups-dropdown"; +import useFetchDataHook from "../fetch-data-hook.js"; /** * A multiselect and table for groups. @@ -27,8 +28,6 @@ function SelectGroupsTable({ }) { const { t } = useTranslation("common", { keyPrefix: "select-groups-table" }); const [selectedData, setSelectedData] = useState([]); - const [totalItems, setTotalItems] = useState(0); - const [page, setPage] = useState(1); const [searchText, setSearchText] = useState(""); // Get 30 groups for dropdown, and when search is changed more will be fetched. @@ -39,35 +38,26 @@ function SelectGroupsTable({ order: "asc", }); - // Get 10 of the selected groups for table below dropdown, table is paginated so on page change more is fetched. - const { data: alreadySelectedGroups } = getSelectedMethod( - { - itemsPerPage: 10, - page, - id, - }, - { skip: !id }, - ); + // Get the selected groups for table below dropdown + const { data: preSelectedGroups } = useFetchDataHook(getSelectedMethod, [id]); /** Map loaded data. */ useEffect(() => { - if (alreadySelectedGroups) { - let newGroups = alreadySelectedGroups["hydra:member"]; + if (preSelectedGroups) { + let newGroups = preSelectedGroups; if (mappingId) { - newGroups = alreadySelectedGroups["hydra:member"].map( - (localScreenGroup) => { - return localScreenGroup[mappingId]; - }, - ); + newGroups = preSelectedGroups.map((localScreenGroup) => { + return localScreenGroup[mappingId]; + }); } - setTotalItems(alreadySelectedGroups["hydra:totalItems"]); + const value = [...selectedData, ...newGroups]; setSelectedData(value); handleChange({ target: { id: name, value: value.map((item) => item["@id"]) }, }); } - }, [alreadySelectedGroups]); + }, [preSelectedGroups]); /** * Adds group to list of groups. @@ -135,13 +125,7 @@ function SelectGroupsTable({ /> {selectedData.length > 0 && ( <> - setPage(page + 1)} - label={t("more-groups")} - totalItems={totalItems} - /> +
{t("edit-groups-help-text")} )} diff --git a/assets/admin/components/util/multi-and-table/select-playlists-table.jsx b/assets/admin/components/util/multi-and-table/select-playlists-table.jsx index 609e1a05..89e9235f 100644 --- a/assets/admin/components/util/multi-and-table/select-playlists-table.jsx +++ b/assets/admin/components/util/multi-and-table/select-playlists-table.jsx @@ -2,12 +2,13 @@ import { useState, useEffect } from "react"; import { useTranslation } from "react-i18next"; import Table from "../table/table"; import { + enhancedApi, useGetV2PlaylistsQuery, - useGetV2SlidesByIdPlaylistsQuery, useGetV2PlaylistsByIdSlidesQuery, } from "../../../../shared/redux/enhanced-api.ts"; import PlaylistsDropdown from "../forms/multiselect-dropdown/playlists/playlists-dropdown"; import { SelectPlaylistColumns } from "../../playlist/playlists-columns"; +import useFetchDataHook from "../fetch-data-hook.js"; /** * A multiselect and table for groups. @@ -23,8 +24,6 @@ function SelectPlaylistsTable({ handleChange, name, id = "", helpText }) { keyPrefix: "select-playlists-table", }); const [selectedData, setSelectedData] = useState([]); - const [totalItems, setTotalItems] = useState(0); - const [page, setPage] = useState(1); const [searchText, setSearchText] = useState(""); // Get 30 playlists for dropdown, and when search is changed more will be fetched. @@ -36,28 +35,22 @@ function SelectPlaylistsTable({ handleChange, name, id = "", helpText }) { order: { createdAt: "desc" }, }); - // Get 10 of the selected playlists for table below dropdown, table is paginated so on page change more is fetched. - const { data: alreadySelectedPlaylists } = useGetV2SlidesByIdPlaylistsQuery( - { - itemsPerPage: 10, - page, - id, - }, - { skip: !id }, + // Get the selected playlists for table below dropdown + const { data: preSelectedPlaylists } = useFetchDataHook( + enhancedApi.endpoints.getV2SlidesByIdPlaylists.initiate, + [id], ); /** Map loaded data. */ + useEffect(() => { - if (alreadySelectedPlaylists) { - setTotalItems(alreadySelectedPlaylists["hydra:totalItems"]); - const newPlaylists = alreadySelectedPlaylists["hydra:member"].map( - ({ playlist }) => { - return playlist; - }, - ); + if (preSelectedPlaylists) { + const newPlaylists = preSelectedPlaylists.map(({ playlist }) => { + return playlist; + }); setSelectedData([...selectedData, ...newPlaylists]); } - }, [alreadySelectedPlaylists]); + }, [preSelectedPlaylists]); /** * Adds group to list of groups. @@ -127,13 +120,7 @@ function SelectPlaylistsTable({ handleChange, name, id = "", helpText }) { helpText={helpText} /> {selectedData.length > 0 && ( -
setPage(page + 1)} - label={t("more-playlists")} - totalItems={totalItems} - /> +
)} )} diff --git a/assets/admin/components/util/multi-and-table/select-screens-table.jsx b/assets/admin/components/util/multi-and-table/select-screens-table.jsx index 077da922..1b743c58 100644 --- a/assets/admin/components/util/multi-and-table/select-screens-table.jsx +++ b/assets/admin/components/util/multi-and-table/select-screens-table.jsx @@ -4,10 +4,11 @@ import Table from "../table/table"; import ScreensDropdown from "../forms/multiselect-dropdown/screens/screens-dropdown"; import { SelectScreenColumns } from "../../screen/util/screen-columns"; import { + enhancedApi, useGetV2ScreensQuery, useGetV2ScreensByIdScreenGroupsQuery, - useGetV2CampaignsByIdScreensQuery, } from "../../../../shared/redux/enhanced-api.ts"; +import useFetchDataHook from "../fetch-data-hook.js"; /** * A multiselect and table for screens. @@ -21,8 +22,6 @@ import { function SelectScreensTable({ handleChange, name, campaignId = "" }) { const { t } = useTranslation("common", { keyPrefix: "select-screens-table" }); const [selectedData, setSelectedData] = useState([]); - const [totalItems, setTotalItems] = useState(0); - const [page, setPage] = useState(1); const [searchText, setSearchText] = useState(""); // Get 30 screens for dropdown, and when search is changed more will be fetched. @@ -32,25 +31,18 @@ function SelectScreensTable({ handleChange, name, campaignId = "" }) { order: { createdAt: "desc" }, }); - // Get 10 of the selected screens for table below dropdown, table is paginated so on page change more is fetched. - const { data: alreadySelectedScreens } = useGetV2CampaignsByIdScreensQuery( - { - id: campaignId, - itemsPerPage: 10, - page, - }, - { skip: !campaignId }, + // Get the selected screens for table below dropdown + const { data: preSelectedScreens } = useFetchDataHook( + enhancedApi.endpoints.getV2CampaignsByIdScreens.initiate, + [campaignId], ); useEffect(() => { - if (alreadySelectedScreens) { - setTotalItems(alreadySelectedScreens["hydra:totalItems"]); - const newScreens = alreadySelectedScreens["hydra:member"].map( - ({ screen }) => screen, - ); + if (preSelectedScreens) { + const newScreens = preSelectedScreens.map(({ screen }) => screen); setSelectedData([...selectedData, ...newScreens]); } - }, [alreadySelectedScreens]); + }, [preSelectedScreens]); /** * Adds group to list of groups. @@ -119,13 +111,7 @@ function SelectScreensTable({ handleChange, name, campaignId = "" }) { /> {selectedData?.length > 0 && ( <> -
setPage(page + 1)} - label={t("more-screens")} - totalItems={totalItems} - /> +
{t("edit-screens-help-text")} )} diff --git a/assets/admin/components/util/multi-and-table/select-slides-table.jsx b/assets/admin/components/util/multi-and-table/select-slides-table.jsx index c96831f3..43e1f894 100644 --- a/assets/admin/components/util/multi-and-table/select-slides-table.jsx +++ b/assets/admin/components/util/multi-and-table/select-slides-table.jsx @@ -6,11 +6,12 @@ import DragAndDropTable from "../drag-and-drop-table/drag-and-drop-table"; import SlidesDropdown from "../forms/multiselect-dropdown/slides/slides-dropdown"; import { useGetV2SlidesQuery, - useGetV2PlaylistsByIdSlidesQuery, useGetV2PlaylistsByIdQuery, + enhancedApi, } from "../../../../shared/redux/enhanced-api.ts"; import PlaylistGanttChart from "../../playlist/playlist-gantt-chart"; import { displayWarning } from "../list/toast-component/display-toast"; +import useFetchDataHook from "../fetch-data-hook.js"; /** * A multiselect and table for slides. @@ -25,8 +26,6 @@ function SelectSlidesTable({ handleChange, name, slideId = "" }) { const { t } = useTranslation("common", { keyPrefix: "select-slides-table" }); const [searchText, setSearchText] = useState(""); const [selectedData, setSelectedData] = useState([]); - const [totalItems, setTotalItems] = useState(0); - const [page, setPage] = useState(1); const { data: slides } = useGetV2SlidesQuery({ title: searchText, @@ -34,13 +33,10 @@ function SelectSlidesTable({ handleChange, name, slideId = "" }) { order: { createdAt: "desc" }, }); - const { data } = useGetV2PlaylistsByIdSlidesQuery( - { - id: slideId, - itemsPerPage: 30, - page, - }, - { skip: !slideId }, + // Get the selected slides for table below dropdown + const { data: preSelectedSlides } = useFetchDataHook( + enhancedApi.endpoints.getV2PlaylistsByIdSlides.initiate, + [slideId], ); const sortByStatus = () => { @@ -112,20 +108,14 @@ function SelectSlidesTable({ handleChange, name, slideId = "" }) { }; useEffect(() => { - if (data) { - setTotalItems(data["hydra:totalItems"]); - const newSlides = data["hydra:member"].map(({ slide }) => { + if (preSelectedSlides) { + const newSlides = preSelectedSlides.map(({ slide }) => { return slide; }); setSelectedData([...selectedData, ...newSlides]); - - // Get all selected slides. If a next page is defined, get the next page. - if (data["hydra:view"]["hydra:next"]) { - setPage(page + 1); - } } - }, [data]); + }, [preSelectedSlides]); /** * Adds group to list of groups. @@ -209,9 +199,6 @@ function SelectSlidesTable({ handleChange, name, slideId = "" }) { onDropped={handleAdd} name={name} data={selectedData} - totalItems={totalItems} - label={t("more-slides")} - callback={() => setPage(page + 1)} /> {t("edit-slides-help-text")} diff --git a/assets/admin/components/util/table/table.jsx b/assets/admin/components/util/table/table.jsx index 879c06b7..ce2ce5c4 100644 --- a/assets/admin/components/util/table/table.jsx +++ b/assets/admin/components/util/table/table.jsx @@ -1,38 +1,20 @@ import TableHeader from "./table-header"; import TableBody from "./table-body"; -import PaginationButton from "../forms/multiselect-dropdown/pagination-button"; /** * @param {object} props The props. * @param {Array} props.columns The columns for the table. * @param {Array} props.data The data to display in the table. - * @param {Function} props.callback - The callback. - * @param {string | null} props.label - The label. - * @param {number | null} props.totalItems - Total data items. * @param {boolean} props.isFetching - Fetching items. * @returns {object} The table. */ -function Table({ - columns, - data, - label = null, - callback = null, - totalItems = null, - isFetching = false, -}) { - const showButton = Number.isInteger(totalItems) && totalItems > data.length; - +function Table({ columns, data, isFetching = false }) { return (
{!isFetching && }
- ); } diff --git a/assets/admin/translations/da/common.json b/assets/admin/translations/da/common.json index fe19661d..513c0659 100644 --- a/assets/admin/translations/da/common.json +++ b/assets/admin/translations/da/common.json @@ -388,7 +388,6 @@ "published": "Udgivet", "number-of-slides": "Slides tilknyttede" }, - "more-playlists": "Hent flere spillelister", "edit-playlists-help-text": "Hvis du vil redigere en spilleliste, åbnes dette i en ny fane.", "remove-from-list": "Fjern fra liste", "info-modal": { @@ -630,7 +629,6 @@ }, "playlist-drag-and-drop": { "show-only-shared": "Vis delte spillelister", - "more-playlists": "Hent flere spillelister", "columns": { "published": "Udgivet" }, @@ -647,7 +645,6 @@ "published": "Udgivet" }, "edit-slides-help-text": "Hvis du vil redigere et slide, åbnes dette i en ny fane.", - "more-slides": "Hent flere slides", "edit-slides-order": "Afspilningsrækkefølge", "remove-from-list": "Fjern fra liste", "error-messages": { @@ -660,7 +657,6 @@ "on-groups": "Tilknyttede grupper", "location": "Lokation" }, - "more-screens": "Hent flere skærme", "edit-screens-help-text": "Hvis du vil redigere en skærm, åbnes dette i en ny fane.", "info-modal": { "screen-in-groups": "Skærmen er i følgende grupper" @@ -733,7 +729,6 @@ }, "select-groups-table": { "edit-groups-help-text": "Hvis du vil redigere en gruppe, åbnes dette i en ny fane.", - "more-groups": "Hent flere skærmgrupper", "remove-from-list": "Fjern fra liste", "info-modal": { "screens": "Gruppen indeholder følgende skærme" diff --git a/assets/tests/admin/admin-campaign.spec.js b/assets/tests/admin/admin-campaign.spec.js index cf52f0e3..367d5566 100644 --- a/assets/tests/admin/admin-campaign.spec.js +++ b/assets/tests/admin/admin-campaign.spec.js @@ -5,7 +5,7 @@ import { beforeEachTest, loginTest, } from "./test-helper.js"; -import { slidesJson1 } from "./data-fixtures.js"; +import { emptyJson, slidesJson1 } from "./data-fixtures.js"; test.describe("Campaign pages work", () => { test.beforeEach(async ({ page }) => { @@ -35,6 +35,7 @@ test.describe("Campaign pages work", () => { test("It removes slide", async ({ page }) => { // Intercept slides in dropdown await fulfillDataRoute(page, "**/slides*", slidesJson1); + await fulfillDataRoute(page, "**/playlists/*/slides*", emptyJson); // Pick slide await page