@treely/strapi-slices 7.1.4 → 7.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/__mocks__/swr/index.d.ts +6 -0
- package/dist/__mocks__/swr/infinite.d.ts +4 -0
- package/dist/components/EventCard/EventCard.d.ts +6 -0
- package/dist/components/EventCard/index.d.ts +2 -0
- package/dist/components/EventCard/messages.de.d.ts +15 -0
- package/dist/components/EventCard/messages.en.d.ts +15 -0
- package/dist/components/StrapiLinkButton/StrapiLinkButton.d.ts +1 -0
- package/dist/index.d.ts +2 -1
- package/dist/models/SWRData.d.ts +5 -0
- package/dist/models/hooks/UseInfiniteDataHookProps.d.ts +11 -0
- package/dist/models/hooks/useEvents.d.ts +9 -0
- package/dist/models/strapi/StrapiEvent.d.ts +48 -0
- package/dist/rootMessages.de.d.ts +21 -0
- package/dist/rootMessages.en.d.ts +21 -0
- package/dist/slices/Events/Events.d.ts +11 -0
- package/dist/slices/Events/Events.stories.d.ts +4 -0
- package/dist/slices/Events/index.d.ts +2 -0
- package/dist/slices/Events/messages.de.d.ts +12 -0
- package/dist/slices/Events/messages.en.d.ts +12 -0
- package/dist/slices/QAndA/QAndA.d.ts +1 -0
- package/dist/strapi-slices.cjs.development.js +785 -44
- package/dist/strapi-slices.cjs.development.js.map +1 -1
- package/dist/strapi-slices.cjs.production.min.js +1 -1
- package/dist/strapi-slices.cjs.production.min.js.map +1 -1
- package/dist/strapi-slices.esm.js +786 -46
- package/dist/strapi-slices.esm.js.map +1 -1
- package/dist/test/strapiMocks/strapiEventMock.d.ts +3 -0
- package/dist/utils/getCountryFlag.d.ts +2 -0
- package/dist/utils/getMessages.d.ts +42 -0
- package/package.json +17 -3
- package/src/components/ContextProvider/ContextProvider.tsx +32 -4
- package/src/components/CustomerQuoteCard/CustomerQuoteCard.tsx +1 -0
- package/src/components/EventCard/EventCard.test.tsx +127 -0
- package/src/components/EventCard/EventCard.tsx +309 -0
- package/src/components/EventCard/index.ts +3 -0
- package/src/components/EventCard/messages.de.ts +16 -0
- package/src/components/EventCard/messages.en.ts +16 -0
- package/src/components/SliceRenderer/SliceRenderer.tsx +5 -0
- package/src/components/StrapiLinkButton/StrapiLinkButton.tsx +1 -0
- package/src/index.tsx +2 -0
- package/src/models/SWRData.ts +6 -0
- package/src/models/hooks/UseInfiniteDataHookProps.tsx +13 -0
- package/src/models/hooks/useEvents.ts +46 -0
- package/src/models/strapi/StrapiEvent.ts +51 -0
- package/src/rootMessages.de.ts +4 -0
- package/src/rootMessages.en.ts +4 -0
- package/src/slices/Events/Events.stories.tsx +36 -0
- package/src/slices/Events/Events.test.tsx +344 -0
- package/src/slices/Events/Events.tsx +440 -0
- package/src/slices/Events/index.ts +3 -0
- package/src/slices/Events/messages.de.ts +14 -0
- package/src/slices/Events/messages.en.ts +13 -0
- package/src/slices/QAndA/QAndA.stories.tsx +5 -0
- package/src/slices/QAndA/QAndA.test.tsx +1 -0
- package/src/slices/QAndA/QAndA.tsx +2 -1
- package/src/test/strapiMocks/strapiEventMock.ts +57 -0
- package/src/utils/getCountryFlag.test.ts +9 -0
- package/src/utils/getCountryFlag.ts +6 -0
- package/src/utils/strapiMediaUrl.ts +1 -2
|
@@ -24,6 +24,15 @@ declare const getMessages: (locale: string) => {
|
|
|
24
24
|
'sections.glossary.copyButtonLabel': string;
|
|
25
25
|
'sections.glossary.copySuccessMessage': string;
|
|
26
26
|
'sections.glossary.copyFailureMessage': string;
|
|
27
|
+
'sections.events.loadMore': string;
|
|
28
|
+
'sections.events.noUpcomingEvents': string;
|
|
29
|
+
'sections.events.noPastEvents': string;
|
|
30
|
+
'sections.events.eventsFilter.searchPlaceholder': string;
|
|
31
|
+
'sections.events.eventsFilter.eventType': string;
|
|
32
|
+
'sections.events.eventsFilter.language': string;
|
|
33
|
+
'sections.events.eventsFilter.sortBy.title': string;
|
|
34
|
+
'sections.events.eventsFilter.sortBy.newest': string;
|
|
35
|
+
'sections.events.eventsFilter.sortBy.oldest': string;
|
|
27
36
|
'sections.customerQuoteCard.more': string;
|
|
28
37
|
'sections.customerCard.more': string;
|
|
29
38
|
'sections.cta.backgroundShapesDark': string;
|
|
@@ -59,6 +68,18 @@ declare const getMessages: (locale: string) => {
|
|
|
59
68
|
'features.projectInfo.properties.year': string;
|
|
60
69
|
'features.portfolio.documentsDownloadList.projectDocuments': string;
|
|
61
70
|
'features.portfolio.documentsDownloadList.downloadDocument': string;
|
|
71
|
+
'sections.eventCard.recommendedEvent': string;
|
|
72
|
+
'sections.eventCard.buttonShowMore': string;
|
|
73
|
+
'sections.eventCard.buttonShowLess': string;
|
|
74
|
+
'sections.eventCard.eventType.conference': string;
|
|
75
|
+
'sections.eventCard.eventType.webinar': string;
|
|
76
|
+
'sections.eventCard.eventType.forestwalk': string;
|
|
77
|
+
'sections.eventCard.eventType.partnerevent': string;
|
|
78
|
+
'sections.eventCard.eventType.lunch&learn': string;
|
|
79
|
+
'sections.eventCard.eventType.fair': string;
|
|
80
|
+
'sections.eventCard.eventType.festival': string;
|
|
81
|
+
'sections.eventCard.eventType.roadshow': string;
|
|
82
|
+
'sections.eventCard.eventType.meetup': string;
|
|
62
83
|
'components.creditsAvailableBadge.text.yes': string;
|
|
63
84
|
'components.creditsAvailableBadge.text.some': string;
|
|
64
85
|
'components.creditsAvailableBadge.text.no': string;
|
|
@@ -89,6 +110,15 @@ declare const getMessages: (locale: string) => {
|
|
|
89
110
|
'sections.glossary.copyButtonLabel': string;
|
|
90
111
|
'sections.glossary.copySuccessMessage': string;
|
|
91
112
|
'sections.glossary.copyFailureMessage': string;
|
|
113
|
+
'sections.events.loadMore': string;
|
|
114
|
+
'sections.events.noUpcomingEvents': string;
|
|
115
|
+
'sections.events.noPastEvents': string;
|
|
116
|
+
'sections.eventsFilter.searchPlaceholder': string;
|
|
117
|
+
'sections.events.eventsFilter.eventType': string;
|
|
118
|
+
'sections.events.eventsFilter.language': string;
|
|
119
|
+
'sections.events.eventsFilter.sortBy.title': string;
|
|
120
|
+
'sections.events.eventsFilter.sortBy.newest': string;
|
|
121
|
+
'sections.events.eventsFilter.sortBy.oldest': string;
|
|
92
122
|
'sections.customerQuoteCard.more': string;
|
|
93
123
|
'sections.customerCard.more': string;
|
|
94
124
|
'sections.cta.backgroundShapes': string;
|
|
@@ -124,6 +154,18 @@ declare const getMessages: (locale: string) => {
|
|
|
124
154
|
'features.projectInfo.properties.year': string;
|
|
125
155
|
'features.portfolio.documentsDownloadList.projectDocuments': string;
|
|
126
156
|
'features.portfolio.documentsDownloadList.downloadDocument': string;
|
|
157
|
+
'sections.eventCard.recommendedEvent': string;
|
|
158
|
+
'sections.eventCard.buttonShowMore': string;
|
|
159
|
+
'sections.eventCard.buttonShowLess': string;
|
|
160
|
+
'sections.eventCard.eventType.conference': string;
|
|
161
|
+
'sections.eventCard.eventType.webinar': string;
|
|
162
|
+
'sections.eventCard.eventType.forestwalk': string;
|
|
163
|
+
'sections.eventCard.eventType.partnerevent': string;
|
|
164
|
+
'sections.eventCard.eventType.lunch&learn': string;
|
|
165
|
+
'sections.eventCard.eventType.fair': string;
|
|
166
|
+
'sections.eventCard.eventType.festival': string;
|
|
167
|
+
'sections.eventCard.eventType.roadshow': string;
|
|
168
|
+
'sections.eventCard.eventType.meetup': string;
|
|
127
169
|
'components.creditsAvailableBadge.text.yes': string;
|
|
128
170
|
'components.creditsAvailableBadge.text.some': string;
|
|
129
171
|
'components.creditsAvailableBadge.text.no': string;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@treely/strapi-slices",
|
|
3
|
-
"version": "7.
|
|
3
|
+
"version": "7.3.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"author": "Tree.ly FlexCo",
|
|
6
6
|
"description": "@treely/strapi-slices is a open source library maintained by Tree.ly.",
|
|
@@ -49,10 +49,17 @@
|
|
|
49
49
|
"trailingComma": "es5"
|
|
50
50
|
},
|
|
51
51
|
"jest": {
|
|
52
|
+
"transform": {
|
|
53
|
+
"^.+\\.(ts|tsx)$": "ts-jest"
|
|
54
|
+
},
|
|
52
55
|
"testEnvironment": "jsdom",
|
|
53
56
|
"setupFilesAfterEnv": [
|
|
54
57
|
"./src/test/setupTests.ts"
|
|
55
58
|
],
|
|
59
|
+
"extensionsToTreatAsEsm": [
|
|
60
|
+
".ts",
|
|
61
|
+
".tsx"
|
|
62
|
+
],
|
|
56
63
|
"moduleNameMapper": {
|
|
57
64
|
"^@/(.*)$": "<rootDir>/src/$1",
|
|
58
65
|
"\\.(css|less|scss|sass)$": "identity-obj-proxy"
|
|
@@ -134,12 +141,19 @@
|
|
|
134
141
|
"adblock-detect-react": "^1.1.0",
|
|
135
142
|
"axios": "^1.7.2",
|
|
136
143
|
"axios-cache-interceptor": "^1.5.3",
|
|
137
|
-
"boemly": "^7.
|
|
144
|
+
"boemly": "^7.5.0",
|
|
138
145
|
"embla-carousel-auto-scroll": "^8.5.1",
|
|
139
146
|
"embla-carousel-autoplay": "^8.5.1",
|
|
140
147
|
"embla-carousel-react": "^8.5.1",
|
|
141
148
|
"formik": "^2.4.5",
|
|
142
149
|
"framer-motion": "^10.16.5",
|
|
143
|
-
"mapbox-gl": "^2.15.0"
|
|
150
|
+
"mapbox-gl": "^2.15.0",
|
|
151
|
+
"swr": "^2.3.2"
|
|
152
|
+
},
|
|
153
|
+
"msw": {
|
|
154
|
+
"workerDirectory": [
|
|
155
|
+
"public",
|
|
156
|
+
".storybook/public"
|
|
157
|
+
]
|
|
144
158
|
}
|
|
145
159
|
}
|
|
@@ -3,6 +3,8 @@ import { createIntl, createIntlCache } from 'react-intl';
|
|
|
3
3
|
import { Global } from '@emotion/react';
|
|
4
4
|
import { GLOBAL_STYLE } from '../../constants/globalStyle';
|
|
5
5
|
import getMessages from '../../utils/getMessages';
|
|
6
|
+
import strapiClient from '../../integrations/strapi/strapiClient';
|
|
7
|
+
import { SWRConfig } from 'swr/_internal';
|
|
6
8
|
|
|
7
9
|
const cache = createIntlCache();
|
|
8
10
|
|
|
@@ -26,12 +28,38 @@ export const ContextProvider: React.FC<ContextProviderProps> = ({
|
|
|
26
28
|
children,
|
|
27
29
|
locale,
|
|
28
30
|
}: ContextProviderProps): JSX.Element => {
|
|
31
|
+
const fetcher = async (resource: any, init: any) => {
|
|
32
|
+
const response = await strapiClient.get(`${resource}`, {
|
|
33
|
+
...init,
|
|
34
|
+
headers: {},
|
|
35
|
+
});
|
|
36
|
+
// Check if the response was an error:
|
|
37
|
+
if (response.status < 200 || response.status >= 300) {
|
|
38
|
+
let errorData = { message: '' };
|
|
39
|
+
try {
|
|
40
|
+
errorData = await response.data;
|
|
41
|
+
} catch (error) {
|
|
42
|
+
errorData = {
|
|
43
|
+
message: `An unknown error occurred while fetching data.`,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
throw new Error(errorData.message); // Throwing the error will lead to onError being called
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return { body: await response.data, headers: response.headers };
|
|
50
|
+
};
|
|
29
51
|
return (
|
|
30
52
|
<>
|
|
31
|
-
<
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
53
|
+
<SWRConfig
|
|
54
|
+
value={{
|
|
55
|
+
fetcher,
|
|
56
|
+
}}
|
|
57
|
+
>
|
|
58
|
+
<Global styles={{ GLOBAL_STYLE }} />
|
|
59
|
+
<IntlContext.Provider value={intlFactory(locale)}>
|
|
60
|
+
{children}
|
|
61
|
+
</IntlContext.Provider>
|
|
62
|
+
</SWRConfig>
|
|
35
63
|
</>
|
|
36
64
|
);
|
|
37
65
|
};
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { render, screen, userEvent, waitFor } from '../../test/testUtils';
|
|
3
|
+
import EventCard from '.';
|
|
4
|
+
import { EventCardProps } from './EventCard';
|
|
5
|
+
import { strapiEventMock } from '../../test/strapiMocks/strapiEventMock';
|
|
6
|
+
import { mergeDeep } from '../../utils/mergeDeep';
|
|
7
|
+
|
|
8
|
+
const defaultProps: EventCardProps = {
|
|
9
|
+
event: strapiEventMock.attributes,
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
const setup = (props = {}) => {
|
|
13
|
+
const combinedProps = mergeDeep(defaultProps, props);
|
|
14
|
+
render(<EventCard {...combinedProps} />);
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
describe('The EventCard component', () => {
|
|
18
|
+
it('displays the event cards', () => {
|
|
19
|
+
setup();
|
|
20
|
+
|
|
21
|
+
expect(screen.getByText(defaultProps.event.title)).toBeInTheDocument();
|
|
22
|
+
|
|
23
|
+
expect(screen.getAllByRole('img')[0]).toHaveAttribute(
|
|
24
|
+
'alt',
|
|
25
|
+
defaultProps.event.image.alt
|
|
26
|
+
);
|
|
27
|
+
expect(screen.getAllByRole('img')[1]).toHaveAttribute(
|
|
28
|
+
'alt',
|
|
29
|
+
defaultProps.event.logo.alt
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
expect(screen.getByText('Event Description')).toBeInTheDocument();
|
|
33
|
+
expect(screen.getByText('Event Title')).toBeInTheDocument();
|
|
34
|
+
expect(screen.getByText('Meet Up')).toBeInTheDocument();
|
|
35
|
+
expect(screen.getByText('English')).toBeInTheDocument();
|
|
36
|
+
|
|
37
|
+
expect(screen.getByRole('link')).toHaveAttribute(
|
|
38
|
+
'href',
|
|
39
|
+
defaultProps.event.button.url
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
expect(screen.queryByText('Recommended')).not.toBeInTheDocument();
|
|
43
|
+
expect(screen.queryByText('Online')).not.toBeInTheDocument();
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it('displays start and end date and time correctly', () => {
|
|
47
|
+
setup();
|
|
48
|
+
|
|
49
|
+
expect(
|
|
50
|
+
screen.getByText(/3\/12\/2024\s*\|\s*08:30\s*-\s*09:30/i)
|
|
51
|
+
).toBeInTheDocument();
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('displays the speakers', async () => {
|
|
55
|
+
setup();
|
|
56
|
+
|
|
57
|
+
const firstSpeakerImage = screen.getAllByRole('img')[2];
|
|
58
|
+
expect(firstSpeakerImage).toHaveAttribute(
|
|
59
|
+
'alt',
|
|
60
|
+
defaultProps.event.speakers[0].image.alt
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
await waitFor(() => userEvent.hover(firstSpeakerImage));
|
|
64
|
+
await waitFor(() => expect(screen.getByText('John Doe')).toBeVisible());
|
|
65
|
+
|
|
66
|
+
const secondSpeakerImage = screen.getAllByRole('img')[3];
|
|
67
|
+
expect(secondSpeakerImage).toHaveAttribute(
|
|
68
|
+
'alt',
|
|
69
|
+
defaultProps.event.speakers[1].image.alt
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
await waitFor(() => userEvent.hover(secondSpeakerImage));
|
|
73
|
+
await waitFor(() => expect(screen.getByText('Jane Doe')).toBeVisible());
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it('displays optional props if they are defined in the slice', () => {
|
|
77
|
+
setup({
|
|
78
|
+
event: {
|
|
79
|
+
...defaultProps.event,
|
|
80
|
+
recommended: true,
|
|
81
|
+
online: true,
|
|
82
|
+
location: 'Vienna',
|
|
83
|
+
},
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
expect(
|
|
87
|
+
screen.getByText((content) => content.includes('Recommended'))
|
|
88
|
+
).toBeInTheDocument();
|
|
89
|
+
expect(screen.getByText('Online')).toBeInTheDocument();
|
|
90
|
+
expect(screen.getByText('Vienna')).toBeInTheDocument();
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it('displays the right country flag', () => {
|
|
94
|
+
setup();
|
|
95
|
+
expect(screen.getByText('🇬🇧')).toBeInTheDocument();
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it('displays multiple event types if they are defined in the slice', () => {
|
|
99
|
+
setup({
|
|
100
|
+
event: {
|
|
101
|
+
...defaultProps.event,
|
|
102
|
+
eventTypes: [
|
|
103
|
+
{ id: 1, eventType: 'Conference' },
|
|
104
|
+
{ id: 2, eventType: 'Meet Up' },
|
|
105
|
+
],
|
|
106
|
+
},
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
expect(screen.getByText('Conference')).toBeInTheDocument();
|
|
110
|
+
expect(screen.getByText('Meet Up')).toBeInTheDocument();
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it('displays multiple languages if they are defined in the slice', () => {
|
|
114
|
+
setup({
|
|
115
|
+
event: {
|
|
116
|
+
...defaultProps.event,
|
|
117
|
+
languages: [
|
|
118
|
+
{ id: 1, language: 'German', countryCode: 'de' },
|
|
119
|
+
{ id: 2, language: 'English', countryCode: 'en' },
|
|
120
|
+
],
|
|
121
|
+
},
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
expect(screen.getByText('German')).toBeInTheDocument();
|
|
125
|
+
expect(screen.getByText('English')).toBeInTheDocument();
|
|
126
|
+
});
|
|
127
|
+
});
|
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
import React, { useContext, useState } from 'react';
|
|
2
|
+
import {
|
|
3
|
+
Text,
|
|
4
|
+
Box,
|
|
5
|
+
Flex,
|
|
6
|
+
Heading,
|
|
7
|
+
Tooltip,
|
|
8
|
+
Tag,
|
|
9
|
+
useMediaQuery,
|
|
10
|
+
Button,
|
|
11
|
+
} from 'boemly';
|
|
12
|
+
import { css } from '@emotion/react';
|
|
13
|
+
import Image from 'next/image';
|
|
14
|
+
import StrapiLinkButton from '../StrapiLinkButton';
|
|
15
|
+
import {
|
|
16
|
+
BowlFood,
|
|
17
|
+
CalendarBlank,
|
|
18
|
+
CaretDown,
|
|
19
|
+
CaretRight,
|
|
20
|
+
CaretUp,
|
|
21
|
+
ChalkboardTeacher,
|
|
22
|
+
Confetti,
|
|
23
|
+
Handshake,
|
|
24
|
+
Headset,
|
|
25
|
+
Info,
|
|
26
|
+
Laptop,
|
|
27
|
+
MapPinLine,
|
|
28
|
+
PersonSimpleWalk,
|
|
29
|
+
ProjectorScreenChart,
|
|
30
|
+
Star,
|
|
31
|
+
UsersThree,
|
|
32
|
+
Webcam,
|
|
33
|
+
} from '@phosphor-icons/react';
|
|
34
|
+
import getCountryFlag from '../../utils/getCountryFlag';
|
|
35
|
+
import { BREAKPOINT_MD_QUERY } from '../../constants/breakpoints';
|
|
36
|
+
import StrapiEvent, { EventType } from '../../models/strapi/StrapiEvent';
|
|
37
|
+
import { IntlContext } from '../ContextProvider';
|
|
38
|
+
import strapiMediaUrl from '../../utils/strapiMediaUrl';
|
|
39
|
+
|
|
40
|
+
export interface EventCardProps {
|
|
41
|
+
event: StrapiEvent;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const MAX_LENGTH = 120;
|
|
45
|
+
const LOCATION_MAX_LENGTH = 28;
|
|
46
|
+
|
|
47
|
+
const getEventIcon = (eventType: string): JSX.Element => {
|
|
48
|
+
switch (eventType) {
|
|
49
|
+
case EventType.WEBINAR:
|
|
50
|
+
return <Webcam size={12} />;
|
|
51
|
+
case EventType.CONFERENCE:
|
|
52
|
+
return <Headset size={12} />;
|
|
53
|
+
case EventType.MEET_UP:
|
|
54
|
+
return <UsersThree size={12} />;
|
|
55
|
+
case EventType.FOREST_WALK:
|
|
56
|
+
return <PersonSimpleWalk size={12} />;
|
|
57
|
+
case EventType.PARTNER_EVENT:
|
|
58
|
+
return <Handshake size={12} />;
|
|
59
|
+
case EventType.LUNCH_AND_LEARN:
|
|
60
|
+
return <BowlFood size={12} />;
|
|
61
|
+
case EventType.FAIR:
|
|
62
|
+
return <ChalkboardTeacher size={12} />;
|
|
63
|
+
case EventType.FESTIVAL:
|
|
64
|
+
return <Confetti size={12} />;
|
|
65
|
+
case EventType.ROADSHOW:
|
|
66
|
+
return <ProjectorScreenChart size={12} />;
|
|
67
|
+
default:
|
|
68
|
+
return <Info size={12} weight="fill" />;
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
export const EventCard = ({ event }: EventCardProps): JSX.Element => {
|
|
73
|
+
const { formatDate, formatNumber, formatMessage } = useContext(IntlContext);
|
|
74
|
+
const [isExpanded, setIsExpanded] = useState(false);
|
|
75
|
+
const [mobile] = useMediaQuery(BREAKPOINT_MD_QUERY);
|
|
76
|
+
|
|
77
|
+
const toggleText = () => {
|
|
78
|
+
setIsExpanded(!isExpanded);
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
const isLocationTooLong =
|
|
82
|
+
(event.location?.length ?? 0) >= LOCATION_MAX_LENGTH;
|
|
83
|
+
|
|
84
|
+
return (
|
|
85
|
+
<Box
|
|
86
|
+
borderRadius={['xl', null, null, '2xl']}
|
|
87
|
+
height="full"
|
|
88
|
+
width="full"
|
|
89
|
+
border="1px solid var(--boemly-colors-gray-200)"
|
|
90
|
+
background="white"
|
|
91
|
+
>
|
|
92
|
+
<Box
|
|
93
|
+
position="relative"
|
|
94
|
+
width="full"
|
|
95
|
+
height={['32', null, null, '44']}
|
|
96
|
+
borderTopRadius={['xl', null, null, '2xl']}
|
|
97
|
+
css={css`
|
|
98
|
+
& span,
|
|
99
|
+
div,
|
|
100
|
+
img {
|
|
101
|
+
border-top-left-radius: inherit;
|
|
102
|
+
border-top-right-radius: inherit;
|
|
103
|
+
}
|
|
104
|
+
`}
|
|
105
|
+
>
|
|
106
|
+
<Image
|
|
107
|
+
src={strapiMediaUrl(event.image?.img, 'medium')}
|
|
108
|
+
alt={event.image?.alt}
|
|
109
|
+
fill
|
|
110
|
+
style={{
|
|
111
|
+
objectFit: event.image?.objectFit || 'cover',
|
|
112
|
+
}}
|
|
113
|
+
/>
|
|
114
|
+
|
|
115
|
+
<Box
|
|
116
|
+
position="absolute"
|
|
117
|
+
top={['6', null, null, '8']}
|
|
118
|
+
right={['6', null, null, '8']}
|
|
119
|
+
zIndex="1"
|
|
120
|
+
width={['12', null, null, '16']}
|
|
121
|
+
height={['12', null, null, '16']}
|
|
122
|
+
>
|
|
123
|
+
<Image
|
|
124
|
+
src={strapiMediaUrl(event.logo.img, 'medium')}
|
|
125
|
+
alt={event.logo.alt}
|
|
126
|
+
fill
|
|
127
|
+
style={{
|
|
128
|
+
objectFit: event.logo.objectFit || 'contain',
|
|
129
|
+
borderRadius: 'var(--boemly-radii-md)',
|
|
130
|
+
border:
|
|
131
|
+
'1px solid, var(--whiteAlpha-700, rgba(255, 255, 255, 0.64))',
|
|
132
|
+
}}
|
|
133
|
+
/>
|
|
134
|
+
</Box>
|
|
135
|
+
</Box>
|
|
136
|
+
<Flex
|
|
137
|
+
flexDir="column"
|
|
138
|
+
p={['6', null, null, '8']}
|
|
139
|
+
h="calc(var(--boemly-sizes-full) - var(--boemly-sizes-44))"
|
|
140
|
+
>
|
|
141
|
+
<Flex flexDir="row" mb="4" gap="2" flexWrap="wrap">
|
|
142
|
+
{event.recommended ? (
|
|
143
|
+
<Flex mb={['2', null, null, '0']}>
|
|
144
|
+
<Tag backgroundColor="green.600">
|
|
145
|
+
<Star size={12} weight="fill" color="white" />
|
|
146
|
+
|
|
147
|
+
<Text size="xsLowBold" color="white">
|
|
148
|
+
{formatMessage({
|
|
149
|
+
id: 'sections.eventCard.recommendedEvent',
|
|
150
|
+
})}
|
|
151
|
+
</Text>
|
|
152
|
+
</Tag>
|
|
153
|
+
</Flex>
|
|
154
|
+
) : (
|
|
155
|
+
<></>
|
|
156
|
+
)}
|
|
157
|
+
<Flex flexWrap="wrap" gap="2">
|
|
158
|
+
{event.eventTypes.map((e) => (
|
|
159
|
+
<Tag key={e.id}>
|
|
160
|
+
{getEventIcon(e.eventType)}
|
|
161
|
+
<Text size="xsLowBold" color="gray.800">
|
|
162
|
+
{formatMessage({
|
|
163
|
+
id: `sections.eventCard.eventType.${e.eventType
|
|
164
|
+
.toLowerCase()
|
|
165
|
+
.replace(/\s+/g, '')}`,
|
|
166
|
+
})}
|
|
167
|
+
</Text>
|
|
168
|
+
</Tag>
|
|
169
|
+
))}
|
|
170
|
+
{event.languages.map(({ id, language, countryCode }) => (
|
|
171
|
+
<Tag key={id}>
|
|
172
|
+
{getCountryFlag(countryCode)}
|
|
173
|
+
<Text size="xsLowBold" color="gray.800">
|
|
174
|
+
{language}
|
|
175
|
+
</Text>
|
|
176
|
+
</Tag>
|
|
177
|
+
))}
|
|
178
|
+
</Flex>
|
|
179
|
+
</Flex>
|
|
180
|
+
<Heading>{event.title}</Heading>
|
|
181
|
+
<Flex
|
|
182
|
+
gap={isLocationTooLong ? '2' : ['2', null, null, '6']}
|
|
183
|
+
alignItems={mobile || isLocationTooLong ? 'flex-start' : 'center'}
|
|
184
|
+
my="4"
|
|
185
|
+
flexDir={mobile || isLocationTooLong ? 'column' : 'row'}
|
|
186
|
+
>
|
|
187
|
+
{event.online && (
|
|
188
|
+
<Flex gap="2" alignItems="center">
|
|
189
|
+
<Laptop size={20} color={'var(--boemly-colors-primary-700)'} />
|
|
190
|
+
<Text size={['xsLowBold', null, null, 'smLowBold']}>Online</Text>
|
|
191
|
+
</Flex>
|
|
192
|
+
)}
|
|
193
|
+
{event.location && (
|
|
194
|
+
<Flex gap="2" alignItems="center">
|
|
195
|
+
<MapPinLine
|
|
196
|
+
size={20}
|
|
197
|
+
color={'var(--boemly-colors-primary-700)'}
|
|
198
|
+
weight="fill"
|
|
199
|
+
/>
|
|
200
|
+
<Text size={['xsLowBold', null, null, 'smLowBold']}>
|
|
201
|
+
{event.location}
|
|
202
|
+
</Text>
|
|
203
|
+
</Flex>
|
|
204
|
+
)}
|
|
205
|
+
<Flex alignItems="center" gap="2">
|
|
206
|
+
<CalendarBlank
|
|
207
|
+
size={20}
|
|
208
|
+
color={'var(--boemly-colors-primary-700)'}
|
|
209
|
+
/>
|
|
210
|
+
<Text size={['xsLowBold', null, null, 'smLowBold']}>
|
|
211
|
+
{formatDate(event.start, {
|
|
212
|
+
year: 'numeric',
|
|
213
|
+
month: '2-digit',
|
|
214
|
+
day: '2-digit',
|
|
215
|
+
})}{' '}
|
|
216
|
+
|{' '}
|
|
217
|
+
{formatNumber(new Date(event.start).getUTCHours(), {
|
|
218
|
+
minimumIntegerDigits: 2,
|
|
219
|
+
})}
|
|
220
|
+
:
|
|
221
|
+
{formatNumber(new Date(event.start).getUTCMinutes(), {
|
|
222
|
+
minimumIntegerDigits: 2,
|
|
223
|
+
})}{' '}
|
|
224
|
+
-{' '}
|
|
225
|
+
{formatNumber(new Date(event.end).getUTCHours(), {
|
|
226
|
+
minimumIntegerDigits: 2,
|
|
227
|
+
})}
|
|
228
|
+
:
|
|
229
|
+
{formatNumber(new Date(event.end).getUTCMinutes(), {
|
|
230
|
+
minimumIntegerDigits: 2,
|
|
231
|
+
})}
|
|
232
|
+
</Text>
|
|
233
|
+
</Flex>
|
|
234
|
+
</Flex>
|
|
235
|
+
<Text
|
|
236
|
+
mb={mobile ? '0' : '7'}
|
|
237
|
+
size={['xsRegularNormal', null, null, 'smRegularNormal']}
|
|
238
|
+
>
|
|
239
|
+
{isExpanded || !mobile
|
|
240
|
+
? event.description
|
|
241
|
+
: `${event.description.substring(0, MAX_LENGTH)}...`}
|
|
242
|
+
</Text>
|
|
243
|
+
{event.description.length > MAX_LENGTH && mobile && (
|
|
244
|
+
<Flex justifyContent="flex-start">
|
|
245
|
+
<Button
|
|
246
|
+
mt="2"
|
|
247
|
+
onClick={toggleText}
|
|
248
|
+
variant="link"
|
|
249
|
+
rightIcon={
|
|
250
|
+
isExpanded ? <CaretUp size="12" /> : <CaretDown size="12" />
|
|
251
|
+
}
|
|
252
|
+
>
|
|
253
|
+
{formatMessage(
|
|
254
|
+
isExpanded
|
|
255
|
+
? {
|
|
256
|
+
id: 'sections.eventCard.buttonShowLess',
|
|
257
|
+
}
|
|
258
|
+
: { id: 'sections.eventCard.buttonShowMore' }
|
|
259
|
+
)}
|
|
260
|
+
</Button>
|
|
261
|
+
</Flex>
|
|
262
|
+
)}
|
|
263
|
+
<Flex
|
|
264
|
+
mt={mobile ? '7' : 'auto'}
|
|
265
|
+
justifyContent={mobile ? undefined : 'space-between'}
|
|
266
|
+
flexDir={mobile ? 'column-reverse' : 'row'}
|
|
267
|
+
gap={mobile ? '4' : '0'}
|
|
268
|
+
>
|
|
269
|
+
<Flex width={mobile ? 'full' : 'auto'}>
|
|
270
|
+
<StrapiLinkButton
|
|
271
|
+
key={event.button.id}
|
|
272
|
+
size="md"
|
|
273
|
+
variant={event.buttonVariant}
|
|
274
|
+
link={event.button}
|
|
275
|
+
rightIcon={<CaretRight size="10" />}
|
|
276
|
+
width="full"
|
|
277
|
+
/>
|
|
278
|
+
</Flex>
|
|
279
|
+
<Flex flexDir="row" gap="2">
|
|
280
|
+
{event.speakers.map((speaker) => (
|
|
281
|
+
<Box key={speaker.id}>
|
|
282
|
+
<Box
|
|
283
|
+
width={['10', null, null, '12']}
|
|
284
|
+
height={['10', null, null, '12']}
|
|
285
|
+
position="relative"
|
|
286
|
+
borderRadius="2xl"
|
|
287
|
+
>
|
|
288
|
+
<Tooltip label={speaker.name}>
|
|
289
|
+
<Image
|
|
290
|
+
src={strapiMediaUrl(speaker.image.img, 'medium')}
|
|
291
|
+
alt={speaker.image.alt}
|
|
292
|
+
fill
|
|
293
|
+
style={{
|
|
294
|
+
objectFit: speaker.image.objectFit || 'cover',
|
|
295
|
+
borderRadius: 'var(--boemly-radii-md)',
|
|
296
|
+
border:
|
|
297
|
+
'1px solid, var(--whiteAlpha-700, rgba(255, 255, 255, 0.64))',
|
|
298
|
+
}}
|
|
299
|
+
/>
|
|
300
|
+
</Tooltip>
|
|
301
|
+
</Box>
|
|
302
|
+
</Box>
|
|
303
|
+
))}
|
|
304
|
+
</Flex>
|
|
305
|
+
</Flex>
|
|
306
|
+
</Flex>
|
|
307
|
+
</Box>
|
|
308
|
+
);
|
|
309
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
const messagesDe = {
|
|
2
|
+
'sections.eventCard.recommendedEvent': 'Empfohlene Veranstaltung',
|
|
3
|
+
'sections.eventCard.buttonShowMore': 'Mehr anzeigen',
|
|
4
|
+
'sections.eventCard.buttonShowLess': 'Weniger anzeigen',
|
|
5
|
+
|
|
6
|
+
'sections.eventCard.eventType.conference': 'Konferenz',
|
|
7
|
+
'sections.eventCard.eventType.webinar': 'Webinar',
|
|
8
|
+
'sections.eventCard.eventType.forestwalk': 'Waldspaziergang',
|
|
9
|
+
'sections.eventCard.eventType.partnerevent': 'Partnerveranstaltung',
|
|
10
|
+
'sections.eventCard.eventType.lunch&learn': 'Mittagessen & Lernen',
|
|
11
|
+
'sections.eventCard.eventType.fair': 'Messe',
|
|
12
|
+
'sections.eventCard.eventType.festival': 'Festival',
|
|
13
|
+
'sections.eventCard.eventType.roadshow': 'Roadshow',
|
|
14
|
+
'sections.eventCard.eventType.meetup': 'Meet Up',
|
|
15
|
+
};
|
|
16
|
+
export default messagesDe;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
const messagesEn = {
|
|
2
|
+
'sections.eventCard.recommendedEvent': 'Recommended Event',
|
|
3
|
+
'sections.eventCard.buttonShowMore': 'Show More',
|
|
4
|
+
'sections.eventCard.buttonShowLess': 'Show Less',
|
|
5
|
+
|
|
6
|
+
'sections.eventCard.eventType.conference': 'Conference',
|
|
7
|
+
'sections.eventCard.eventType.webinar': 'Webinar',
|
|
8
|
+
'sections.eventCard.eventType.forestwalk': 'Forest Walk',
|
|
9
|
+
'sections.eventCard.eventType.partnerevent': 'Partner Event',
|
|
10
|
+
'sections.eventCard.eventType.lunch&learn': 'Lunch & Learn',
|
|
11
|
+
'sections.eventCard.eventType.fair': 'Fair',
|
|
12
|
+
'sections.eventCard.eventType.festival': 'Festival',
|
|
13
|
+
'sections.eventCard.eventType.roadshow': 'Roadshow',
|
|
14
|
+
'sections.eventCard.eventType.meetup': 'Meet Up',
|
|
15
|
+
};
|
|
16
|
+
export default messagesEn;
|
|
@@ -41,6 +41,7 @@ import CarouselMarqueeBanner from '../../slices/CarouselMarqueeBanner';
|
|
|
41
41
|
import Locale from '../../models/Locale';
|
|
42
42
|
import { ContextProvider } from '../ContextProvider';
|
|
43
43
|
import Timeline from '../../slices/Timeline';
|
|
44
|
+
import Events from '../../slices/Events';
|
|
44
45
|
|
|
45
46
|
export interface CustomSliceProps {
|
|
46
47
|
slice: any;
|
|
@@ -309,6 +310,10 @@ export const SliceRenderer = ({
|
|
|
309
310
|
slice={slice}
|
|
310
311
|
/>
|
|
311
312
|
);
|
|
313
|
+
case 'sections.events':
|
|
314
|
+
return (
|
|
315
|
+
<Events key={`${slice.__component}-${slice.id}`} slice={slice} />
|
|
316
|
+
);
|
|
312
317
|
default:
|
|
313
318
|
if (CustomSlice) {
|
|
314
319
|
return (
|