@treely/strapi-slices 5.11.2 → 5.12.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.
Files changed (39) hide show
  1. package/dist/components/ContextProvider/ContextProvider.d.ts +1 -2
  2. package/dist/constants/strapi.d.ts +1 -0
  3. package/dist/index.d.ts +6 -3
  4. package/dist/integrations/strapi/getAllSlugsFromStrapi.d.ts +10 -0
  5. package/dist/integrations/strapi/getStaticPathsFromStrapi.d.ts +1 -0
  6. package/dist/integrations/strapi/getStaticPropsFromStrapi.d.ts +1 -0
  7. package/dist/integrations/strapi/getStrapiCollectionType.d.ts +10 -0
  8. package/dist/integrations/strapi/getStrapiSingleType.d.ts +8 -0
  9. package/dist/models/Locale.d.ts +3 -1
  10. package/dist/models/LocalizedEntity.d.ts +7 -0
  11. package/dist/models/strapi/StrapiGlobal.d.ts +2 -0
  12. package/dist/strapi-slices.cjs.development.js +207 -35
  13. package/dist/strapi-slices.cjs.development.js.map +1 -1
  14. package/dist/strapi-slices.cjs.production.min.js +1 -1
  15. package/dist/strapi-slices.cjs.production.min.js.map +1 -1
  16. package/dist/strapi-slices.esm.js +207 -38
  17. package/dist/strapi-slices.esm.js.map +1 -1
  18. package/dist/utils/getMessages.d.ts +142 -0
  19. package/package.json +1 -1
  20. package/src/components/ContextProvider/ContextProvider.tsx +3 -5
  21. package/src/components/MinimalProviders/MinimalProviders.tsx +2 -8
  22. package/src/components/portfolio/SmallCheckout/SmallCheckout.tsx +33 -24
  23. package/src/constants/strapi.ts +2 -0
  24. package/src/index.tsx +8 -2
  25. package/src/integrations/strapi/getAllSlugsFromStrapi.test.ts +33 -0
  26. package/src/integrations/strapi/getAllSlugsFromStrapi.ts +50 -0
  27. package/src/integrations/strapi/getPortfolioProjects.test.ts +24 -0
  28. package/src/integrations/strapi/getPortfolioProjects.ts +25 -5
  29. package/src/integrations/strapi/getStaticPathsFromStrapi.ts +1 -0
  30. package/src/integrations/strapi/getStaticPropsFromStrapi.ts +1 -0
  31. package/src/integrations/strapi/getStrapiCollectionType.test.ts +65 -0
  32. package/src/integrations/strapi/getStrapiCollectionType.ts +61 -0
  33. package/src/integrations/strapi/getStrapiSingleType.test.ts +53 -0
  34. package/src/integrations/strapi/getStrapiSingleType.ts +50 -0
  35. package/src/models/Locale.ts +3 -1
  36. package/src/models/LocalizedEntity.ts +9 -0
  37. package/src/models/strapi/StrapiGlobal.ts +2 -0
  38. package/src/test/strapiMocks/minimalGlobalData.ts +1 -0
  39. package/src/utils/getMessages.ts +18 -0
