@treely/strapi-slices 7.15.0 → 7.15.2
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/components/portfolio/ProjectInfo/messages.de.d.ts +2 -2
- package/dist/components/portfolio/ProjectInfo/messages.en.d.ts +2 -2
- package/dist/integrations/strapi/getFpmProjectById.d.ts +3 -0
- package/dist/rootMessages.de.d.ts +2 -2
- package/dist/rootMessages.en.d.ts +2 -2
- package/dist/strapi-slices.cjs.development.js +116 -73
- 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 +116 -73
- package/dist/strapi-slices.esm.js.map +1 -1
- package/dist/utils/getMessages.d.ts +4 -4
- package/package.json +1 -1
- package/src/components/portfolio/ProjectInfo/ProjectInfo.test.tsx +1 -1
- package/src/components/portfolio/ProjectInfo/ProjectInfo.tsx +17 -8
- package/src/components/portfolio/ProjectInfo/messages.de.ts +3 -4
- package/src/components/portfolio/ProjectInfo/messages.en.ts +2 -3
- package/src/integrations/strapi/getFpmProjectById.test.ts +24 -0
- package/src/integrations/strapi/getFpmProjectById.ts +12 -0
- package/src/integrations/strapi/getPortfolioProjects.test.ts +22 -43
- package/src/integrations/strapi/getPortfolioProjects.ts +18 -39
- package/src/slices/ProjectFacts/ProjectFacts.test.tsx +54 -6
- package/src/slices/ProjectFacts/ProjectFacts.tsx +48 -3
- package/src/utils/shuffleElements.test.ts +30 -12
|
@@ -62,8 +62,8 @@ declare const getMessages: (locale: string) => {
|
|
|
62
62
|
'features.projectInfo.properties.verificationStandard.label': string;
|
|
63
63
|
'features.projectInfo.properties.verificationStandard.value.SilvaconsultFCSISO14': string;
|
|
64
64
|
'features.projectInfo.properties.verificationStandard.value.MfKWCH': string;
|
|
65
|
-
'features.projectInfo.properties.
|
|
66
|
-
'features.projectInfo.properties.
|
|
65
|
+
'features.projectInfo.properties.projectVolume.label': string;
|
|
66
|
+
'features.projectInfo.properties.projectVolume.toolTip': string;
|
|
67
67
|
'features.projectInfo.properties.riskBuffer': string;
|
|
68
68
|
'features.projectInfo.properties.year': string;
|
|
69
69
|
'features.portfolio.documentsDownloadList.projectDocuments': string;
|
|
@@ -148,8 +148,8 @@ declare const getMessages: (locale: string) => {
|
|
|
148
148
|
'features.projectInfo.properties.verificationStandard.label': string;
|
|
149
149
|
'features.projectInfo.properties.verificationStandard.value.SilvaconsultFCSISO14': string;
|
|
150
150
|
'features.projectInfo.properties.verificationStandard.value.MfKWCH': string;
|
|
151
|
-
'features.projectInfo.properties.
|
|
152
|
-
'features.projectInfo.properties.
|
|
151
|
+
'features.projectInfo.properties.projectVolume.toolTip': string;
|
|
152
|
+
'features.projectInfo.properties.projectVolume.label': string;
|
|
153
153
|
'features.projectInfo.properties.riskBuffer': string;
|
|
154
154
|
'features.projectInfo.properties.year': string;
|
|
155
155
|
'features.portfolio.documentsDownloadList.projectDocuments': string;
|
package/package.json
CHANGED
|
@@ -87,7 +87,7 @@ describe('The ProjectInfo component', () => {
|
|
|
87
87
|
|
|
88
88
|
const tooltip = await screen.findByRole('tooltip');
|
|
89
89
|
expect(tooltip).toHaveTextContent(
|
|
90
|
-
messagesEn['features.projectInfo.properties.
|
|
90
|
+
messagesEn['features.projectInfo.properties.projectVolume.toolTip']
|
|
91
91
|
);
|
|
92
92
|
|
|
93
93
|
await userEvent.unhover(trigger);
|
|
@@ -43,7 +43,6 @@ export const ProjectInfo: React.FC<ProjectInfoProps> = ({
|
|
|
43
43
|
subtitles,
|
|
44
44
|
}: ProjectInfoProps) => {
|
|
45
45
|
const { formatMessage, formatNumber, formatDate } = useContext(IntlContext);
|
|
46
|
-
|
|
47
46
|
return (
|
|
48
47
|
<Container p="2" width="full">
|
|
49
48
|
<Heading size="xl" textAlign="left">
|
|
@@ -196,7 +195,9 @@ export const ProjectInfo: React.FC<ProjectInfoProps> = ({
|
|
|
196
195
|
<></>
|
|
197
196
|
)}
|
|
198
197
|
|
|
199
|
-
{project.averageSellableAmountPerYear
|
|
198
|
+
{(project.averageSellableAmountPerYear &&
|
|
199
|
+
project.averageSellableAmountPerYear > 0) ||
|
|
200
|
+
project.riskBuffer ? (
|
|
200
201
|
<>
|
|
201
202
|
<Spacer height="8" />
|
|
202
203
|
<Divider />
|
|
@@ -206,16 +207,16 @@ export const ProjectInfo: React.FC<ProjectInfoProps> = ({
|
|
|
206
207
|
<></>
|
|
207
208
|
)}
|
|
208
209
|
<SimpleGrid columns={[1, null, null, 2]} spacingX="10" spacingY="8">
|
|
209
|
-
{project.averageSellableAmountPerYear
|
|
210
|
+
{project.averageSellableAmountPerYear > 0 ? (
|
|
210
211
|
<Tooltip
|
|
211
212
|
label={formatMessage({
|
|
212
|
-
id: 'features.projectInfo.properties.
|
|
213
|
+
id: 'features.projectInfo.properties.projectVolume.toolTip',
|
|
213
214
|
})}
|
|
214
215
|
>
|
|
215
216
|
<Box>
|
|
216
217
|
<LabelTextPair
|
|
217
218
|
label={formatMessage({
|
|
218
|
-
id: 'features.projectInfo.properties.
|
|
219
|
+
id: 'features.projectInfo.properties.projectVolume.label',
|
|
219
220
|
})}
|
|
220
221
|
text={formatMessage(
|
|
221
222
|
{
|
|
@@ -234,6 +235,10 @@ export const ProjectInfo: React.FC<ProjectInfoProps> = ({
|
|
|
234
235
|
/>
|
|
235
236
|
</Box>
|
|
236
237
|
</Tooltip>
|
|
238
|
+
) : (
|
|
239
|
+
<Box>
|
|
240
|
+
<CreditsAvailableBadge status={project.creditAvailability} />
|
|
241
|
+
</Box>
|
|
237
242
|
)}
|
|
238
243
|
|
|
239
244
|
{project.riskBuffer && (
|
|
@@ -252,9 +257,13 @@ export const ProjectInfo: React.FC<ProjectInfoProps> = ({
|
|
|
252
257
|
)}
|
|
253
258
|
</SimpleGrid>
|
|
254
259
|
|
|
255
|
-
|
|
256
|
-
<
|
|
257
|
-
|
|
260
|
+
{project.averageSellableAmountPerYear > 0 ? (
|
|
261
|
+
<Box mt="2">
|
|
262
|
+
<CreditsAvailableBadge status={project.creditAvailability} />
|
|
263
|
+
</Box>
|
|
264
|
+
) : (
|
|
265
|
+
<></>
|
|
266
|
+
)}
|
|
258
267
|
</Container>
|
|
259
268
|
);
|
|
260
269
|
};
|
|
@@ -12,10 +12,9 @@ const messagesDe = {
|
|
|
12
12
|
'SILVACONSULT® Forest Carbon Standard, ISO 14064-2',
|
|
13
13
|
'features.projectInfo.properties.verificationStandard.value.MfKWCH':
|
|
14
14
|
'Methodik für Klimaschutzprojekte im Wald für die Schweiz',
|
|
15
|
-
'features.projectInfo.properties.
|
|
16
|
-
'Dies ist
|
|
17
|
-
'features.projectInfo.properties.
|
|
18
|
-
'Projektvolumen',
|
|
15
|
+
'features.projectInfo.properties.projectVolume.toolTip':
|
|
16
|
+
'Dies ist die durchschnittliche Anzahl der Credits, die pro Jahr ausgestellt werden. Dieser Wert entspricht nicht den Verfügbarkeiten.',
|
|
17
|
+
'features.projectInfo.properties.projectVolume.label': 'Projektvolumen',
|
|
19
18
|
'features.projectInfo.properties.riskBuffer': 'Anteil Risikopuffer',
|
|
20
19
|
|
|
21
20
|
'features.projectInfo.properties.year':
|
|
@@ -12,9 +12,8 @@ const messagesEn = {
|
|
|
12
12
|
'SILVACONSULT® Forest Carbon Standard, ISO 14064-2',
|
|
13
13
|
'features.projectInfo.properties.verificationStandard.value.MfKWCH':
|
|
14
14
|
'Methodik für Klimaschutzprojekte im Wald für die Schweiz',
|
|
15
|
-
'features.projectInfo.properties.
|
|
16
|
-
|
|
17
|
-
'features.projectInfo.properties.forecastedAmountYear.toolTip':
|
|
15
|
+
'features.projectInfo.properties.projectVolume.label': 'Project Volume',
|
|
16
|
+
'features.projectInfo.properties.projectVolume.toolTip':
|
|
18
17
|
"This is the average amount of credits that are issued per year. This value doesn't represent availabilities.",
|
|
19
18
|
'features.projectInfo.properties.riskBuffer': 'Risk Buffer Share',
|
|
20
19
|
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import MockAxios from 'jest-mock-axios';
|
|
2
|
+
import fpmProjectMock from '../../test/integrationMocks/fpmProjectMock';
|
|
3
|
+
import getFpmProjectById from './getFpmProjectById';
|
|
4
|
+
|
|
5
|
+
describe('The getFpmProjectById function', () => {
|
|
6
|
+
afterEach(() => {
|
|
7
|
+
MockAxios.reset();
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
it('fetches FPM project by ID successfully', async () => {
|
|
11
|
+
const projectId = 'test-project-1';
|
|
12
|
+
const projectPromise = getFpmProjectById(projectId);
|
|
13
|
+
|
|
14
|
+
MockAxios.mockResponseFor(
|
|
15
|
+
{ url: `/public/projects/${projectId}` },
|
|
16
|
+
{ data: fpmProjectMock }
|
|
17
|
+
);
|
|
18
|
+
|
|
19
|
+
const result = await projectPromise;
|
|
20
|
+
|
|
21
|
+
expect(result).toEqual(fpmProjectMock);
|
|
22
|
+
expect(MockAxios.get).toHaveBeenCalledWith(`/public/projects/${projectId}`);
|
|
23
|
+
});
|
|
24
|
+
});
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import FPMProject from '../../models/fpm/FPMProject';
|
|
2
|
+
import fpmClient from '../fpmClient';
|
|
3
|
+
|
|
4
|
+
const getFpmProjectById = async (projectId: string): Promise<FPMProject> => {
|
|
5
|
+
const fpmResponse = await fpmClient.get<FPMProject>(
|
|
6
|
+
`/public/projects/${projectId}`
|
|
7
|
+
);
|
|
8
|
+
|
|
9
|
+
return fpmResponse.data;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export default getFpmProjectById;
|
|
@@ -2,76 +2,55 @@ import MockAxios from 'jest-mock-axios';
|
|
|
2
2
|
import fpmProjectMock from '../../test/integrationMocks/fpmProjectMock';
|
|
3
3
|
import { strapiProjectMock } from '../../test/strapiMocks/strapiProject';
|
|
4
4
|
import getPortfolioProjects from './getPortfolioProjects';
|
|
5
|
-
import fpmClient from '../fpmClient';
|
|
6
|
-
import strapiClient from './strapiClient';
|
|
7
|
-
|
|
8
|
-
// Mock the fpmClient module
|
|
9
|
-
jest.mock('../fpmClient', () => ({
|
|
10
|
-
__esModule: true,
|
|
11
|
-
default: {
|
|
12
|
-
get: jest.fn(),
|
|
13
|
-
},
|
|
14
|
-
}));
|
|
15
|
-
|
|
16
|
-
// Mock the strapiClient module
|
|
17
|
-
jest.mock('./strapiClient', () => ({
|
|
18
|
-
__esModule: true,
|
|
19
|
-
default: {
|
|
20
|
-
get: jest.fn(),
|
|
21
|
-
},
|
|
22
|
-
}));
|
|
23
5
|
|
|
24
6
|
describe('The getPortfolioProjects function', () => {
|
|
25
7
|
afterEach(() => {
|
|
26
8
|
MockAxios.reset();
|
|
27
|
-
jest.clearAllMocks();
|
|
28
9
|
});
|
|
29
10
|
|
|
30
11
|
it('returns the FPM projects with the slug from strapi', async () => {
|
|
31
|
-
|
|
32
|
-
(fpmClient.get as jest.Mock)
|
|
33
|
-
.mockResolvedValueOnce({ data: [fpmProjectMock] }) // /public/projects
|
|
34
|
-
.mockResolvedValueOnce({
|
|
35
|
-
data: { ...fpmProjectMock, averageSellableAmountPerYear: 1000 },
|
|
36
|
-
}); // /public/projects/${id}
|
|
12
|
+
const projectsPromise = getPortfolioProjects();
|
|
37
13
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
14
|
+
MockAxios.mockResponseFor(
|
|
15
|
+
{ url: '/public/projects' },
|
|
16
|
+
{ data: [fpmProjectMock] }
|
|
17
|
+
);
|
|
18
|
+
MockAxios.mockResponseFor(
|
|
19
|
+
{ url: '/projects' },
|
|
20
|
+
{ data: { data: [strapiProjectMock] } }
|
|
21
|
+
);
|
|
22
|
+
MockAxios.mockResponseFor({ url: '/projects' }, { data: { data: [] } });
|
|
42
23
|
|
|
43
|
-
const projects = await
|
|
24
|
+
const projects = await projectsPromise;
|
|
44
25
|
|
|
45
26
|
expect(projects.length).toBe(1);
|
|
46
27
|
expect(projects[0]).toStrictEqual({
|
|
47
28
|
...fpmProjectMock,
|
|
48
29
|
slug: strapiProjectMock.attributes.slug,
|
|
49
30
|
creditAvailability: fpmProjectMock.creditAvailability,
|
|
50
|
-
averageSellableAmountPerYear: 1000,
|
|
51
31
|
});
|
|
52
32
|
});
|
|
53
33
|
|
|
54
34
|
it('returns the FPM project in english if no localized version is available', async () => {
|
|
55
|
-
|
|
56
|
-
(fpmClient.get as jest.Mock)
|
|
57
|
-
.mockResolvedValueOnce({ data: [fpmProjectMock] }) // /public/projects
|
|
58
|
-
.mockResolvedValueOnce({
|
|
59
|
-
data: { ...fpmProjectMock, averageSellableAmountPerYear: 1000 },
|
|
60
|
-
}); // /public/projects/${id}
|
|
35
|
+
const projectsPromise = getPortfolioProjects('de');
|
|
61
36
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
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
|
+
);
|
|
66
46
|
|
|
67
|
-
const projects = await
|
|
47
|
+
const projects = await projectsPromise;
|
|
68
48
|
|
|
69
49
|
expect(projects.length).toBe(1);
|
|
70
50
|
expect(projects[0]).toStrictEqual({
|
|
71
51
|
...fpmProjectMock,
|
|
72
52
|
slug: strapiProjectMock.attributes.slug,
|
|
73
53
|
creditAvailability: fpmProjectMock.creditAvailability,
|
|
74
|
-
averageSellableAmountPerYear: 1000,
|
|
75
54
|
});
|
|
76
55
|
});
|
|
77
56
|
});
|
|
@@ -15,45 +15,24 @@ const getPortfolioProjects = async (
|
|
|
15
15
|
getStrapiProjects(locale, STRAPI_DEFAULT_POPULATE_DEPTH, preview),
|
|
16
16
|
]);
|
|
17
17
|
|
|
18
|
-
return
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
const strapiProject = strapiProjects.get(fpmProject.id);
|
|
40
|
-
|
|
41
|
-
const toReturn: PortfolioProject = fpmProject;
|
|
42
|
-
|
|
43
|
-
if (strapiProject?.attributes.slug) {
|
|
44
|
-
toReturn.slug = strapiProject.attributes.slug;
|
|
45
|
-
}
|
|
46
|
-
if (strapiProject?.attributes.thumbnail) {
|
|
47
|
-
toReturn.thumbnail = strapiProject?.attributes.thumbnail;
|
|
48
|
-
}
|
|
49
|
-
if (strapiProject?.attributes.portfolio.data?.attributes.host) {
|
|
50
|
-
toReturn.portfolioHost =
|
|
51
|
-
strapiProject.attributes.portfolio.data.attributes.host;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
return toReturn;
|
|
55
|
-
})
|
|
56
|
-
);
|
|
18
|
+
return fpmProjects.map((fpmProject: FPMProject) => {
|
|
19
|
+
const strapiProject = strapiProjects.get(fpmProject.id);
|
|
20
|
+
|
|
21
|
+
const toReturn: PortfolioProject = fpmProject;
|
|
22
|
+
|
|
23
|
+
if (strapiProject?.attributes.slug) {
|
|
24
|
+
toReturn.slug = strapiProject.attributes.slug;
|
|
25
|
+
}
|
|
26
|
+
if (strapiProject?.attributes.thumbnail) {
|
|
27
|
+
toReturn.thumbnail = strapiProject?.attributes.thumbnail;
|
|
28
|
+
}
|
|
29
|
+
if (strapiProject?.attributes.portfolio.data?.attributes.host) {
|
|
30
|
+
toReturn.portfolioHost =
|
|
31
|
+
strapiProject.attributes.portfolio.data.attributes.host;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return toReturn;
|
|
35
|
+
});
|
|
57
36
|
};
|
|
58
37
|
|
|
59
38
|
export default getPortfolioProjects;
|
|
@@ -1,21 +1,33 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { render, screen } from '../../test/testUtils';
|
|
2
|
+
import { render, screen, waitFor } from '../../test/testUtils';
|
|
3
3
|
import ProjectFacts from '.';
|
|
4
4
|
import { ProjectFactsProps } from './ProjectFacts';
|
|
5
5
|
import portfolioProjectMock from '../../test/mocks/portfolioProjectMock';
|
|
6
|
+
import getFpmProjectById from '../../integrations/strapi/getFpmProjectById';
|
|
7
|
+
|
|
8
|
+
// Mock the getFpmProjectById function
|
|
9
|
+
jest.mock('../../integrations/strapi/getFpmProjectById');
|
|
10
|
+
const mockGetFpmProjectById = getFpmProjectById as jest.MockedFunction<
|
|
11
|
+
typeof getFpmProjectById
|
|
12
|
+
>;
|
|
6
13
|
|
|
7
14
|
const defaultProps: ProjectFactsProps = {
|
|
8
15
|
slice: {
|
|
9
16
|
projectId: 'project-id-1',
|
|
10
17
|
},
|
|
11
18
|
};
|
|
19
|
+
|
|
12
20
|
const setup = (props: Partial<ProjectFactsProps> = {}) => {
|
|
13
21
|
const combinedProps = { ...defaultProps, ...props };
|
|
14
22
|
render(<ProjectFacts {...combinedProps} />);
|
|
15
23
|
};
|
|
16
24
|
|
|
17
25
|
describe('The ProjectFacts slice', () => {
|
|
18
|
-
|
|
26
|
+
beforeEach(() => {
|
|
27
|
+
jest.clearAllMocks();
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it('displays error message if there is no project', () => {
|
|
19
31
|
setup();
|
|
20
32
|
|
|
21
33
|
expect(
|
|
@@ -25,11 +37,47 @@ describe('The ProjectFacts slice', () => {
|
|
|
25
37
|
).toBeInTheDocument();
|
|
26
38
|
});
|
|
27
39
|
|
|
28
|
-
it('displays
|
|
40
|
+
it('displays loading state when fetching project data', () => {
|
|
41
|
+
// Mock a delayed response to test loading state
|
|
42
|
+
mockGetFpmProjectById.mockImplementation(
|
|
43
|
+
() =>
|
|
44
|
+
new Promise((resolve) =>
|
|
45
|
+
setTimeout(() => resolve(portfolioProjectMock), 100)
|
|
46
|
+
)
|
|
47
|
+
);
|
|
48
|
+
|
|
29
49
|
setup({ project: portfolioProjectMock });
|
|
30
50
|
|
|
31
|
-
expect(
|
|
32
|
-
|
|
33
|
-
|
|
51
|
+
expect(screen.getByText('Loading project data...')).toBeInTheDocument();
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('displays the enhanced project info with averageSellableAmountPerYear from FPM data', async () => {
|
|
55
|
+
// Create enhanced FPM data with different averageSellableAmountPerYear
|
|
56
|
+
const enhancedFpmData = {
|
|
57
|
+
...portfolioProjectMock,
|
|
58
|
+
averageSellableAmountPerYear: 1200000, // Value from FPM findOne project
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
mockGetFpmProjectById.mockResolvedValue(enhancedFpmData);
|
|
62
|
+
|
|
63
|
+
setup({ project: portfolioProjectMock });
|
|
64
|
+
|
|
65
|
+
await waitFor(() => {
|
|
66
|
+
expect(mockGetFpmProjectById).toHaveBeenCalledWith(
|
|
67
|
+
portfolioProjectMock.id
|
|
68
|
+
);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
await waitFor(() => {
|
|
72
|
+
expect(mockGetFpmProjectById).toHaveBeenCalledTimes(1);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
await waitFor(() => {
|
|
76
|
+
expect(
|
|
77
|
+
screen.queryByText('Loading project data...')
|
|
78
|
+
).not.toBeInTheDocument();
|
|
79
|
+
|
|
80
|
+
expect(screen.getByText('1,200 tCO₂/year')).toBeInTheDocument();
|
|
81
|
+
});
|
|
34
82
|
});
|
|
35
83
|
});
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React from 'react';
|
|
1
|
+
import React, { useEffect, useState } from 'react';
|
|
2
2
|
import { DefaultSectionContainer, Flex, Wrapper } from 'boemly';
|
|
3
3
|
import StrapiLink from '../../models/strapi/StrapiLink';
|
|
4
4
|
import PortfolioProject from '../../models/PortfolioProject';
|
|
@@ -7,6 +7,7 @@ import ProjectInfo from '../../components/portfolio/ProjectInfo';
|
|
|
7
7
|
import DocumentsDownloadList from '../../components/portfolio/DocumentsDownloadList';
|
|
8
8
|
import Contact from '../../components/portfolio/Contact';
|
|
9
9
|
import StrapiImage from '../../models/strapi/StrapiImage';
|
|
10
|
+
import getFpmProjectById from '../../integrations/strapi/getFpmProjectById';
|
|
10
11
|
|
|
11
12
|
export interface ProjectFactsProps {
|
|
12
13
|
project?: PortfolioProject;
|
|
@@ -47,17 +48,61 @@ export const ProjectFacts: React.FC<ProjectFactsProps> = ({
|
|
|
47
48
|
slice,
|
|
48
49
|
project,
|
|
49
50
|
}: ProjectFactsProps) => {
|
|
50
|
-
|
|
51
|
+
const [enhancedProject, setEnhancedProject] = useState<
|
|
52
|
+
PortfolioProject | undefined
|
|
53
|
+
>(project);
|
|
54
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
55
|
+
|
|
56
|
+
useEffect(() => {
|
|
57
|
+
const fetchCompleteProjectData = async () => {
|
|
58
|
+
if (project && project.id) {
|
|
59
|
+
setIsLoading(true);
|
|
60
|
+
try {
|
|
61
|
+
const completeFpmProject = await getFpmProjectById(project.id);
|
|
62
|
+
|
|
63
|
+
// Merge the complete FPM data with existing project data (preserving Strapi fields like slug, portfolioHost, thumbnail)
|
|
64
|
+
const mergedProject: PortfolioProject = {
|
|
65
|
+
...completeFpmProject,
|
|
66
|
+
slug: project.slug,
|
|
67
|
+
portfolioHost: project.portfolioHost,
|
|
68
|
+
thumbnail: project.thumbnail,
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
setEnhancedProject(mergedProject);
|
|
72
|
+
} catch (error) {
|
|
73
|
+
console.error('Error fetching complete project data:', error);
|
|
74
|
+
// Fallback to original project data if fetch fails
|
|
75
|
+
setEnhancedProject(project);
|
|
76
|
+
} finally {
|
|
77
|
+
setIsLoading(false);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
fetchCompleteProjectData();
|
|
83
|
+
}, [project]);
|
|
84
|
+
|
|
85
|
+
if (!enhancedProject) {
|
|
51
86
|
return (
|
|
52
87
|
<>Invalid configuration, check if a project this id exists in the FPM</>
|
|
53
88
|
);
|
|
54
89
|
}
|
|
55
90
|
|
|
91
|
+
if (isLoading) {
|
|
92
|
+
return (
|
|
93
|
+
<DefaultSectionContainer>
|
|
94
|
+
<Wrapper>
|
|
95
|
+
<div>Loading project data...</div>
|
|
96
|
+
</Wrapper>
|
|
97
|
+
</DefaultSectionContainer>
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
|
|
56
101
|
return (
|
|
57
102
|
<DefaultSectionContainer>
|
|
58
103
|
<Wrapper>
|
|
59
104
|
<Flex flexDir={['column', null, null, 'row']} gap="4" width="full">
|
|
60
|
-
<ProjectInfo project={
|
|
105
|
+
<ProjectInfo project={enhancedProject} subtitles={slice} />
|
|
61
106
|
<Flex flexDir="column" gap="4" width="full">
|
|
62
107
|
{slice.documentUrls && slice.documentUrls.length > 0 && (
|
|
63
108
|
<DocumentsDownloadList documentUrls={slice.documentUrls} />
|
|
@@ -1,9 +1,20 @@
|
|
|
1
1
|
import shuffleElements from './shuffleElements';
|
|
2
2
|
|
|
3
|
-
const originalSlides = [
|
|
3
|
+
const originalSlides = [
|
|
4
|
+
{ id: 1 },
|
|
5
|
+
{ id: 2 },
|
|
6
|
+
{ id: 3 },
|
|
7
|
+
{ id: 4 },
|
|
8
|
+
{ id: 5 },
|
|
9
|
+
{ id: 6 },
|
|
10
|
+
{ id: 7 },
|
|
11
|
+
{ id: 8 },
|
|
12
|
+
{ id: 9 },
|
|
13
|
+
{ id: 10 },
|
|
14
|
+
];
|
|
4
15
|
|
|
5
16
|
describe('shuffleElement', () => {
|
|
6
|
-
it('returns a new array with the same elements
|
|
17
|
+
it('returns a new array with the same elements without mutating the original array', () => {
|
|
7
18
|
const shuffled = shuffleElements(originalSlides);
|
|
8
19
|
|
|
9
20
|
// Check that the shuffled array is not the same reference as the original
|
|
@@ -13,18 +24,25 @@ describe('shuffleElement', () => {
|
|
|
13
24
|
expect(shuffled).toEqual(expect.arrayContaining(originalSlides));
|
|
14
25
|
expect(originalSlides).toEqual(expect.arrayContaining(shuffled));
|
|
15
26
|
|
|
16
|
-
//
|
|
17
|
-
|
|
18
|
-
(slide, index) => slide.id !== shuffled[index].id
|
|
19
|
-
);
|
|
20
|
-
expect(isShuffled).toBe(true);
|
|
27
|
+
// Check that the arrays have the same length
|
|
28
|
+
expect(shuffled).toHaveLength(originalSlides.length);
|
|
21
29
|
});
|
|
22
30
|
|
|
23
|
-
it('
|
|
24
|
-
|
|
25
|
-
|
|
31
|
+
it('produces a different order when shuffled multiple times', () => {
|
|
32
|
+
// Test multiple shuffles to ensure at least one produces a different order
|
|
33
|
+
const shuffles = [
|
|
34
|
+
shuffleElements(originalSlides),
|
|
35
|
+
shuffleElements(originalSlides),
|
|
36
|
+
shuffleElements(originalSlides),
|
|
37
|
+
];
|
|
26
38
|
|
|
27
|
-
//
|
|
28
|
-
|
|
39
|
+
// Check that at least one shuffle produces a different order
|
|
40
|
+
const hasDifferentOrder = shuffles.some((shuffled) => {
|
|
41
|
+
return shuffled.some(
|
|
42
|
+
(element, index) => element.id !== originalSlides[index].id
|
|
43
|
+
);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
expect(hasDifferentOrder).toBe(true);
|
|
29
47
|
});
|
|
30
48
|
});
|