Skip to content

Commit c3e0a23

Browse files
authored
Merge branch 'release/3.0.0' into feature/documentation-and-screen-layouts
2 parents 0d8e5be + c7ded08 commit c3e0a23

File tree

19 files changed

+342
-324
lines changed

19 files changed

+342
-324
lines changed

CHANGELOG.md

Lines changed: 24 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -4,32 +4,33 @@ All notable changes to this project will be documented in this file.
44

55
## [Unreleased]
66

7-
* Gathered all repositories in one Symfony application.
8-
* Changed to vite 7 and rolldown.
9-
* Added ADRs 008 and 009.
10-
* Cleaned up Github Actions workflows.
11-
* Updated PHP dependencies.
12-
* Added Playwright github action.
13-
* Changed how templates are imported.
14-
* Removed propTypes.
15-
* Upgraded redux-toolkit and how api slices are generated.
16-
* Fixed redux-toolkit cache handling.
17-
* Added Taskfile
18-
* Added update command.
19-
* Added (Client) online-check to public.
20-
* Updated developer documentation.
21-
* Removed admin/access-config.json fetch
22-
* Aligned with v. 2.5.2.
23-
* Removed themes.
24-
* Refactored screen layout commands.
25-
* Added command to migrate config.json files.
7+
- Gathered all repositories in one Symfony application.
8+
- Changed to vite 7 and rolldown.
9+
- Added ADRs 008 and 009.
10+
- Cleaned up Github Actions workflows.
11+
- Updated PHP dependencies.
12+
- Added Playwright github action.
13+
- Changed how templates are imported.
14+
- Removed propTypes.
15+
- Upgraded redux-toolkit and how api slices are generated.
16+
- Fixed redux-toolkit cache handling.
17+
- Added Taskfile
18+
- Added update command.
19+
- Added (Client) online-check to public.
20+
- Updated developer documentation.
21+
- Removed admin/access-config.json fetch
22+
- Aligned with v. 2.5.2.
23+
- Removed themes.
24+
- Added command to migrate config.json files.
25+
- Fix data fetching bug
26+
- Refactored screen layout commands.
2627

2728
### NB! Prior to 3.x the project was split into separate repositories
2829

2930
Therefore, changelogs were maintained for each repo. The v2 changelogs have been moved to the `docs/v2-changelogs/`
3031
folder.
3132

32-
* API: [docs/v2-changelogs/api.md](docs/v2-changelogs/api.md)
33-
* Admin: [docs/v2-changelogs/admin.md](docs/v2-changelogs/admin.md)
34-
* Template: [docs/v2-changelogs/template.md](docs/v2-changelogs/template.md)
35-
* Client: [docs/v2-changelogs/client.md](docs/v2-changelogs/client.md)
33+
- API: [docs/v2-changelogs/api.md](docs/v2-changelogs/api.md)
34+
- Admin: [docs/v2-changelogs/admin.md](docs/v2-changelogs/admin.md)
35+
- Template: [docs/v2-changelogs/template.md](docs/v2-changelogs/template.md)
36+
- Client: [docs/v2-changelogs/client.md](docs/v2-changelogs/client.md)

assets/admin/components/playlist-drag-and-drop/playlist-drag-and-drop.jsx

Lines changed: 8 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -33,21 +33,16 @@ function PlaylistDragAndDrop({
3333
keyPrefix: "playlist-drag-and-drop",
3434
});
3535
const [searchText, setSearchText] = useState();
36-
const [page, setPage] = useState(1);
3736
const [onlySharedPlaylists, setOnlySharedPlaylists] = useState(false);
3837

39-
const {
40-
data: {
41-
"hydra:member": playlists = null,
42-
"hydra:totalItems": totalItems = 0,
43-
} = {},
44-
} = useGetV2PlaylistsQuery({
45-
isCampaign: false,
46-
title: searchText,
47-
itemsPerPage: 30,
48-
order: { createdAt: "desc" },
49-
sharedWithMe: onlySharedPlaylists,
50-
});
38+
const { data: { "hydra:member": playlists = null } = {} } =
39+
useGetV2PlaylistsQuery({
40+
isCampaign: false,
41+
title: searchText,
42+
itemsPerPage: 30,
43+
order: { createdAt: "desc" },
44+
sharedWithMe: onlySharedPlaylists,
45+
});
5146