@@ -0,0 +1,142 @@
1
+ declare const getMessages: (locale: string) => {
2
+ 'unit.formatter.tonsCo2': string;
3
+ 'unit.formatter.tonsCo2PerYear': string;
4
+ 'sections.timeline.backgroundShapes': string;
5
+ 'sections.timeline.showMoreButton': string;
6
+ 'sections.textCarousel.moveRight': string;
7
+ 'sections.textCarousel.moveLeft': string;
8
+ 'sections.shopCheckout.intro.price': string;
9
+ 'sections.shopCheckout.contributionValue.label.EUR': string;
10
+ 'sections.shopCheckout.contributionValue.label.CHF': string;
11
+ 'sections.shopCheckout.contributionValue.unit.EUR': string;
12
+ 'sections.shopCheckout.contributionValue.unit.CHF': string;
13
+ 'sections.shopCheckout.contributionValue.validation.empty': string;
14
+ 'sections.shopCheckout.contributionValue.validation.tooLow.EUR': string;
15
+ 'sections.shopCheckout.contributionValue.validation.tooLow.CHF': string;
16
+ 'sections.shopCheckout.contributionValue.validation.tooHigh': string;
17
+ 'sections.shopCheckout.summary.kg': string;
18
+ 'sections.shopCheckout.summary.price': string;
19
+ 'sections.shopCheckout.summary.price.taxNotIncluded': string;
20
+ 'sections.shopCheckout.summary.price.taxIncluded': string;
21
+ 'sections.shopCheckout.submit': string;
22
+ 'sections.projectsMap.link.text': string;
23
+ 'sections.projectFacts.projectInfo.value': string;
24
+ 'projects.projectFacts.properties.area': string;
25
+ 'sections.glossary.copyButtonLabel': string;
26
+ 'sections.glossary.copySuccessMessage': string;
27
+ 'sections.glossary.copyFailureMessage': string;
28
+ 'sections.customerQuoteCard.more': string;
29
+ 'sections.customerCard.more': string;
30
+ 'sections.cta.backgroundShapesDark': string;
31
+ 'sections.cta.backgroundShapesLight': string;
32
+ 'sections.comparison.backgroundShapes': string;
33
+ 'portfolio.smallCheckout.price.taxNotIncluded': string;
34
+ 'portfolio.smallCheckout.price.taxIncluded': string;
35
+ 'portfolio.smallCheckout.contributionValueCurrency.label.CHF': string;
36
+ 'portfolio.smallCheckout.contributionValueCurrency.label.EUR': string;
37
+ 'portfolio.smallCheckout.contributionValueCurrency.unit.EUR': string;
38
+ 'portfolio.smallCheckout.contributionValueCurrency.unit.CHF': string;
39
+ 'portfolio.smallCheckout.contributionValueCurrency.validation.tooLow.CHF': string;
40
+ 'portfolio.smallCheckout.contributionValueCurrency.validation.tooLow.EUR': string;
41
+ 'portfolio.smallCheckout.contributionValueCurrency.validation.empty': string;
42
+ 'portfolio.smallCheckout.contributionValueCurrency.validation.tooHigh': string;
43
+ 'portfolio.smallCheckout.contributionValueKgs.label': string;
44
+ 'portfolio.smallCheckout.submitButton': string;
45
+ 'portfolio.smallCheckout.cta.title': string;
46
+ 'portfolio.smallCheckout.cta.subTitle': string;
47
+ 'portfolio.smallCheckout.cta.button': string;
48
+ 'features.projectInfo.projectInfo.value': string;
49
+ 'features.projectInfo.properties.area': string;
50
+ 'features.projectInfo.properties.location': string;
51
+ 'features.projectInfo.properties.start': string;
52
+ 'features.projectInfo.properties.timeSpan': string;
53
+ 'features.projectInfo.properties.projectType': string;
54
+ 'features.projectInfo.properties.projectDeveloper': string;
55
+ 'features.projectInfo.properties.verificationStandard.label': string;
56
+ 'features.projectInfo.properties.verificationStandard.value.SilvaconsultFCSISO14': string;
57
+ 'features.projectInfo.properties.verificationStandard.value.MfKWCH': string;
58
+ 'features.projectInfo.properties.forecastedAmountYear.label': string;
59
+ 'features.projectInfo.properties.riskBuffer': string;
60
+ 'features.projectInfo.properties.year': string;
61
+ 'components.portfolioProjectCard.text.yes': string;
62
+ 'components.portfolioProjectCard.text.some': string;
63
+ 'components.portfolioProjectCard.text.no': string;
64
+ 'components.portfolioProjectCard.text.notYet': string;
65
+ 'features.portfolio.documentsDownloadList.projectDocuments': string;
66
+ 'features.portfolio.documentsDownloadList.downloadDocument': string;
67
+ 'components.creditsAvailableBadge.text.yes': string;
68
+ 'components.creditsAvailableBadge.text.some': string;
69
+ 'components.creditsAvailableBadge.text.no': string;
70
+ 'components.creditsAvailableBadge.text.notYet': string;
71
+ } | {
72
+ 'unit.formatter.tonsCo2': string;
73
+ 'unit.formatter.tonsCo2PerYear': string;
74
+ 'sections.timeline.backgroundShapes': string;
75
+ 'sections.timeline.showMoreButton': string;
76
+ 'sections.textCarousel.moveRight': string;
77
+ 'sections.textCarousel.moveLeft': string;
78
+ 'sections.shopCheckout.intro.price': string;
79
+ 'sections.shopCheckout.contributionValue.label.EUR': string;
80
+ 'sections.shopCheckout.contributionValue.label.CHF': string;
81
+ 'sections.shopCheckout.contributionValue.unit.EUR': string;
82
+ 'sections.shopCheckout.contributionValue.unit.CHF': string;
83
+ 'sections.shopCheckout.contributionValue.validation.empty': string;
84
+ 'sections.shopCheckout.contributionValue.validation.tooLow.EUR': string;
85
+ 'sections.shopCheckout.contributionValue.validation.tooLow.CHF': string;
86
+ 'sections.shopCheckout.contributionValue.validation.tooHigh': string;
87
+ 'sections.shopCheckout.summary.kg': string;
88
+ 'sections.shopCheckout.summary.price': string;
89
+ 'sections.shopCheckout.summary.price.taxNotIncluded': string;
90
+ 'sections.shopCheckout.summary.price.taxIncluded': string;
91
+ 'sections.shopCheckout.submit': string;
92
+ 'sections.projectsMap.link.text': string;
93
+ 'sections.projectFacts.projectInfo.value': string;
94
+ 'projects.projectFacts.properties.area': string;
95
+ 'sections.glossary.copyButtonLabel': string;
96
+ 'sections.glossary.copySuccessMessage': string;
97
+ 'sections.glossary.copyFailureMessage': string;
98
+ 'sections.customerQuoteCard.more': string;
99
+ 'sections.customerCard.more': string;
100
+ 'sections.cta.backgroundShapes': string;
101
+ 'sections.cta.backgroundShapesLight': string;
102
+ 'sections.comparison.backgroundShapes': string;
103
+ 'portfolio.smallCheckout.price.taxNotIncluded': string;
104
+ 'portfolio.smallCheckout.price.taxIncluded': string;
105
+ 'portfolio.smallCheckout.contributionValueCurrency.label.CHF': string;
106
+ 'portfolio.smallCheckout.contributionValueCurrency.label.EUR': string;
107
+ 'portfolio.smallCheckout.contributionValueCurrency.unit.EUR': string;
108
+ 'portfolio.smallCheckout.contributionValueCurrency.unit.CHF': string;
109
+ 'portfolio.smallCheckout.contributionValueCurrency.validation.tooLow.CHF': string;
110
+ 'portfolio.smallCheckout.contributionValueCurrency.validation.tooLow.EUR': string;
111
+ 'portfolio.smallCheckout.contributionValueCurrency.validation.empty': string;
112
+ 'portfolio.smallCheckout.contributionValueCurrency.validation.tooHigh': string;
113
+ 'portfolio.smallCheckout.contributionValueKgs.label': string;
114
+ 'portfolio.smallCheckout.submitButton': string;
115
+ 'portfolio.smallCheckout.cta.title': string;
116
+ 'portfolio.smallCheckout.cta.subTitle': string;
117
+ 'portfolio.smallCheckout.cta.button': string;
118
+ 'features.projectInfo.projectInfo.value': string;
119
+ 'features.projectInfo.properties.area': string;
120
+ 'features.projectInfo.properties.location': string;
121
+ 'features.projectInfo.properties.start': string;
122
+ 'features.projectInfo.properties.timeSpan': string;
123
+ 'features.projectInfo.properties.projectType': string;
124
+ 'features.projectInfo.properties.projectDeveloper': string;
125
+ 'features.projectInfo.properties.verificationStandard.label': string;
126
+ 'features.projectInfo.properties.verificationStandard.value.SilvaconsultFCSISO14': string;
127
+ 'features.projectInfo.properties.verificationStandard.value.MfKWCH': string;
128
+ 'features.projectInfo.properties.forecastedAmountYear.label': string;
129
+ 'features.projectInfo.properties.riskBuffer': string;
130
+ 'features.projectInfo.properties.year': string;
131
+ 'components.portfolioProjectCard.text.yes': string;
132
+ 'components.portfolioProjectCard.text.some': string;
133
+ 'components.portfolioProjectCard.text.no': string;
134
+ 'components.portfolioProjectCard.text.notYet': string;
135
+ 'features.portfolio.documentsDownloadList.projectDocuments': string;
136
+ 'features.portfolio.documentsDownloadList.downloadDocument': string;
137
+ 'components.creditsAvailableBadge.text.yes': string;
138
+ 'components.creditsAvailableBadge.text.some': string;
139
+ 'components.creditsAvailableBadge.text.no': string;
140
+ 'components.creditsAvailableBadge.text.notYet': string;
141
+ };
142
+ export default getMessages;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@treely/strapi-slices",
3
- "version": "5.11.2",
3
+ "version": "5.12.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.",
@@ -1,10 +1,8 @@
1
1
  import React, { createContext } from 'react';
