Skip to content

Commit 4af2ef9

Browse files
authored
feat: storyblok resource carousel + refactoring course page carousels (chaynHQ#1402)
* feat: storyblok resource carousel + refactoring course page carousels * fix: linting issues * fix: build issue * fix: tests for filterStoryByLanguageAndPartnerAccess
1 parent fc1a35f commit 4af2ef9

16 files changed

+383
-127
lines changed

.github/configs/labeler.yml

Lines changed: 36 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -5,55 +5,55 @@
55
needs-review:
66
- changed-files:
77
- any-glob-to-any-file:
8-
- "lib/firebase.ts"
9-
- "lib/auth.ts"
10-
- "lib/api.ts"
11-
- "middleware.ts"
12-
- "components/guards/AuthGuard.tsx"
13-
- "components/guards/**"
14-
- "next.config.js"
15-
- ".env*"
16-
- "**/.env*"
17-
- "lib/constants/common.ts"
8+
- 'lib/firebase.ts'
9+
- 'lib/auth.ts'
10+
- 'lib/api.ts'
11+
- 'middleware.ts'
12+
- 'components/guards/AuthGuard.tsx'
13+
- 'components/guards/**'
14+
- 'next.config.js'
15+
- '.env*'
16+
- '**/.env*'
17+
- 'lib/constants/common.ts'
1818

1919
config-changes:
2020
- changed-files:
2121
- any-glob-to-any-file:
22-
- "tsconfig.json"
23-
- "package.json"
24-
- "**/*.lock"
25-
- "cypress-after.config.ts"
26-
- "cypress.config.ts"
27-
- "jest.config.js"
28-
- "newrelic.js"
29-
- "lib/rollbar.ts"
30-
- "**/*.json"
31-
- "**/*.yaml"
32-
- "**/*.yml"
22+
- 'tsconfig.json'
23+
- 'package.json'
24+
- '**/*.lock'
25+
- 'cypress-after.config.ts'
26+
- 'cypress.config.ts'
27+
- 'jest.config.js'
28+
- 'newrelic.js'
29+
- 'lib/rollbar.ts'
30+
- '**/*.json'
31+
- '**/*.yaml'
32+
- '**/*.yml'
3333

3434
automation-changes:
3535
- changed-files:
3636
- any-glob-to-any-file:
37-
- "scripts/**"
38-
- "bin/**"
39-
- "*.sh"
40-
- "*.py"
41-
- ".husky/**"
42-
- ".github/**"
37+
- 'scripts/**'
38+
- 'bin/**'
39+
- '*.sh'
40+
- '*.py'
41+
- '.husky/**'
42+
- '.github/**'
4343

4444
dev-tooling-changes:
4545
- changed-files:
4646
- any-glob-to-any-file:
47-
- ".vscode/**"
48-
- ".gitignore"
49-
- ".prettierrc"
50-
- ".nycrc"
51-
- "next-env.d.ts"
47+
- '.vscode/**'
48+
- '.gitignore'
49+
- '.prettierrc'
50+
- '.nycrc'
51+
- 'next-env.d.ts'
5252

5353
test-config-changes:
5454
- changed-files:
5555
- any-glob-to-any-file:
56-
- "cypress/**"
57-
- "cypress-after.config.ts"
58-
- "cypress.config.ts"
59-
- "jest.config.js"
56+
- 'cypress/**'
57+
- 'cypress-after.config.ts'
58+
- 'cypress.config.ts'
59+
- 'jest.config.js'

.github/dependabot.yml

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,44 +4,44 @@
44

55
version: 2
66
updates:
7-
- package-ecosystem: "npm" # same value for yarn
8-
directory: "/" # Location of package manifests
7+
- package-ecosystem: 'npm' # same value for yarn
8+
directory: '/' # Location of package manifests
99
schedule:
10-
interval: "weekly"
11-
time: "09:00"
12-
timezone: "Europe/London"
10+
interval: 'weekly'
11+
time: '09:00'
12+
timezone: 'Europe/London'
1313
assignees:
1414
- kyleecodes
1515
reviewers:
1616
- kyleecodes
1717
groups:
1818
dev-deps:
19-
dependency-type: "development"
19+
dependency-type: 'development'
2020
update-types: # Excludes major updates so we can review individually
21-
- "minor"
22-
- "patch"
21+
- 'minor'
22+
- 'patch'
2323
prod-deps:
24-
dependency-type: "production"
24+
dependency-type: 'production'
2525
update-types: # Excludes major updates so we can review individually
26-
- "minor"
27-
- "patch"
26+
- 'minor'
27+
- 'patch'
2828

2929
# Maintain dependencies for GitHub Actions
30-
- package-ecosystem: "github-actions"
30+
- package-ecosystem: 'github-actions'
3131
# Workflow files stored in the default location of `.github/workflows`.
3232
# (You don't need to specify `/.github/workflows` for `directory`.
3333
# You can use `directory: "/"`.)
34-
directory: "/"
34+
directory: '/'
3535
schedule:
36-
interval: "weekly"
37-
time: "09:00"
38-
timezone: "Europe/London"
36+
interval: 'weekly'
37+
time: '09:00'
38+
timezone: 'Europe/London'
3939
assignees:
4040
- kyleecodes
4141
reviewers:
4242
- kyleecodes
4343
groups:
4444
actions-deps:
4545
patterns:
46-
- "*" # Group all GitHub Actions updates together
46+
- '*' # Group all GitHub Actions updates together
4747

app/[locale]/courses/[slug]/[sessionSlug]/page.tsx

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -77,10 +77,6 @@ export default async function Page({ params }: { params: Params }) {
7777
const content = story.content as StoryblokSessionPageProps;
7878

7979
return (
80-
<StoryblokSessionPage
81-
{...content}
82-
storyUuid={story.uuid}
83-
storyPosition={story.position}
84-
/>
80+
<StoryblokSessionPage {...content} storyUuid={story.uuid} storyPosition={story.position} />
8581
);
8682
}
Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
import { RESOURCE_CATEGORIES } from '@/lib/constants/enums';
2+
import { useTypedSelector } from '@/lib/hooks/store';
3+
import { getStoryblokStories } from '@/lib/storyblok';
4+
import filterResourcesForLocaleAndPartnerAccess from '@/lib/utils/filterStoryByLanguageAndPartnerAccess';
5+
import { getDefaultFullSlug } from '@/lib/utils/getDefaultFullSlug';
6+
import userHasAccessToPartnerContent from '@/lib/utils/userHasAccessToPartnerContent';
7+
import { Box } from '@mui/material';
8+
import { ISbStoriesParams, ISbStoryData } from '@storyblok/react/rsc';
9+
import Cookies from 'js-cookie';
10+
import { useLocale } from 'next-intl';
11+
import { useEffect, useState } from 'react';
12+
import { RelatedContentCard } from '../cards/RelatedContentCard';
13+
import { ShortsCard } from '../cards/ShortsCard';
14+
import Carousel, { getSlideWidth } from '../common/Carousel';
15+
16+
export interface ResourceCarouselProps {
17+
resourceTypes?: string[];
18+
title?: string;
19+
// Either you can pass the data down if you already have it or you can pull from the storyblok API
20+
resources?: ISbStoryData[];
21+
}
22+
23+
const ResourceCarousel = ({
24+
resourceTypes,
25+
title = 'resource-category-carousel',
26+
resources = [],
27+
}: ResourceCarouselProps) => {
28+
const userId = useTypedSelector((state) => state.user.id);
29+
const entryPartnerReferral = useTypedSelector((state) => state.user.entryPartnerReferral);
30+
const partnerAccesses = useTypedSelector((state) => state.partnerAccesses);
31+
const partnerAdmin = useTypedSelector((state) => state.partnerAdmin);
32+
// TODO filter by partner based on user access!
33+
const locale = useLocale(); // Get the current locale
34+
const baseProps: Partial<ISbStoriesParams> = {
35+
language: locale,
36+
version: 'published',
37+
sort_by: 'position:description',
38+
};
39+
40+
const [carouselStories, setCarouselStories] = useState<ISbStoryData[]>([]);
41+
useEffect(() => {
42+
if (resources.length < 1 && resourceTypes) {
43+
const referralPartner = Cookies.get('referralPartner') || entryPartnerReferral;
44+
const userPartners = userHasAccessToPartnerContent(
45+
partnerAdmin?.partner,
46+
partnerAccesses,
47+
referralPartner,
48+
userId,
49+
);
50+
51+
if (resourceTypes.includes('shorts')) {
52+
getStoryblokStories(locale, {
53+
...baseProps,
54+
starts_with: 'shorts/',
55+
})
56+
.then((shorts) => {
57+
setCarouselStories([
58+
...carouselStories,
59+
...((shorts &&
60+
filterResourcesForLocaleAndPartnerAccess(shorts, locale, userPartners)) ||
61+
[]),
62+
]);
63+
})
64+
.catch((error) => {
65+
console.error('Failed to fetch carousel shorts' + error);
66+
});
67+
}
68+
69+
// This is a placeholder for the courses carousel implementation
70+
if (resourceTypes.includes('conversations')) {
71+
// add try catches
72+
getStoryblokStories(locale, {
73+
...baseProps,
74+
starts_with: 'conversations/',
75+
})
76+
.then((conversations) => {
77+
setCarouselStories([
78+
...carouselStories,
79+
...((conversations &&
80+
filterResourcesForLocaleAndPartnerAccess(conversations, locale, userPartners)) ||
81+
[]),
82+
]);
83+
})
84+
.catch((error) => {
85+
console.error('Failed to fetch carousel conversation' + error);
86+
});
87+
}
88+
// This is a placeholder for the courses carousel implementation
89+
if (resourceTypes.includes('courses')) {
90+
getStoryblokStories(locale, {
91+
...baseProps,
92+
starts_with: 'courses/',
93+
})
94+
.then((courses) => {
95+
setCarouselStories([...carouselStories, ...(courses || [])]);
96+
})
97+
.catch((error) => {
98+
console.error('Failed to fetch carousel courses' + error);
99+
});
100+
}
101+
} else {
102+
setCarouselStories(resources);
103+
}
104+
}, []);
105+
106+
if (!resourceTypes && resources.length < 1) {
107+
console.error('ResourceCarousel: resourceTypes or resources must be provided');
108+
return null;
109+
}
110+
111+
const slidesPerView = {
112+
xs: 1,
113+
sm: 2,
114+
md: 3,
115+
lg: 3,
116+
xl: 3,
117+
};
118+
return (
119+
<Carousel
120+
title={title}
121+
theme="primary"
122+
showArrows={true}
123+
slidesPerView={slidesPerView}
124+
items={carouselStories.map((story) => {
125+
return (
126+
(story.content.component === 'resource_short_video' && (
127+
<Box p={0.25} minWidth="260px" width="260px" key={story.name}>
128+
<ShortsCard
129+
title={story.content.name}
130+
category={RESOURCE_CATEGORIES.SHORT_VIDEO}
131+
href={getDefaultFullSlug(story.full_slug, locale)}
132+
duration={story.content.duration}
133+
image={story.content.preview_image}
134+
/>
135+
</Box>
136+
)) ||
137+
(story.content.component === 'resource_conversation' && (
138+
<Box
139+
sx={{
140+
...getSlideWidth(1, 2, 3),
141+
}}
142+
padding={1}
143+
key={story.name}
144+
>
145+
<RelatedContentCard
146+
title={story.name}
147+
href={getDefaultFullSlug(story.full_slug, locale)}
148+
category={RESOURCE_CATEGORIES.CONVERSATION}
149+
duration={story.content.duration}
150+
/>
151+
</Box>
152+
))
153+
);
154+
})}
155+
/>
156+
);
157+
};
158+
159+
export default ResourceCarousel;

0 commit comments

Comments
 (0)