Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ All notable changes to this project will be documented in this file.
Release tarball.
- Changed code analysis tool from psalm to phpstan.
- Changed src/Controller/Api/AuthOidcController.php to get session from request.
- Aligned with release/2.7.0.
- Fixed instagram-feed template display when no entries.
- Notified FeedType: Added support for video media and cleanup implementation.

Expand Down
7 changes: 6 additions & 1 deletion assets/admin/components/feed-sources/feed-source-manager.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,12 @@ function FeedSourceManager({
value: "App\\Feed\\BrndFeedType",
title: t("brnd-feed-type.title"),
key: "BrndFeedType",
secretsDefault: {},
secretsDefault: {
api_base_uri: "",
company_id: "",
api_auth_key: "",
api_version: "1.0",
},
},
];

Expand Down
15 changes: 15 additions & 0 deletions assets/admin/components/feed-sources/templates/brnd-feed-type.jsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
import React from "react";
import { useTranslation } from "react-i18next";
import FormInput from "../../util/forms/form-input";
import Select from "../../util/forms/select";

const BrndFeedType = ({ handleInput, formStateObject, mode }) => {
const { t } = useTranslation("common", {
keyPrefix: "brnd-feed-type",
});
const apiVersionOptions = [
{ key: "api-version-1-0", title: "1.0", value: "1.0" },
{ key: "api-version-2-0", title: "2.0", value: "2.0" },
];

return (
<>
Expand Down Expand Up @@ -43,6 +48,16 @@ const BrndFeedType = ({ handleInput, formStateObject, mode }) => {
}
value={formStateObject?.api_auth_key}
/>

<Select
name="api_version"
formGroupClasses="mb-2"
label={t("api-version")}
options={apiVersionOptions}
allowNull={false}
onChange={handleInput}
value={formStateObject?.api_version || "1.0"}
/>
</>
);
};
Expand Down
3 changes: 2 additions & 1 deletion assets/admin/translations/da/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -1166,7 +1166,8 @@
"company-id": "Company ID",
"api-auth-key": "API Auth Key",
"values-info": "Værdierne Company ID og API Auth Key udleveres af BRND. Ret henvendelse til BRND support.",
"redacted-value-input-placeholder": "Skjult værdi"
"redacted-value-input-placeholder": "Skjult værdi",
"api-version": "API Version"
},
"event-database-api-v2-feed-type": {
"title": "Event databasen v.2",
Expand Down
15 changes: 15 additions & 0 deletions assets/shared/templates/brnd.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,24 @@
"key": "layout1",
"title": "Sportcenter Dagsprogram",
"value": "sportcenter-today"
},
{
"key": "layout2",
"title": "Idrætsanlæg - Dagsprogram",
"value": "idraetsanlaeg"
}
]
},
{
"key": "brnd-form-duration",
"input": "duration",
"name": "duration",
"type": "number",
"min": "1",
"label": "Varighed pr. slide (i sekunder)",
"helpText": "Her skal du skrive varigheden på slidet.",
"formGroupClasses": "col-md-6 mb-3"
},
{
"key": "brnd-form-form-14",
"input": "select",
Expand Down
10 changes: 10 additions & 0 deletions assets/shared/templates/brnd.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
getFirstMediaUrlFromField,
ThemeStyles,
} from "../slide-utils/slide-util";
import BrndIdraetsanlaeg from "./brnd/brnd-idraetsanlaeg";
import BrndSportcenterToday from "./brnd/brnd-sportcenter-today";
import GlobalStyles from "../slide-utils/GlobalStyles";
import "./brnd/brnd.scss";
Expand Down Expand Up @@ -105,6 +106,15 @@ function Brnd({ slide, content, run, slideDone, executionId }) {
getTitle={getTitle}
/>
)}
{layout === "idraetsanlaeg" && (
<BrndIdraetsanlaeg
bookings={feedData.bookings}
content={content}
templateClasses={classes}
templateRootStyle={rootStyle}
getTitle={getTitle}
/>
)}
</IntlProvider>
<ThemeStyles id={executionId} css={slide?.theme?.cssStyles} />
<GlobalStyles />
Expand Down
245 changes: 245 additions & 0 deletions assets/shared/templates/brnd/brnd-idraetsanlaeg.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,245 @@
import React, { Fragment, useEffect, useState } from "react";
import PropTypes from "prop-types";
import dayjs from "dayjs";
import localeDa from "dayjs/locale/da";
import localizedFormat from "dayjs/plugin/localizedFormat";
import styled from "styled-components";