2
2
  import { createIntl, createIntlCache } from 'react-intl';
3
- import rootMessagesDe from '../../rootMessages.de';
4
- import rootMessagesEn from '../../rootMessages.en';
5
- import Locale from '../../models/Locale';
6
3
  import { Global } from '@emotion/react';
7
4
  import { GLOBAL_STYLE } from '../../constants/globalStyle';
5
+ import getMessages from '../../utils/getMessages';
8
6
 
9
7
  const cache = createIntlCache();
10
8
 
@@ -12,7 +10,7 @@ const intlFactory = (locale: string) =>
12
10
  createIntl(
13
11
  {
14
12
  locale,
15
- messages: { de: rootMessagesDe, en: rootMessagesEn }[locale],
13
+ messages: getMessages(locale),
16
14
  },
17
15
  cache
18
16
  );
@@ -21,7 +19,7 @@ export const IntlContext = createContext(intlFactory('en'));
21
19
 
22
20
  export interface ContextProviderProps {
23
21
  children: React.ReactNode;
24
- locale: Locale;
22
+ locale: string;
25
23
  }
26
24
 
27
25
  export const ContextProvider: React.FC<ContextProviderProps> = ({
@@ -1,14 +1,8 @@
1
1
  import React from 'react';
2
2
  import { FONT_CUSTOMIZATIONS } from '../../constants/fontCustomizations';
3
- import rootMessagesDe from '../../rootMessages.de';
4
- import rootMessagesEn from '../../rootMessages.en';
5
3
  import { BoemlyThemeProvider } from 'boemly';
6
4
  import { IntlProvider } from 'react-intl';
7
-
8
- const messages = {
9
- en: rootMessagesEn,
10
- de: rootMessagesDe,
11
- };
5
+ import getMessages from '../../utils/getMessages';
12
6
 
13
7
  interface MinimalProvidersProps {
14
8
  locale: string;
@@ -16,7 +10,7 @@ interface MinimalProvidersProps {
16
10
  }
17
11
 
18
12
  const MinimalProviders = ({ children, locale }: MinimalProvidersProps) => (
19
- <IntlProvider messages={messages[locale as 'en' | 'de']} locale={locale}>
13
+ <IntlProvider messages={getMessages(locale)} locale={locale}>
20
14
  <BoemlyThemeProvider fonts={FONT_CUSTOMIZATIONS}>
21
15
  {children}
22
16
  </BoemlyThemeProvider>
@@ -1,4 +1,4 @@
1
- import React, { useCallback, useContext, useState } from 'react';
1
+ import React, { useCallback, useContext } from 'react';
2
2
  import {
3
3
  BoemlyFormControl,
4
4
  Box,
@@ -29,6 +29,17 @@ import { CDN_URI, FPM_API_URI } from '../../../constants/api';
29
29
  import StrapiLinkButton from '../../StrapiLinkButton';
30
30
  import SmallCheckoutForm from '../../../models/forms/SmallCheckoutForm';
31
31
 
32
+ const calculateTaxIncludedValue = (
33
+ values: SmallCheckoutForm,
34
+ taxInPercent: number
35
+ ) => {
36
+ const value = parseInt(values.contributionValueCurrency);
37
+
38
+ if (isNaN(value)) return 0;
39
+
40
+ return value + value * (taxInPercent / 100);
41
+ };
42
+
32
43
  export interface SmallCheckoutProps {
33
44
  batchId: string;
34
45
  pricePerKg: number;
@@ -55,9 +66,6 @@ const SmallCheckout = ({
55
66
  }: SmallCheckoutProps) => {
56
67
  const { formatNumber, formatMessage, locale } = useContext(IntlContext);
57
68
  const { push } = useRouter();
58
- const [contributionValue, setContributionValue] = useState<number>(
59
- initialContributionValue
60
- );
61
69
 
62
70
  const validateForm = useCallback(
63
71
  (values: SmallCheckoutForm) => {
@@ -79,7 +87,7 @@ const SmallCheckout = ({
79
87
 
80
88
  return errors;
81
89
  },
82
- [locale]
90
+ [currency, locale]
83
91
  );
84
92
 
85
93
  const onSubmit = async ({ contributionValueCurrency }: SmallCheckoutForm) => {
@@ -148,6 +156,7 @@ const SmallCheckout = ({
148
156
  touched,
149
157
  handleSubmit,
150
158
  setValues,
159
+ values,
151
160
  }: FormikProps<SmallCheckoutForm>) => (
152
161
  <Form onSubmit={handleSubmit}>
153
162
  <Flex gap="4">
@@ -162,7 +171,6 @@ const SmallCheckout = ({
162
171
  value: field.value || '',
163
172
  onChange: (e) => {
164
173
  const value = e.target.valueAsNumber;
165
- setContributionValue(value);
166
174
 
167
175
  setValues({
168
176
  contributionValueCurrency: value.toString(),
@@ -231,24 +239,25 @@ const SmallCheckout = ({
231
239
  </Field>
232
240
  </Box>
233
241
  </Flex>
234
- {contributionValue > 0 && taxInPercent && taxInPercent > 0 && (
235
- <Text size="smLowNormal" mt="2">
236
- {formatMessage(
237
- { id: 'portfolio.smallCheckout.price.taxIncluded' },
238
- {
239
- number: formatNumber(
240
- contributionValue +
241
- (contributionValue * taxInPercent) / 100,
242
- {
243
- style: 'currency',
244
- currency,
245
- maximumFractionDigits: 2,
246
- }
247
- ),
248
- }
249
- )}
250
- </Text>
251
- )}
242
+ {values.contributionValueCurrency &&
243
+ taxInPercent &&
244
+ taxInPercent > 0 && (
245
+ <Text size="smLowNormal" mt="2">
246
+ {formatMessage(
247
+ { id: 'portfolio.smallCheckout.price.taxIncluded' },
248
+ {
249
+ number: formatNumber(
250
+ calculateTaxIncludedValue(values, taxInPercent),
251
+ {
252
+ style: 'currency',
253
+ currency,
254
+ maximumFractionDigits: 2,
255
+ }
256
+ ),
257
+ }
258
+ )}
259
+ </Text>
260
+ )}
252
261
 
253
262
  <Spacer height="4" />
254
263
 
@@ -2,3 +2,5 @@ export const STRAPI_URI =
2
2
  process.env.NEXT_PUBLIC_STRAPI_URI || 'http://127.0.0.1:1337';
3
3
 
4
4
  export const STRAPI_DEFAULT_PAGE_SIZE = '100';
5
+
6
+ export const STRAPI_FALLBACK_LOCALE = 'en';
package/src/index.tsx CHANGED
@@ -31,8 +31,8 @@ import StrapiPageProps from './models/strapi/StrapiPageProps';
31
31
  import StrapiPortfolio from './models/strapi/StrapiPortfolio';
32
32
  import StrapiPortfolioCard from './models/strapi/StrapiPortfolioCard';
33
33
  import StrapiProject from './models/strapi/StrapiProject';
34
- import StrapiProjectProps from './models/strapi/StrapiProjectProps';
35
34
  import StrapiProjectCard from './models/strapi/StrapiProjectCard';
35
+ import StrapiProjectProps from './models/strapi/StrapiProjectProps';
36
36
  import StrapiQuoteCard from './models/strapi/StrapiQuoteCard';
37
37
  import StrapiShapesCard from './models/strapi/StrapiShapesCard';
38
38
  import StrapiTextCardWithIcons from './models/strapi/StrapiTextCardWithIcons';
@@ -45,9 +45,12 @@ import PageMetadata from './models/PageMetadata';
45
45
  import PageProps from './models/PageProps';
46
46
  import PortfolioProject from './models/PortfolioProject';
47
47
 
48
+ import getAllSlugsFromStrapi from './integrations/strapi/getAllSlugsFromStrapi';
48
49
  import getPortfolioProjects from './integrations/strapi/getPortfolioProjects';
49
50
  import getStaticPathsFromStrapi from './integrations/strapi/getStaticPathsFromStrapi';
50
51
  import getStaticPropsFromStrapi from './integrations/strapi/getStaticPropsFromStrapi';
52
+ import getStrapiCollectionType from './integrations/strapi/getStrapiCollectionType';
53
+ import getStrapiSingleType from './integrations/strapi/getStrapiSingleType';
51
54
 
52
55
  import mergeGlobalAndStrapiBlogPostData from './utils/mergeGlobalAndStrapiBlogPostData';
53
56
  import mergeGlobalAndStrapiCustomerStoryData from './utils/mergeGlobalAndStrapiCustomerStoryData';
@@ -70,9 +73,12 @@ export {
70
73
  strapiMediaUrl,
71
74
 
72
75
  // Integrations
76
+ getAllSlugsFromStrapi,
73
77
  getPortfolioProjects,
74
78
  getStaticPathsFromStrapi,
75
79
  getStaticPropsFromStrapi,
80
+ getStrapiCollectionType,
81
+ getStrapiSingleType,
76
82
  };
77
83
 
78
84
  export type {
@@ -110,8 +116,8 @@ export type {
110
116
  StrapiPortfolio,
111
117
  StrapiPortfolioCard,
112
118
  StrapiProject,
113
- StrapiProjectProps,
114
119
  StrapiProjectCard,
120
+ StrapiProjectProps,
115
121
  StrapiQuoteCard,
116
122
  StrapiShapesCard,
117
123
  StrapiTextCardWithIcons,
@@ -0,0 +1,33 @@
1
+ import MockAxios from 'jest-mock-axios';
2
+ import getAllSlugsFromStrapi from './getAllSlugsFromStrapi';
3
+ import StrapiPage from '../../models/strapi/StrapiPage';
4
+ import { strapiPageMock } from '../../test/strapiMocks/strapiPage';
5
+
6
+ describe('The getAllSlugsFromStrapi function', () => {
7
+ afterEach(() => {
8
+ MockAxios.reset();
9
+ });
10
+
11
+ it('returns all slugs and creates a fallback for locales that dont have a translation', async () => {
12
+ const slugsPromise = getAllSlugsFromStrapi<StrapiPage>('/api/pages', [
13
+ 'en',
14
+ 'de',
15
+ 'hu',
16
+ ]);
17
+
18
+ // This page is avaliable in 'de' and 'en'
19
+ MockAxios.mockResponseFor(
20
+ { url: '/api/pages' },
21
+ { data: { data: [strapiPageMock] } }
22
+ );
23
+
24
+ const slugs = await slugsPromise;
25
+
26
+ expect(slugs).toStrictEqual([
27
+ { locale: 'en', slug: strapiPageMock.attributes.slug },
28
+ { locale: 'de', slug: strapiPageMock.attributes.slug },
29
+ // Fallback for 'hu' gets created
30
+ { locale: 'hu', slug: strapiPageMock.attributes.slug },
31
+ ]);
32
+ });
33
+ });
@@ -0,0 +1,50 @@
1
+ import strapiClient from './strapiClient';
2
+ import {
3
+ STRAPI_DEFAULT_PAGE_SIZE,
4
+ STRAPI_FALLBACK_LOCALE,
5
+ } from '../../constants/strapi';
6
+ import IStrapiResponse from '../../models/strapi/IStrapiResponse';
7
+ import IStrapiData from '../../models/strapi/IStrapiData';
8
+ import LocalizedEntity from '../../models/LocalizedEntity';
9
+
10
+ interface Options {
11
+ filters?: Record<string, any>;
12
+ }
13
+
14
+ type Slug = { slug: string; locale: string };
15
+
16
+ const getAllSlugsFromStrapi = async <T extends LocalizedEntity<'slug'>>(
17
+ path: string,
18
+ locales: string[],
19
+ { filters = {} }: Options = { filters: {} }
20
+ ): Promise<Slug[]> => {
21
+ const params: Record<string, any> = {
22
+ locale: 'all',
23
+ 'pagination[pageSize]': STRAPI_DEFAULT_PAGE_SIZE,
24
+ filters,
25
+ };
26
+
27
+ const { data } = await strapiClient.get<IStrapiResponse<IStrapiData<T>[]>>(
28
+ path,
29
+ { params }
30
+ );
31
+
32
+ const slugs: Slug[] = data.data.map((page) => ({
33
+ slug: page.attributes.slug,
34
+ locale: page.attributes.locale,
35
+ }));
36
+
37
+ const fallBackSlugs: Slug[] = locales.flatMap((locale) =>
38
+ slugs
39
+ .filter((slug) => slug.locale === STRAPI_FALLBACK_LOCALE)
40
+ .map((slug) => ({ ...slug, locale }))
41
+ );
42
+
43
+ const nonFallbackSlugs = slugs.filter(
44
+ (p) => p.locale !== STRAPI_FALLBACK_LOCALE
45
+ );
46
+
47
+ return [...fallBackSlugs, ...nonFallbackSlugs];
48
+ };
49
+
50
+ export default getAllSlugsFromStrapi;
@@ -19,6 +19,30 @@ describe('The getPortfolioProjects function', () => {
19
19
  { url: '/projects' },
20
20
  { data: { data: [strapiProjectMock] } }
21
21
  );
22
+ MockAxios.mockResponseFor({ url: '/projects' }, { data: { data: [] } });
23
+
24
+ const projects = await projectsPromise;
25
+
26
+ expect(projects.length).toBe(1);
27
+ expect(projects[0]).toStrictEqual({
28
+ ...fpmProjectMock,
29
+ slug: strapiProjectMock.attributes.slug,
30
+ creditsAvailable: strapiProjectMock.attributes.creditsAvailable,
31
+ });
32
+ });
33
+
34
+ it('returns the FPM project in english if no localized version is available', async () => {
35
+ const projectsPromise = getPortfolioProjects('de');
36
+
37
+ MockAxios.mockResponseFor(
38
+ { url: '/public/projects' },
39
+ { data: [fpmProjectMock] }
40
+ );
41
+ MockAxios.mockResponseFor({ url: '/projects' }, { data: { data: [] } });
42
+ MockAxios.mockResponseFor(
43
+ { url: '/projects' },
44
+ { data: { data: [strapiProjectMock] } }
45
+ );
22
46
 
23
47
  const projects = await projectsPromise;
24
48
 
@@ -9,6 +9,8 @@ import FPMProject from '../../models/fpm/FPMProject';
9
9
  import fpmClient from '../fpmClient';
10
10
  import strapiClient from './strapiClient';
11
11
 
12
+ const FALLBACK_LOCALE = 'en';
13
+
12
14
  const getPortfolioProjects = async (
13
15
  locale: string = 'en',
14
16
  preview: boolean = false
@@ -23,7 +25,11 @@ const getPortfolioProjects = async (
23
25
  params.publicationState = 'preview';
24
26
  }
25
27
 
26
- const [{ data: fpmProjects }, { data: strapiProjects }] = await Promise.all([
28
+ const [
29
+ { data: fpmProjects },
30
+ { data: strapiProjectsLocalized },
31
+ { data: strapiProjectsEnglish },
32
+ ] = await Promise.all([
27
33
  fpmClient.get<FPMProject[]>('/public/projects'),
28
34
  strapiClient.get<IStrapiResponse<IStrapiData<StrapiProject>[]>>(
29
35
  '/projects',
@@ -31,13 +37,27 @@ const getPortfolioProjects = async (
31
37
  params,
32
38
  }
33
39
  ),
40
+ strapiClient.get<IStrapiResponse<IStrapiData<StrapiProject>[]>>(
41
+ '/projects',
42
+ {
43
+ params: { ...params, locale: FALLBACK_LOCALE },
44
+ }
45
+ ),
34
46
  ]);
35
47
 
48
+ const strapiProjects = new Map<string, IStrapiData<StrapiProject>>();
49
+
50
+ for (const project of [
51
+ ...strapiProjectsEnglish.data,
52
+ ...strapiProjectsLocalized.data,
53
+ ]) {
54
+ if (project.attributes.fpmProjectId) {
55
+ strapiProjects.set(project.attributes.fpmProjectId, project);
56
+ }
57
+ }
58
+
36
59
  return fpmProjects.map((fpmProject: FPMProject) => {
37
- const strapiProject = strapiProjects.data.find(
38
- (sp: IStrapiData<StrapiProject>) =>
39
- sp.attributes.fpmProjectId === fpmProject.id
40
- );
60
+ const strapiProject = strapiProjects.get(fpmProject.id);
41
61
 
42
62
  const toReturn: PortfolioProject = fpmProject;
43
63
 
@@ -6,6 +6,7 @@ interface Options {
6
6
  filters?: Record<string, any>;
7
7
  }
8
8
 
9
+ /** @deprecated Migrate to getAllSlugsFromStrapi */
9
10
  const getStaticPathsFromStrapi = async (
10
11
  path: string,
11
12
  { filters = {} }: Options = { filters: {} }
@@ -9,6 +9,7 @@ interface Options {
9
9
  filters?: Record<string, any>;
10
10
  }
11
11
 
12
+ /** @deprecated Migrate to getStrapiSingleType or getStrapiCollectionType */
12
13
  const getStaticPropsFromStrapi = async (
13
14
  path: string,
14
15
  { locale = 'en', slug, preview = false, filters = {} }: Options
@@ -0,0 +1,65 @@
1
+ import MockAxios from 'jest-mock-axios';
2
+ import getStrapiCollectionType from './getStrapiCollectionType';
3
+ import StrapiPage from '../../models/strapi/StrapiPage';
4
+ import { strapiPageMock } from '../../test/strapiMocks/strapiPage';
5
+
6
+ describe('The getStrapiCollectionType function', () => {
7
+ const germanStrapiPageMock = {
8
+ ...strapiPageMock,
9
+ attributes: {
10
+ ...strapiPageMock.attributes,
11
+ locale: 'de',
12
+ title: 'Über uns',
13
+ },
14
+ };
15
+
16
+ afterEach(() => {
17
+ MockAxios.reset();
18
+ });
19
+
20
+ it('returns the localized versions if available', async () => {
21
+ const pages = getStrapiCollectionType<StrapiPage, 'slug'>(
22
+ '/api/pages',
23
+ 'slug',
24
+ { locale: 'de' }
25
+ );
26
+
27
+ MockAxios.mockResponseFor(
28
+ { url: '/api/pages' },
29
+ { data: { data: [strapiPageMock, germanStrapiPageMock] } }
30
+ );
31
+
32
+ const page = await pages;
33
+
34
+ expect(page).toStrictEqual([
35
+ expect.objectContaining({
36
+ attributes: expect.objectContaining({ title: 'Über uns' }),
37
+ }),
38
+ ]);
39
+
40
+ expect(page).toHaveLength(1);
41
+ });
42
+
43
+ it('returns the english versions if no localized version is available', async () => {
44
+ const pages = getStrapiCollectionType<StrapiPage, 'slug'>(
45
+ '/api/pages',
46
+ 'slug',
47
+ { locale: 'de' }
48
+ );
49
+
50
+ MockAxios.mockResponseFor(
51
+ { url: '/api/pages' },
52
+ { data: { data: [strapiPageMock] } }
53
+ );
54
+
55
+ const page = await pages;
56
+
57
+ expect(page).toStrictEqual([
58
+ expect.objectContaining({
59
+ attributes: expect.objectContaining({ title: 'About us' }),
60
+ }),
61
+ ]);
62
+
63
+ expect(page).toHaveLength(1);
64
+ });
65
+ });