From fdb7b4df2ff3e1bc1d3579dfec37265f0b294d6e Mon Sep 17 00:00:00 2001 From: Troels Ugilt Jensen <6103205+tuj@users.noreply.github.com> Date: Thu, 27 Jun 2024 13:10:59 +0200 Subject: [PATCH 1/9] 1765: Added columns to playlist slides --- CHANGELOG.md | 1 + src/components/playlist/playlists-columns.jsx | 4 +- .../playlist/shared-playlists-column.jsx | 4 +- src/components/slide/slides-columns.jsx | 15 ++-- .../drag-and-drop-table.jsx | 3 +- .../multi-and-table/select-slides-table.jsx | 5 +- .../util/{published.jsx => publishing.jsx} | 44 +++++----- src/components/util/publishingStatus.jsx | 86 +++++++++++++++++++ src/translations/da/common.json | 10 ++- 9 files changed, 133 insertions(+), 39 deletions(-) rename src/components/util/{published.jsx => publishing.jsx} (56%) create mode 100644 src/components/util/publishingStatus.jsx diff --git a/CHANGELOG.md b/CHANGELOG.md index 2afe3020..fda8c98f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ All notable changes to this project will be documented in this file. ## [Unreleased] +- Added option to sort slides in playlist by published. - Changed from vite CJS to ESM. - Removed array spread. - Fixed HMR setup. diff --git a/src/components/playlist/playlists-columns.jsx b/src/components/playlist/playlists-columns.jsx index 06605e61..6189db7f 100644 --- a/src/components/playlist/playlists-columns.jsx +++ b/src/components/playlist/playlists-columns.jsx @@ -4,7 +4,7 @@ import ColumnHoc from "../util/column-hoc"; import SelectColumnHoc from "../util/select-column-hoc"; import UserContext from "../../context/user-context"; import ListButton from "../util/list/list-button"; -import Published from "../util/published"; +import Publishing from "../util/publishing.jsx"; /** * Columns for playlists lists. @@ -33,7 +33,7 @@ function getPlaylistColumns({ label: t("published"), // eslint-disable-next-line react/prop-types content: ({ publishedFrom, publishedTo, published }) => ( - ), diff --git a/src/components/playlist/shared-playlists-column.jsx b/src/components/playlist/shared-playlists-column.jsx index ee50fbe7..6a8300bf 100644 --- a/src/components/playlist/shared-playlists-column.jsx +++ b/src/components/playlist/shared-playlists-column.jsx @@ -1,6 +1,6 @@ import { React } from "react"; import { useTranslation } from "react-i18next"; -import Published from "../util/published"; +import Publishing from "../util/publishing.jsx"; /** * Columns for shared playlists lists. @@ -25,7 +25,7 @@ function getSharedPlaylistColumns() { path: "published", label: t("published"), // eslint-disable-next-line react/prop-types - content: ({ published }) => , + content: ({ published }) => , }, ]; diff --git a/src/components/slide/slides-columns.jsx b/src/components/slide/slides-columns.jsx index 1dc0dbc2..748836c7 100644 --- a/src/components/slide/slides-columns.jsx +++ b/src/components/slide/slides-columns.jsx @@ -2,9 +2,10 @@ import { React } from "react"; import { useTranslation } from "react-i18next"; import TemplateLabelInList from "../util/template-label-in-list"; import ListButton from "../util/list/list-button"; -import Published from "../util/published"; +import Publishing from "../util/publishing.jsx"; import ColumnHoc from "../util/column-hoc"; import SelectColumnHoc from "../util/select-column-hoc"; +import PublishingStatus from "../util/publishingStatus.jsx"; /** * Columns for slides lists. @@ -48,10 +49,14 @@ function getSlidesColumns({ label: t("columns.slide-on-playlists"), }, { - key: "published", - // eslint-disable-next-line react/prop-types - content: ({ published }) => , - label: t("columns.published"), + key: "publishing", + content: ({ published }) => , + label: t("columns.publishing"), + }, + { + key: "active", + content: ({ published }) => , + label: t("columns.status"), }, ]; 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 9dd1ebc8..0c234473 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 @@ -134,6 +134,7 @@ function DragAndDropTable({ providedSnapshot.isDragging, providedDraggable.draggableProps.style )} + className={data.className ?? ''} > { if (data) { setTotalItems(data["hydra:totalItems"]); const newSlides = data["hydra:member"].map(({ slide }) => { return slide; }); + setSelectedData([...selectedData, ...newSlides]); // Get all selected slides. If a next page is defined, get the next page. diff --git a/src/components/util/published.jsx b/src/components/util/publishing.jsx similarity index 56% rename from src/components/util/published.jsx rename to src/components/util/publishing.jsx index 9793d97b..54f72f78 100644 --- a/src/components/util/published.jsx +++ b/src/components/util/publishing.jsx @@ -1,58 +1,54 @@ -import { React, useEffect, useState } from "react"; +import { React, useEffect } from "react"; import PropTypes from "prop-types"; import dayjs from "dayjs"; import localeDa from "dayjs/locale/da"; import localizedFormat from "dayjs/plugin/localizedFormat"; import { useTranslation } from "react-i18next"; -import calculateIsPublished from "./helpers/calculate-is-published"; /** * @param {object} props The props. * @param {object} props.published Object with from and to. * @returns {object} The published yes/no component. */ -function Published({ published }) { +function Publishing({ published }) { const { t } = useTranslation("common", { keyPrefix: "published" }); - const [isPublished, setIsPublished] = useState(false); useEffect(() => { dayjs.extend(localizedFormat); }, []); - useEffect(() => { - setIsPublished(calculateIsPublished(published)); - }, [published]); - - if (!published.from && !published.to) { - return
{isPublished ? t("yes") : t("no")}
; - } - - let publishedFrom = "-"; - let publishedTo = "-"; + let publishedFrom = null; + let publishedTo = null; if (published.from) { - publishedFrom = dayjs(published.from).locale(localeDa).format("LLLL"); + publishedFrom = dayjs(published.from).locale(localeDa).format("LLL"); } + if (published.to) { - publishedTo = dayjs(published.to).locale(localeDa).format("LLLL"); + publishedTo = dayjs(published.to).locale(localeDa).format("LLL"); } + return ( <> -
- {t("from")}: {publishedFrom} -
-
- {t("to")}: {publishedTo} -
+ {publishedFrom && ( +
+ {t("from")}: {publishedFrom} +
+ )} + {publishedTo && ( +
+ {t("to")}: {publishedTo} +
+ )} ); } -Published.propTypes = { +Publishing.propTypes = { published: PropTypes.shape({ from: PropTypes.string, to: PropTypes.string, }).isRequired, }; -export default Published; +export default Publishing; diff --git a/src/components/util/publishingStatus.jsx b/src/components/util/publishingStatus.jsx new file mode 100644 index 00000000..b6d99fef --- /dev/null +++ b/src/components/util/publishingStatus.jsx @@ -0,0 +1,86 @@ +import { React, useEffect, useState } from "react"; +import PropTypes from "prop-types"; +import dayjs from "dayjs"; +import localizedFormat from "dayjs/plugin/localizedFormat"; +import { useTranslation } from "react-i18next"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { + faMinusCircle, + faCheckCircle, + faClock, +} from "@fortawesome/free-solid-svg-icons"; + +/** + * @param {object} props The props. + * @param {object} props.published Object with from and to. + * @returns {object} The published yes/no component. + */ +function PublishingStatus({ published }) { + const PUBLISHED_EXPIRED = "EXPIRED"; + const PUBLISHED_ACTIVE = "ACTIVE"; + const PUBLISHED_FUTURE = "FUTURE"; + + const { t } = useTranslation("common", { keyPrefix: "published-state" }); + const [publishedState, setPublishedState] = useState(null); + + useEffect(() => { + dayjs.extend(localizedFormat); + }, []); + + useEffect(() => { + let newPublishedState = null; + const now = dayjs(new Date()); + + const from = published.from ? dayjs(published.from) : null; + const to = published.to ? dayjs(published.to) : null; + + if (from !== null) { + if (from.isAfter(now)) { + newPublishedState = PUBLISHED_FUTURE; + } + } + + if (to !== null) { + if (to.isBefore(now)) { + newPublishedState = PUBLISHED_EXPIRED; + } + } + + setPublishedState(newPublishedState ?? PUBLISHED_ACTIVE); + + return () => {}; + }, [published]); + + // TODO: Fix to follow system colors. + return ( + + {publishedState === PUBLISHED_EXPIRED && ( + <> + {" "} + {t("expired")} + + )} + {publishedState === PUBLISHED_ACTIVE && ( + <> + {" "} + {t("active")} + + )} + {publishedState === PUBLISHED_FUTURE && ( + <> + {" "} + {t("future")} + + )} + + ); +} + +PublishingStatus.propTypes = { + published: PropTypes.shape({ + from: PropTypes.string, + to: PropTypes.string, + }).isRequired, +}; + +export default PublishingStatus; diff --git a/src/translations/da/common.json b/src/translations/da/common.json index c59ff732..384a8803 100644 --- a/src/translations/da/common.json +++ b/src/translations/da/common.json @@ -28,7 +28,8 @@ "columns": { "template": "Skabelon", "slide-on-playlists": "Spillelister", - "published": "Udgivet" + "publishing": "Udgivelse", + "status": "Status" }, "info-modal": { "slide-on-playlists": "Slidet er på de følgende spillelister" @@ -641,7 +642,7 @@ "drag-and-drop-table": { "not-available": "Ikke tilgængeligt", "table-header-for-indicator": "Drag and drop indikator", - "help-text": "Brug space til at vælge et element til flytning, og brug derefter piltasterne til at trække op eller ned. Brug space til at slippe elementet igen, og brug escape til at annullere. Nogle skærmlæsere skal være i fokus før det virker." + "help-text": "Brug space til at vælge et element til flytning, og brug derefter piletasterne til at trække op eller ned. Brug space til at slippe elementet igen, og brug escape til at annullere. Nogle skærmlæsere skal være i fokus før det virker." }, "link-for-list": { "label": "Rediger" @@ -981,5 +982,10 @@ "loading-messages": { "saving-activation-code": "Opretter aktiveringskode" } + }, + "published-state": { + "active": "Aktiv", + "future": "Fremtidig", + "expired": "Udløbet" } } From fed3ef51e334860e6cf708d854a8f619ab659590 Mon Sep 17 00:00:00 2001 From: Troels Ugilt Jensen <6103205+tuj@users.noreply.github.com> Date: Fri, 28 Jun 2024 08:48:39 +0200 Subject: [PATCH 2/9] 1765: Added always as option for showing publishing --- src/components/util/publishing.jsx | 3 +++ src/translations/da/common.json | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/components/util/publishing.jsx b/src/components/util/publishing.jsx index 54f72f78..f3f99ab1 100644 --- a/src/components/util/publishing.jsx +++ b/src/components/util/publishing.jsx @@ -40,6 +40,9 @@ function Publishing({ published }) { {t("to")}: {publishedTo} )} + {publishedFrom === null && publishedTo === null && ( +
{t("always")}
+ )} ); } diff --git a/src/translations/da/common.json b/src/translations/da/common.json index 384a8803..33559409 100644 --- a/src/translations/da/common.json +++ b/src/translations/da/common.json @@ -557,7 +557,8 @@ "yes": "Ja", "no": "Nej", "from": "Fra", - "to": "Til" + "to": "Til", + "always": "Ubegrænset" }, "modal-dialog": { "cancel": "Annuller", From 52fddd34c3ee2e8150a5cf4818b30874fcc359e6 Mon Sep 17 00:00:00 2001 From: Troels Ugilt Jensen <6103205+tuj@users.noreply.github.com> Date: Mon, 1 Jul 2024 11:09:48 +0200 Subject: [PATCH 3/9] 1765: Simplified table --- .../playlist/playlist-campaign-form.jsx | 2 +- src/components/slide/slides-columns.jsx | 68 ++++++++++++++----- src/components/util/date-value.jsx | 24 +++++++ .../drag-and-drop-table.jsx | 2 +- .../multi-and-table/select-slides-table.jsx | 62 +++++++++++++++++ src/components/util/publishing.jsx | 4 +- src/components/util/publishingStatus.jsx | 1 - src/translations/da/common.json | 5 +- 8 files changed, 146 insertions(+), 22 deletions(-) create mode 100644 src/components/util/date-value.jsx diff --git a/src/components/playlist/playlist-campaign-form.jsx b/src/components/playlist/playlist-campaign-form.jsx index 69f9fd65..b30e3213 100644 --- a/src/components/playlist/playlist-campaign-form.jsx +++ b/src/components/playlist/playlist-campaign-form.jsx @@ -1,5 +1,5 @@ import { React, useState } from "react"; -import { Button, Col, Form, Row } from "react-bootstrap"; +import { Alert, Button, Col, Form, Row } from "react-bootstrap"; import { useTranslation } from "react-i18next"; import { useNavigate } from "react-router-dom"; import PropTypes from "prop-types"; diff --git a/src/components/slide/slides-columns.jsx b/src/components/slide/slides-columns.jsx index 748836c7..a7da0857 100644 --- a/src/components/slide/slides-columns.jsx +++ b/src/components/slide/slides-columns.jsx @@ -6,6 +6,7 @@ import Publishing from "../util/publishing.jsx"; import ColumnHoc from "../util/column-hoc"; import SelectColumnHoc from "../util/select-column-hoc"; import PublishingStatus from "../util/publishingStatus.jsx"; +import DateValue from "../util/date-value.jsx"; /** * Columns for slides lists. @@ -15,6 +16,7 @@ import PublishingStatus from "../util/publishingStatus.jsx"; * @param {string} props.infoModalRedirect - The url for redirecting in the info modal. * @param {string} props.infoModalTitle - The info modal title. * @param {string} props.dataKey The data key for mapping the data. the list button + * @param props.hideColumns * @returns {object} The columns for the slides lists. */ function getSlidesColumns({ @@ -22,19 +24,39 @@ function getSlidesColumns({ infoModalRedirect, infoModalTitle, dataKey, + hideColumns = {}, }) { const { t } = useTranslation("common", { keyPrefix: "slides-list" }); - const columns = [ - { + const columns = []; + + if (!hideColumns?.title) { + columns.push({ + path: "title", + label: t("columns.name"), + }); + } + + if (!hideColumns?.createdBy) { + columns.push({ + path: "createdBy", + label: t("columns.created-by"), + }); + } + + if (!hideColumns?.template) { + columns.push({ // eslint-disable-next-line react/prop-types content: ({ templateInfo }) => ( ), key: "template", label: t("columns.template"), - }, - { + }); + } + + if (!hideColumns?.playlists) { + columns.push({ key: "playlists", // eslint-disable-next-line react/prop-types content: ({ onPlaylists }) => ( @@ -47,23 +69,37 @@ function getSlidesColumns({ /> ), label: t("columns.slide-on-playlists"), - }, - { - key: "publishing", - content: ({ published }) => , - label: t("columns.publishing"), - }, - { - key: "active", + }); + } + + if (!hideColumns?.publishingFrom) { + columns.push({ + key: "publishing-from", + content: ({ published }) => , + label: t("columns.publishing-from"), + }); + } + + if (!hideColumns?.publishingTo) { + columns.push({ + key: "publishing-to", + content: ({ published }) => , + label: t("columns.publishing-to"), + }); + } + + if (!hideColumns?.status) { + columns.push({ + key: "status", content: ({ published }) => , label: t("columns.status"), - }, - ]; + }); + } return columns; } -const SlideColumns = ColumnHoc(getSlidesColumns); -const SelectSlideColumns = SelectColumnHoc(getSlidesColumns); +const SlideColumns = ColumnHoc(getSlidesColumns, true); +const SelectSlideColumns = SelectColumnHoc(getSlidesColumns, true); export { SelectSlideColumns, SlideColumns }; diff --git a/src/components/util/date-value.jsx b/src/components/util/date-value.jsx new file mode 100644 index 00000000..5f6b4ce8 --- /dev/null +++ b/src/components/util/date-value.jsx @@ -0,0 +1,24 @@ +import { React, useEffect } from "react"; +import PropTypes from "prop-types"; +import dayjs from "dayjs"; +import localeDa from "dayjs/locale/da"; +import localizedFormat from "dayjs/plugin/localizedFormat"; + +/** + * @param {object} props The props. + * @param {string} props.date Date string to format. + * @returns {object} Formatted date + */ +function DateValue({ date }) { + useEffect(() => { + dayjs.extend(localizedFormat); + }, []); + + return date ? dayjs(date).locale(localeDa).format("l LT") : ""; +} + +DateValue.propTypes = { + date: PropTypes.string, +}; + +export default DateValue; 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 0c234473..e2d7ae46 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 @@ -15,7 +15,7 @@ import "./drag-and-drop-table.scss"; * @param {Array} props.columns The columns for the table. * @param {Array} props.data The data to display in the table. * @param {string} props.name The id of the form element - * @param {Function} props.onDropped Callback for when an items is dropped and + * @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. diff --git a/src/components/util/multi-and-table/select-slides-table.jsx b/src/components/util/multi-and-table/select-slides-table.jsx index 7e89241c..0747e08c 100644 --- a/src/components/util/multi-and-table/select-slides-table.jsx +++ b/src/components/util/multi-and-table/select-slides-table.jsx @@ -1,6 +1,8 @@ import { React, useEffect, useState } from "react"; import PropTypes from "prop-types"; import { useTranslation } from "react-i18next"; +import { Alert, Button, Card } from "react-bootstrap"; +import dayjs from "dayjs"; import { SelectSlideColumns } from "../../slide/slides-columns"; import DragAndDropTable from "../drag-and-drop-table/drag-and-drop-table"; import SlidesDropdown from "../forms/multiselect-dropdown/slides/slides-dropdown"; @@ -42,6 +44,47 @@ function SelectSlidesTable({ handleChange, name, slideId = "" }) { { skip: !slideId } ); + const changeOrderByStatus = () => { + const expired = []; + const active = []; + const future = []; + + const now = dayjs(new Date()); + + selectedData.forEach((entry) => { + const { published } = entry; + const from = published.from ? dayjs(published.from) : null; + const to = published.to ? dayjs(published.to) : null; + + if (to !== null && to.isBefore(now)) { + expired.push(entry); + } else if (from !== null && from.isAfter(now)) { + future.push(entry); + } else { + active.push(entry); + } + }); + + setSelectedData([...expired, ...active, ...future]); + }; + + const changeOrderByExpirationDate = () => { + const newData = [...selectedData]; + + newData.sort((a, b) => { + if (a.published?.to === null) { + return 1; + } + if (b.published?.to === null) { + return -1; + } + + return a.published.to > b.published?.to ? 1 : -1; + }); + + setSelectedData(newData); + }; + useEffect(() => { if (data) { setTotalItems(data["hydra:totalItems"]); @@ -110,6 +153,11 @@ function SelectSlidesTable({ handleChange, name, slideId = "" }) { editTarget: "slide", infoModalRedirect: "/playlist/edit", infoModalTitle: t("info-modal.slide-on-playlists"), + hideColumns: { + createdBy: true, + template: true, + playlists: true, + }, }); return ( @@ -135,6 +183,20 @@ function SelectSlidesTable({ handleChange, name, slideId = "" }) { callback={() => setPage(page + 1)} /> {t("edit-slides-help-text")} + +
+
{t("change-order-by-status-headline")}
+
+ + +
+
{t("change-order-by-status-helptext")}
+
+ )} diff --git a/src/components/util/publishing.jsx b/src/components/util/publishing.jsx index f3f99ab1..6d678227 100644 --- a/src/components/util/publishing.jsx +++ b/src/components/util/publishing.jsx @@ -21,11 +21,11 @@ function Publishing({ published }) { let publishedTo = null; if (published.from) { - publishedFrom = dayjs(published.from).locale(localeDa).format("LLL"); + publishedFrom = dayjs(published.from).locale(localeDa).format("lll"); } if (published.to) { - publishedTo = dayjs(published.to).locale(localeDa).format("LLL"); + publishedTo = dayjs(published.to).locale(localeDa).format("lll"); } return ( diff --git a/src/components/util/publishingStatus.jsx b/src/components/util/publishingStatus.jsx index b6d99fef..8eba5430 100644 --- a/src/components/util/publishingStatus.jsx +++ b/src/components/util/publishingStatus.jsx @@ -51,7 +51,6 @@ function PublishingStatus({ published }) { return () => {}; }, [published]); - // TODO: Fix to follow system colors. return ( {publishedState === PUBLISHED_EXPIRED && ( diff --git a/src/translations/da/common.json b/src/translations/da/common.json index 33559409..7737fdd7 100644 --- a/src/translations/da/common.json +++ b/src/translations/da/common.json @@ -26,9 +26,12 @@ }, "slides-list": { "columns": { + "name": "Titel", + "created-by": "Oprettet af", "template": "Skabelon", "slide-on-playlists": "Spillelister", - "publishing": "Udgivelse", + "publishing-from": "Udgivelse fra", + "publishing-to": "Udgivelse til", "status": "Status" }, "info-modal": { From 6995916987cf31ecba971edff8fcbc10ef4ae02e Mon Sep 17 00:00:00 2001 From: Troels Ugilt Jensen <6103205+tuj@users.noreply.github.com> Date: Mon, 1 Jul 2024 11:45:36 +0200 Subject: [PATCH 4/9] 1765: Set publishedFrom as now when creating new slides --- src/components/slide/slide-create.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/slide/slide-create.jsx b/src/components/slide/slide-create.jsx index 49e10aab..bb5a39d2 100644 --- a/src/components/slide/slide-create.jsx +++ b/src/components/slide/slide-create.jsx @@ -30,7 +30,7 @@ function SlideCreate() { theme: themeInfo, content: {}, media: [], - published: { from: null, to: null }, + published: { from: new Date(), to: null }, }; return ; From cd3ebed658c8c285a29486d62ffa0065f359e937 Mon Sep 17 00:00:00 2001 From: Troels Ugilt Jensen <6103205+tuj@users.noreply.github.com> Date: Mon, 1 Jul 2024 11:52:43 +0200 Subject: [PATCH 5/9] 1765: Added sorting --- CHANGELOG.md | 4 +- src/components/slide/slides-columns.jsx | 23 ++++++++++- .../multi-and-table/select-slides-table.jsx | 41 +++++++++++-------- src/components/util/table/table-header.jsx | 2 +- src/translations/da/common.json | 3 +- 5 files changed, 51 insertions(+), 22 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fda8c98f..59b89995 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,9 @@ All notable changes to this project will be documented in this file. ## [Unreleased] -- Added option to sort slides in playlist by published. +- Changed playlist.slides list columns. +- Set published.to to now when creating new slides. +- Added option to sort slides in playlist by published.to and status. - Changed from vite CJS to ESM. - Removed array spread. - Fixed HMR setup. diff --git a/src/components/slide/slides-columns.jsx b/src/components/slide/slides-columns.jsx index a7da0857..caec48c9 100644 --- a/src/components/slide/slides-columns.jsx +++ b/src/components/slide/slides-columns.jsx @@ -1,5 +1,6 @@ -import { React } from "react"; +import { React, useState } from "react"; import { useTranslation } from "react-i18next"; +import { Button } from "react-bootstrap"; import TemplateLabelInList from "../util/template-label-in-list"; import ListButton from "../util/list/list-button"; import Publishing from "../util/publishing.jsx"; @@ -17,6 +18,7 @@ import DateValue from "../util/date-value.jsx"; * @param {string} props.infoModalTitle - The info modal title. * @param {string} props.dataKey The data key for mapping the data. the list button * @param props.hideColumns + * @param props.sortColumns * @returns {object} The columns for the slides lists. */ function getSlidesColumns({ @@ -25,6 +27,7 @@ function getSlidesColumns({ infoModalTitle, dataKey, hideColumns = {}, + sortColumns = {}, }) { const { t } = useTranslation("common", { keyPrefix: "slides-list" }); @@ -85,6 +88,15 @@ function getSlidesColumns({ key: "publishing-to", content: ({ published }) => , label: t("columns.publishing-to"), + actions: sortColumns.publishedTo ? ( + + ) : null, }); } @@ -93,6 +105,15 @@ function getSlidesColumns({ key: "status", content: ({ published }) => , label: t("columns.status"), + actions: sortColumns.status ? ( + + ) : null, }); } diff --git a/src/components/util/multi-and-table/select-slides-table.jsx b/src/components/util/multi-and-table/select-slides-table.jsx index 0747e08c..34160601 100644 --- a/src/components/util/multi-and-table/select-slides-table.jsx +++ b/src/components/util/multi-and-table/select-slides-table.jsx @@ -1,7 +1,6 @@ import { React, useEffect, useState } from "react"; import PropTypes from "prop-types"; import { useTranslation } from "react-i18next"; -import { Alert, Button, Card } from "react-bootstrap"; import dayjs from "dayjs"; import { SelectSlideColumns } from "../../slide/slides-columns"; import DragAndDropTable from "../drag-and-drop-table/drag-and-drop-table"; @@ -12,6 +11,7 @@ import { useGetV2PlaylistsByIdQuery, } from "../../../redux/api/api.generated.ts"; import PlaylistGanttChart from "../../playlist/playlist-gantt-chart"; +import { displaySuccess } from "../list/toast-component/display-toast"; /** * A multiselect and table for slides. @@ -44,7 +44,7 @@ function SelectSlidesTable({ handleChange, name, slideId = "" }) { { skip: !slideId } ); - const changeOrderByStatus = () => { + const sortByStatus = () => { const expired = []; const active = []; const future = []; @@ -65,10 +65,18 @@ function SelectSlidesTable({ handleChange, name, slideId = "" }) { } }); - setSelectedData([...expired, ...active, ...future]); + const newData = [...expired, ...active, ...future]; + setSelectedData(newData); + + const order = selectedData.map((entry) => entry["@id"]); + const newOrder = newData.map((entry) => entry["@id"]); + + if (JSON.stringify(order) !== JSON.stringify(newOrder)) { + displaySuccess(t("data-changed")); + } }; - const changeOrderByExpirationDate = () => { + const sortByPublishedTo = () => { const newData = [...selectedData]; newData.sort((a, b) => { @@ -83,6 +91,13 @@ function SelectSlidesTable({ handleChange, name, slideId = "" }) { }); setSelectedData(newData); + + const order = selectedData.map((entry) => entry["@id"]); + const newOrder = newData.map((entry) => entry["@id"]); + + if (JSON.stringify(order) !== JSON.stringify(newOrder)) { + displaySuccess(t("data-changed")); + } }; useEffect(() => { @@ -158,6 +173,10 @@ function SelectSlidesTable({ handleChange, name, slideId = "" }) { template: true, playlists: true, }, + sortColumns: { + publishedTo: sortByPublishedTo, + status: sortByStatus, + }, }); return ( @@ -183,20 +202,6 @@ function SelectSlidesTable({ handleChange, name, slideId = "" }) { callback={() => setPage(page + 1)} /> {t("edit-slides-help-text")} - -
-
{t("change-order-by-status-headline")}
-
- - -
-
{t("change-order-by-status-helptext")}
-
- )} diff --git a/src/components/util/table/table-header.jsx b/src/components/util/table/table-header.jsx index 33ddfe13..9373cf1b 100644 --- a/src/components/util/table/table-header.jsx +++ b/src/components/util/table/table-header.jsx @@ -25,7 +25,7 @@ function TableHeader({ columns, draggable = false }) { )} {columns.map((column) => ( - {column.label} + {column.label}{column.actions} ))} diff --git a/src/translations/da/common.json b/src/translations/da/common.json index 7737fdd7..a6c43175 100644 --- a/src/translations/da/common.json +++ b/src/translations/da/common.json @@ -525,7 +525,8 @@ "remove-from-list": "Fjern fra liste", "error-messages": { "load-selected-slides-error": "Fejl da valgte slides skulle loades" - } + }, + "data-changed": "Rækkefølge ændret. Husk at gemme." }, "select-screens-table": { "columns": { From 39e8e392f40650c3f42b786f4a06483652407f3d Mon Sep 17 00:00:00 2001 From: Troels Ugilt Jensen <6103205+tuj@users.noreply.github.com> Date: Tue, 2 Jul 2024 11:31:49 +0200 Subject: [PATCH 6/9] 1765: Minor adjustments --- .../playlist/playlist-campaign-form.jsx | 2 +- src/components/playlist/playlists-columns.jsx | 28 ++++++++++++------- src/components/util/date-value.jsx | 10 ++----- .../list/toast-component/display-toast.jsx | 7 +++++ .../multi-and-table/select-slides-table.jsx | 7 +++-- src/translations/da/common.json | 4 ++- 6 files changed, 35 insertions(+), 23 deletions(-) diff --git a/src/components/playlist/playlist-campaign-form.jsx b/src/components/playlist/playlist-campaign-form.jsx index b30e3213..cbff6362 100644 --- a/src/components/playlist/playlist-campaign-form.jsx +++ b/src/components/playlist/playlist-campaign-form.jsx @@ -82,7 +82,7 @@ function PlaylistCampaignForm({ type="text" label={t("playlist-campaign-form.playlist-description-label")} placeholder={t( - "playlist-campaign-form.playlist-description-placeholder" + "playlist-campaign-form.playlist-description-placeholder", )} value={playlist.description} onChange={handleInput} diff --git a/src/components/playlist/playlists-columns.jsx b/src/components/playlist/playlists-columns.jsx index 6189db7f..cd71f724 100644 --- a/src/components/playlist/playlists-columns.jsx +++ b/src/components/playlist/playlists-columns.jsx @@ -5,6 +5,9 @@ import SelectColumnHoc from "../util/select-column-hoc"; import UserContext from "../../context/user-context"; import ListButton from "../util/list/list-button"; import Publishing from "../util/publishing.jsx"; +import DateValue from "../util/date-value.jsx"; +import { Button } from "react-bootstrap"; +import PublishingStatus from "../util/publishingStatus.jsx"; /** * Columns for playlists lists. @@ -28,16 +31,6 @@ function getPlaylistColumns({ }); const columns = [ - { - path: "published", - label: t("published"), - // eslint-disable-next-line react/prop-types - content: ({ publishedFrom, publishedTo, published }) => ( - - ), - }, { key: "slides", label: t("number-of-slides"), @@ -61,6 +54,21 @@ function getPlaylistColumns({ /> ), }, + { + key: "publishing-from", + content: ({ published }) => , + label: t("publishing-from"), + }, + { + key: "publishing-to", + content: ({ published }) => , + label: t("publishing-to"), + }, + { + key: "status", + content: ({ published }) => , + label: t("status"), + }, ]; return columns; diff --git a/src/components/util/date-value.jsx b/src/components/util/date-value.jsx index 5f6b4ce8..eff66b73 100644 --- a/src/components/util/date-value.jsx +++ b/src/components/util/date-value.jsx @@ -1,8 +1,6 @@ -import { React, useEffect } from "react"; +import { React } from "react"; import PropTypes from "prop-types"; import dayjs from "dayjs"; -import localeDa from "dayjs/locale/da"; -import localizedFormat from "dayjs/plugin/localizedFormat"; /** * @param {object} props The props. @@ -10,11 +8,7 @@ import localizedFormat from "dayjs/plugin/localizedFormat"; * @returns {object} Formatted date */ function DateValue({ date }) { - useEffect(() => { - dayjs.extend(localizedFormat); - }, []); - - return date ? dayjs(date).locale(localeDa).format("l LT") : ""; + return date ? dayjs(date).format("D/M/YYYY hh:mm") : ""; } DateValue.propTypes = { diff --git a/src/components/util/list/toast-component/display-toast.jsx b/src/components/util/list/toast-component/display-toast.jsx index cf77c712..8493df50 100644 --- a/src/components/util/list/toast-component/display-toast.jsx +++ b/src/components/util/list/toast-component/display-toast.jsx @@ -10,6 +10,13 @@ export function displaySuccess(text) { toast.success(displayText); } +/** @param {string} text The toast display text */ +export function displayWarning(text) { + const displayText = `${text} ${dayjs().format("HH:mm:ss")}`; + + toast.warning(displayText); +} + /** * @param {string} errorString - The toast display text * @param {object} error - The error diff --git a/src/components/util/multi-and-table/select-slides-table.jsx b/src/components/util/multi-and-table/select-slides-table.jsx index 34160601..15397d6a 100644 --- a/src/components/util/multi-and-table/select-slides-table.jsx +++ b/src/components/util/multi-and-table/select-slides-table.jsx @@ -11,7 +11,7 @@ import { useGetV2PlaylistsByIdQuery, } from "../../../redux/api/api.generated.ts"; import PlaylistGanttChart from "../../playlist/playlist-gantt-chart"; -import { displaySuccess } from "../list/toast-component/display-toast"; +import { displayWarning } from "../list/toast-component/display-toast"; /** * A multiselect and table for slides. @@ -72,7 +72,7 @@ function SelectSlidesTable({ handleChange, name, slideId = "" }) { const newOrder = newData.map((entry) => entry["@id"]); if (JSON.stringify(order) !== JSON.stringify(newOrder)) { - displaySuccess(t("data-changed")); + displayWarning(t("data-changed")); } }; @@ -96,7 +96,7 @@ function SelectSlidesTable({ handleChange, name, slideId = "" }) { const newOrder = newData.map((entry) => entry["@id"]); if (JSON.stringify(order) !== JSON.stringify(newOrder)) { - displaySuccess(t("data-changed")); + displayWarning(t("data-changed")); } }; @@ -192,6 +192,7 @@ function SelectSlidesTable({ handleChange, name, slideId = "" }) { /> {selectedData?.length > 0 && ( <> +
Afspilningsrækkefølge
Date: Thu, 15 Aug 2024 17:07:14 +0200 Subject: [PATCH 7/9] 1859: Fixed hour format --- src/components/util/date-value.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/util/date-value.jsx b/src/components/util/date-value.jsx index eff66b73..aed9235f 100644 --- a/src/components/util/date-value.jsx +++ b/src/components/util/date-value.jsx @@ -8,7 +8,7 @@ import dayjs from "dayjs"; * @returns {object} Formatted date */ function DateValue({ date }) { - return date ? dayjs(date).format("D/M/YYYY hh:mm") : ""; + return date ? dayjs(date).format("D/M/YYYY HH:mm") : ""; } DateValue.propTypes = { From 5b2fad63d2ebe39aa7c0d701b662dbad49315ee1 Mon Sep 17 00:00:00 2001 From: Troels Ugilt Jensen <6103205+tuj@users.noreply.github.com> Date: Thu, 15 Aug 2024 17:15:39 +0200 Subject: [PATCH 8/9] 1859: Clean up --- src/components/playlist/playlist-campaign-form.jsx | 2 +- src/components/slide/slides-columns.jsx | 11 +++++------ 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/components/playlist/playlist-campaign-form.jsx b/src/components/playlist/playlist-campaign-form.jsx index cbff6362..c2806682 100644 --- a/src/components/playlist/playlist-campaign-form.jsx +++ b/src/components/playlist/playlist-campaign-form.jsx @@ -1,5 +1,5 @@ import { React, useState } from "react"; -import { Alert, Button, Col, Form, Row } from "react-bootstrap"; +import { Button, Col, Form, Row } from "react-bootstrap"; import { useTranslation } from "react-i18next"; import { useNavigate } from "react-router-dom"; import PropTypes from "prop-types"; diff --git a/src/components/slide/slides-columns.jsx b/src/components/slide/slides-columns.jsx index caec48c9..425c4fad 100644 --- a/src/components/slide/slides-columns.jsx +++ b/src/components/slide/slides-columns.jsx @@ -1,13 +1,12 @@ -import { React, useState } from "react"; +import { React } from "react"; import { useTranslation } from "react-i18next"; import { Button } from "react-bootstrap"; import TemplateLabelInList from "../util/template-label-in-list"; import ListButton from "../util/list/list-button"; -import Publishing from "../util/publishing.jsx"; import ColumnHoc from "../util/column-hoc"; import SelectColumnHoc from "../util/select-column-hoc"; -import PublishingStatus from "../util/publishingStatus.jsx"; -import DateValue from "../util/date-value.jsx"; +import PublishingStatus from "../util/publishingStatus"; +import DateValue from "../util/date-value"; /** * Columns for slides lists. @@ -17,8 +16,8 @@ import DateValue from "../util/date-value.jsx"; * @param {string} props.infoModalRedirect - The url for redirecting in the info modal. * @param {string} props.infoModalTitle - The info modal title. * @param {string} props.dataKey The data key for mapping the data. the list button - * @param props.hideColumns - * @param props.sortColumns + * @param {object} props.hideColumns Columns to hide. + * @param {object} props.sortColumns Columns to sort. * @returns {object} The columns for the slides lists. */ function getSlidesColumns({ From f41da9105e5efcc26307f784d1338114a177f476 Mon Sep 17 00:00:00 2001 From: Troels Ugilt Jensen <6103205+tuj@users.noreply.github.com> Date: Fri, 16 Aug 2024 10:13:08 +0200 Subject: [PATCH 9/9] 1859: Fixed tests after changes in tables --- e2e/campaign.spec.js | 2 +- e2e/playlist.spec.js | 3 ++- e2e/slides.spec.js | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/e2e/campaign.spec.js b/e2e/campaign.spec.js index 5b1c7fef..3cb54cf9 100644 --- a/e2e/campaign.spec.js +++ b/e2e/campaign.spec.js @@ -336,7 +336,7 @@ test.describe("Campaign pages work", () => { .click(); await expect( page.locator("#slides-section").locator("tbody").locator("tr td") - ).toHaveCount(7); + ).toHaveCount(6); // Remove slide await page diff --git a/e2e/playlist.spec.js b/e2e/playlist.spec.js index 5590ac22..465a1e94 100644 --- a/e2e/playlist.spec.js +++ b/e2e/playlist.spec.js @@ -105,6 +105,7 @@ test.describe("Playlist create tests", () => { await expect(page.locator("#cancel_playlist")).not.toBeVisible(); }); }); + test.describe("Playlist list tests", () => { test.beforeEach(async ({ page }) => { await page.goto("/admin/playlist/list"); @@ -238,6 +239,6 @@ test.describe("Playlist list tests", () => { test("The correct amount of column headers loaded (playlist list)", async ({ page, }) => { - await expect(page.locator("thead").locator("th")).toHaveCount(6); + await expect(page.locator("thead").locator("th")).toHaveCount(8); }); }); diff --git a/e2e/slides.spec.js b/e2e/slides.spec.js index 9cc0de47..7895caeb 100644 --- a/e2e/slides.spec.js +++ b/e2e/slides.spec.js @@ -491,7 +491,7 @@ test.describe("Slides list works", () => { }); test("The correct amount of column headers loaded", async ({ page }) => { - await expect(page.locator("thead").locator("th")).toHaveCount(7); + await expect(page.locator("thead").locator("th")).toHaveCount(9); }); test("It removes all selected", async ({ page }) => {