/**
* BRND Idrætsanlæg dagsprogram.
*
* @param {object} props Component props.
* @param {object} props.content Slide content.
* @param {Array} props.bookings Booking entries.
* @param {string[]} [props.templateClasses] Template class names. Default is `[]`.
* @param {Function} props.getTitle Function to normalize displayed text.
* @returns {JSX.Element} BRND Idrætsanlæg Dagsprogram layout.
*/
function BrndIdraetsanlaeg({
content,
bookings,
templateClasses = [],
getTitle,
}) {
const [currentDate, setCurrentDate] = useState(new Date());
const { title = "" } = content;

useEffect(() => {
dayjs.extend(localizedFormat);
const dateAndTimeInterval = setInterval(
() => setCurrentDate(new Date()),
1000,
);

return () => clearInterval(dateAndTimeInterval);
}, []);

// Sort and keep only current/future bookings from today.
const getSortedBookings = (data) => {
const now = dayjs();

return data
.filter((entry) => {
const startDate = dayjs(entry.startTime * 1000);

return entry.endTime > now.unix() && startDate.date() === now.date();
})
.sort((a, b) => a.startTime - b.startTime);
};

const capitalize = (text) => {
if (!text) {
return "";
}

return text.charAt(0).toUpperCase() + text.slice(1);
};

return (
<Wrapper
className={`template-brnd brnd-idraetsanlaeg ${templateClasses.join(
" ",
)}`}
>
<Title className="title">{title}</Title>
<DateRow className="header-date">
{currentDate &&
capitalize(dayjs().locale(localeDa).format("dddd D. MMMM HH:mm"))}
</DateRow>

<Content className="schedule">
<ContentItemsWrapper className="schedule-header">
<ContentHeaderItem className="schedule-header-item">
Hvornår
</ContentHeaderItem>
<ContentHeaderItem className="schedule-header-item">
Hvor
</ContentHeaderItem>
<ContentHeaderItem className="schedule-header-item">
Hvad
</ContentHeaderItem>
<ContentHeaderItem className="schedule-header-item">
Hvem
</ContentHeaderItem>
<ContentHeaderItem className="schedule-header-item">
Bemærkninger
</ContentHeaderItem>
</ContentItemsWrapper>

<ContentItemsWrapper className="schedule-rows">
{bookings?.length > 0 &&
getSortedBookings(bookings).map((entry, rowIndex) => (
<Fragment key={entry.bookingcode}>
<ContentItem
className={`content-item content-item-time ${
rowIndex % 2 === 0 ? "row-even" : "row-odd"
}`}
>
{dayjs(entry.startTime * 1000)
.locale(localeDa)
.format("LT")}
{entry.endTime && (
<>
<span> - </span>
{dayjs(entry.endTime * 1000)
.locale(localeDa)
.format("LT")}
</>
)}
</ContentItem>
<ContentItem
className={`content-item content-item-area ${
rowIndex % 2 === 0 ? "row-even" : "row-odd"
}`}
>
{getTitle(entry.area)}
</ContentItem>
<ContentItem
className={`content-item content-item-activity ${
rowIndex % 2 === 0 ? "row-even" : "row-odd"
}`}
>
{getTitle(entry.activity)}
</ContentItem>
<ContentItem
className={`content-item content-item-booking-by ${
rowIndex % 2 === 0 ? "row-even" : "row-odd"
}`}
>
{getTitle(entry.bookingBy)}
</ContentItem>
<ContentItem
className={`content-item content-item-remarks ${
rowIndex % 2 === 0 ? "row-even" : "row-odd"
}`}
>
{entry.remarks ?? ""}
</ContentItem>
</Fragment>
))}
</ContentItemsWrapper>
</Content>
</Wrapper>
);
}

const Wrapper = styled.div`
font-family: var(--font-family-base);
font-size: var(--font-size-base);
overflow: hidden;
height: 100%;
background-repeat: no-repeat;
background-size: cover;
background-color: var(--color-white);
color: var(--color-white);
display: grid;
grid-template-areas:
"title"
"date"
"content";
grid-template-rows: auto auto 1fr;
padding: 0;
`;

const DateRow = styled.div`
grid-area: date;
background-color: rgb(0, 12, 46);
color: var(--color-white);
text-align: center;
padding: 0 calc(var(--padding-size-base) * 0.7)
calc(var(--padding-size-base) * 0.7);
font-size: var(--h4-font-size);
font-weight: var(--font-weight-light);
`;

const Title = styled.div`
background-color: rgb(0, 12, 46);
font-size: var(--h4-font-size);
font-weight: var(--font-weight-bold);
text-align: center;
text-transform: uppercase;
color: var(--color-white);
padding: calc(var(--padding-size-base) * 0.7);
`;

const Content = styled.div`
background-color: rgb(0, 12, 46);
grid-area: content;
`;

const ContentItemsWrapper = styled.div`
display: grid;
grid-template-columns: 0.9fr 1fr 1.1fr 1.5fr 1.7fr;
`;

const ContentItem = styled.div`
display: flex;
align-items: center;
color: var(--color-black);
padding: calc(var(--padding-size-base) * 0.55);

&.row-even {
background-color: #f1f3f5;
}

&.row-odd {
background-color: var(--color-white);
}
`;

const ContentHeaderItem = styled.div`
display: flex;
align-items: center;
border-bottom: 1px solid var(--color-grey-600);
border-top: 1px solid var(--color-grey-600);
background-color: rgb(0, 12, 46);
color: var(--color-white);
font-weight: var(--font-weight-bold);
padding: calc(var(--padding-size-base) * 0.55);
`;

BrndIdraetsanlaeg.propTypes = {
templateClasses: PropTypes.arrayOf(PropTypes.string),
bookings: PropTypes.arrayOf(
PropTypes.shape({
bookingcode: PropTypes.string.isRequired,
remarks: PropTypes.string,
startTime: PropTypes.number.isRequired,
endTime: PropTypes.number,
complex: PropTypes.string.isRequired,
area: PropTypes.string.isRequired,
facility: PropTypes.string.isRequired,
activity: PropTypes.string.isRequired,
status: PropTypes.string.isRequired,
checkIn: PropTypes.bool,
bookingBy: PropTypes.string.isRequired,
changingRooms: PropTypes.string,
}),
).isRequired,
content: PropTypes.shape({
title: PropTypes.string,
}).isRequired,
getTitle: PropTypes.func.isRequired,
};

export default BrndIdraetsanlaeg;
Loading