5247
/**
5348
* Fetches data for the multi component
@@ -94,9 +89,6 @@ function PlaylistDragAndDrop({
9489
onDropped={handleChange}
9590
name={name}
9691
data={selectedPlaylists}
97-
callback={() => setPage(page + 1)}
98-
label={t("more-playlists")}
99-
totalItems={totalItems}
10092
/>
10193
)}
10294
{selectedPlaylists?.length > 0 && (

assets/admin/components/playlist/campaign-form.jsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { useTranslation } from "react-i18next";
22
import idFromUrl from "../util/helpers/id-from-url";
3-
import { useGetV2CampaignsByIdScreenGroupsQuery } from "../../../shared/redux/enhanced-api.ts";
3+
import { enhancedApi } from "../../../shared/redux/enhanced-api.ts";
44
import ContentBody from "../util/content-body/content-body";
55
import SelectScreensTable from "../util/multi-and-table/select-screens-table";
66
import SelectGroupsTable from "../util/multi-and-table/select-groups-table";
@@ -34,7 +34,9 @@ function CampaignForm({ campaign = null, handleInput }) {
3434
mappingId="screenGroup"
3535
handleChange={handleInput}
3636
name="groups"
37-
getSelectedMethod={useGetV2CampaignsByIdScreenGroupsQuery}
37+
getSelectedMethod={
38+
enhancedApi.endpoints.getV2CampaignsByIdScreenGroups.initiate
39+
}
3840
id={idFromUrl(campaign["@id"])}
3941
/>
4042
</ContentBody>

assets/admin/components/screen/screen-form.jsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import MultiSelectComponent from "../util/forms/multiselect-dropdown/multi-dropd
1313
import idFromUrl from "../util/helpers/id-from-url";
1414
import {
1515
useGetV2LayoutsQuery,
16-
useGetV2ScreensByIdScreenGroupsQuery,
16+
enhancedApi,
1717
} from "../../../shared/redux/enhanced-api.ts";
1818
import FormCheckbox from "../util/forms/form-checkbox";
1919
import Preview from "../preview/preview";
@@ -196,7 +196,9 @@ function ScreenForm({
196196
handleChange={handleInput}
197197
name="inScreenGroups"
198198
id={groupId}
199-
getSelectedMethod={useGetV2ScreensByIdScreenGroupsQuery}
199+
getSelectedMethod={
200+
enhancedApi.endpoints.getV2ScreensByIdScreenGroups.initiate
201+
}
200202
/>
201203
</ContentBody>
202204
<ContentBody>

assets/admin/components/screen/util/grid-generation-and-select.jsx

Lines changed: 92 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,11 @@ import { useState, useEffect } from "react";
22
import { Tabs, Tab, Alert } from "react-bootstrap";
33
import Grid from "./grid";
44
import { useTranslation } from "react-i18next";
5-
import { useDispatch } from "react-redux";
65
import idFromUrl from "../../util/helpers/id-from-url";
76
import PlaylistDragAndDrop from "../../playlist-drag-and-drop/playlist-drag-and-drop";
87
import { enhancedApi } from "../../../../shared/redux/enhanced-api.ts";
8+
import useFetchDataHook from "../../util/fetch-data-hook.js";
9+
import mapToIds from "../../util/helpers/map-to-ids.js";
910
import "./grid.scss";
1011

1112
/**
@@ -27,11 +28,18 @@ function GridGenerationAndSelect({
2728
regions = [],
2829
}) {
2930
const { t } = useTranslation("common");
30-
const dispatch = useDispatch();
3131
const [selectedRegion, setSelectedRegion] = useState(
3232
regions.length > 0 ? regions[0]["@id"] : "",
3333
);
3434
const [selectedPlaylists, setSelectedPlaylists] = useState([]);
35+
const { data: playlistsAndRegions } = useFetchDataHook(
36+
enhancedApi.endpoints.getV2ScreensByIdRegionsAndRegionIdPlaylists.initiate,
37+
mapToIds(regions), // returns and array with ids to fetch for all ids
38+
{
39+
id: screenId, // screen id is the id
40+
},
41+
"regionId", // The key for the list of ids
42+
);
3543

3644
/**
3745
* @param {object} props The props
@@ -40,93 +48,72 @@ function GridGenerationAndSelect({
4048
* @returns {Array} Mapped data
4149
*/
4250
function mapData({ value: inputPlaylists, id }) {
43-
// Map to add region id to incoming data.
44-
const localTarget = inputPlaylists.map((playlist) => {
45-
return {
46-
region: idFromUrl(id),
47-
...playlist,
48-
};
49-
});
50-
// A copy, to be able to remove items.
51-
let selectedPlaylistsCopy = [...selectedPlaylists];
52-
53-
// The following is used to determine if something has been removed from a list.
54-
const regionPlaylists = selectedPlaylists
55-
.filter(({ region }) => region === id)
56-
.map(({ region }) => region);
57-
58-
const selectedWithoutRegion = [];
59-
60-
// Checks if an element has been removed from the list
61-
if (inputPlaylists.length < regionPlaylists.length) {
62-
selectedPlaylists.forEach((playlist) => {
63-
if (!regionPlaylists.includes(playlist.region)) {
64-
selectedWithoutRegion.push(playlist);
65-
}
66-
});
67-
// If a playlist is removed from a list, all the playlists in that region will be removed.
68-
selectedPlaylistsCopy = selectedWithoutRegion;
51+
// Region id form id url
52+
const region = idFromUrl(id);
53+
54+
// Add the region id to each inputted playlist
55+
const playlistsWithRegion = inputPlaylists.map((playlist) => ({
56+
region,
57+
...playlist,
58+
}));
59+
60+
// Get the playlists that belong the same region from the selected playlists
61+
const existingRegionPlaylists = selectedPlaylists.filter(
62+
(playlist) => playlist.region === region,
63+
);
64+
65+
// Check if any playlists from the existing region playlists are missing from
66+
// The inputted playlists if so, they are removed from the list
67+
const removedPlaylists = existingRegionPlaylists.some(
68+
({ "@id": existingId }) =>
69+
!inputPlaylists.find(
70+
({ "@id": incomingId }) => incomingId === existingId,
71+
),
72+
);
73+
74+
// Start with the existing selected playlists
75+
let updatedRegionPlaylists = [...selectedPlaylists];
76+
77+
// If any playlists were removed, filter out all playlists for this region
78+
if (removedPlaylists) {
79+
updatedRegionPlaylists = selectedPlaylists.filter(
80+
(playlist) => playlist.region !== region,
81+
);
6982
}
7083

71-
// Removes duplicates.
72-
const localSelectedPlaylists = [
73-
...localTarget,
74-
...selectedPlaylistsCopy,
84+
// Merge the updated region playlists with the input playlists,
85+
// and remove any duplicate region and id combinations
86+
const mappedData = [
87+
...playlistsWithRegion,
88+
...updatedRegionPlaylists,
7589
].filter(
7690
(playlist, index, self) =>
7791
index ===
7892
self.findIndex(
79-
(secondPlaylist) =>
80-
secondPlaylist["@id"] === playlist["@id"] &&
81-
secondPlaylist.region === playlist.region,
93+
({ region, "@id": playlistId }) =>
94+
playlistId === playlist["@id"] && region === playlist.region,
8295
),
8396
);
8497

85-
return localSelectedPlaylists;
98+
return mappedData;
8699
}
87100

101+
// On received data, map to fit the components
102+
// We need region id to figure out which dropdown they should be placed in
103+
// and weight (order) for sorting.
88104
useEffect(() => {
89-
if (regions.length > 0) {
90-
const promises = [];
91-
regions.forEach(({ "@id": id }) => {
92-
promises.push(
93-
dispatch(
94-
enhancedApi.endpoints.getV2ScreensByIdRegionsAndRegionIdPlaylists.initiate(
95-
{
96-
id: screenId,
97-
regionId: idFromUrl(id),
98-
page: 1,
99-
itemsPerPage: 50,
100-
},
101-
),
102-
),
103-
);
104-
});
105-
106-
Promise.allSettled(promises).then((results) => {
107-
let playlists = [];
108-
results.forEach(
109-
({
110-
value: {
111-
originalArgs: { regionId },
112-
data: { "hydra:member": promisedPlaylists = null } = {},
113-
},
114-
}) => {
115-
playlists = [
116-
...playlists,
117-
...promisedPlaylists.map(({ playlist, weight }) => ({
118-
...playlist,
119-
weight,
120-
region: regionId,
121-
})),
122-
];
123-
},
124-
);
125-
playlists = playlists.sort((a, b) => a.weight - b.weight);
126-
setSelectedPlaylists(playlists);
127-
});
105+
if (playlistsAndRegions && playlistsAndRegions.length > 0) {
106+
const playlists = playlistsAndRegions
107+
.map(({ originalArgs: { regionId }, playlist, weight }) => ({
108+
...playlist,
109+
weight,
110+
region: regionId,
111+
}))
112+
.sort((a, b) => a.weight - b.weight);
113+
114+
setSelectedPlaylists(playlists);
128115
}
129-
}, [regions]);
116+
}, [playlistsAndRegions]);
130117

131118
useEffect(() => {
132119
handleInput({ target: { value: selectedPlaylists, id: "playlists" } });
@@ -156,6 +143,7 @@ function GridGenerationAndSelect({
156143
);
157144
};
158145

146+
// If there are no regions, the components should not spend time rendering.
159147
if (regions?.length === 0) return null;
160148

161149
return (
@@ -171,37 +159,35 @@ function GridGenerationAndSelect({
171159
</div>
172160
</div>
173161
<div className="col-md-12">
174-
<>
175-
<h3 className="h5">{t("screen-form.screen-region-playlists")}</h3>
176-
<Tabs
177-
defaultActiveKey={regions[0]["@id"]}
178-
id="tabs"
179-
onSelect={setSelectedRegion}
180-
className="mb-3"
181-
>
182-
{regions.map(({ title, "@id": id, type }) => (
183-
<Tab eventKey={id} key={id} title={title}>
184-
<PlaylistDragAndDrop
185-
id="playlist_drag_and_drop"
186-
handleChange={handleChange}
187-
removeFromList={removeFromList}
188-
name={id}
189-
regionIdForInitializeCallback={id}
190-
screenId={screenId}
191-
regionId={idFromUrl(id)}
192-
selectedPlaylists={selectedPlaylists.filter(
193-
({ region }) => region === idFromUrl(id),
194-
)}
195-
/>
196-
{type === "touch-buttons" && (
197-
<Alert key="screen-form-touch-buttons" variant="info">
198-
{t("screen-form.touch-region-helptext")}
199-
</Alert>
162+
<h3 className="h5">{t("screen-form.screen-region-playlists")}</h3>
163+
<Tabs
164+
defaultActiveKey={regions[0]["@id"]}
165+
id="tabs"
166+
onSelect={setSelectedRegion}
167+
className="mb-3"
168+
>
169+
{regions.map(({ title, "@id": id, type }) => (
170+
<Tab eventKey={id} key={id} title={title}>
171+
<PlaylistDragAndDrop
172+
id="playlist_drag_and_drop"
173+
handleChange={handleChange}
174+
removeFromList={removeFromList}
175+
name={id}
176+
regionIdForInitializeCallback={id}
177+
screenId={screenId}
178+
regionId={idFromUrl(id)}
179+
selectedPlaylists={selectedPlaylists.filter(
180+
({ region }) => region === idFromUrl(id),
200181
)}
201-
</Tab>
202-
))}
203-
</Tabs>
204-
</>
182+
/>
183+
{type === "touch-buttons" && (
184+
<Alert key="screen-form-touch-buttons" variant="info">
185+
{t("screen-form.touch-region-helptext")}
186+
</Alert>
187+
)}
188+
</Tab>
189+
))}
190+
</Tabs>
205191
</div>
206192
</>
207193
);

0 commit comments

Comments
 (0)