Skip to content

Commit dffce14

Browse files
Ahtesham QuraishAhtesham Quraish
authored andcommitted
feat: implement podcast design
1 parent 6d3fbfd commit dffce14

16 files changed

Lines changed: 1219 additions & 9 deletions

File tree

Lines changed: 3 additions & 0 deletions
Loading
779 KB
Loading
243 KB
Loading
Lines changed: 4 additions & 0 deletions
Loading
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
"use client"
2+
3+
import React from "react"
4+
import PodcastPageTemplate from "./PodcastPageTemplate"
5+
import { useLearningResourcesList } from "api/hooks/learningResources"
6+
import { LearningResource, PodcastEpisodeResource } from "api"
7+
8+
const PodcastPage: React.FC = () => {
9+
const { data: episodesData } = useLearningResourcesList({
10+
resource_type: ["podcast_episode"],
11+
limit: 4,
12+
sortby: "new",
13+
})
14+
15+
const { data: podcastsData } = useLearningResourcesList({
16+
resource_type: ["podcast"],
17+
limit: 15,
18+
sortby: "new",
19+
})
20+
21+
const episodes = (episodesData?.results ?? []) as PodcastEpisodeResource[]
22+
const podcasts = (podcastsData?.results ?? []) as LearningResource[]
23+
24+
return <PodcastPageTemplate episodes={episodes} podcasts={podcasts} />
25+
}
26+
27+
export default PodcastPage
Lines changed: 304 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,304 @@
1+
import React from "react"
2+
import {
3+
styled,
4+
Container,
5+
Breadcrumbs,
6+
Stack,
7+
BannerBackground,
8+
Grid2,
9+
} from "ol-components"
10+
import { LearningResource, PodcastEpisodeResource, ResourceTypeEnum } from "api"
11+
import { RiMailLine } from "@remixicon/react"
12+
import { HOME as HOME_URL } from "../../common/urls"
13+
import RecentEpisodesPanel from "./RecentEpisodesPanel"
14+
import PodcastSubscribePopover from "./PodcastSubscribePopover"
15+
import { ResourceCard } from "@/page-components/ResourceCard/ResourceCard"
16+
const DEFAULT_BACKGROUND_IMAGE_URL =
17+
"/images/backgrounds/background_podcast.png"
18+
const DEFAULT_BACKGROUND_IMAGE_MOBILE_URL =
19+
"/images/backgrounds/background_podcast_mobile.png"
20+
21+
const StyledBannerBackground = styled(BannerBackground)(({ theme }) => ({
22+
position: "relative",
23+
overflow: "hidden",
24+
padding: "48px 0 64px 0",
25+
[theme.breakpoints.down("md")]: {
26+
padding: "40px 0 16px 0",
27+
},
28+
[theme.breakpoints.down("sm")]: {
29+
backgroundImage: `linear-gradient(rgba(0 0 0 / 50%), rgba(0 0 0 / 50%)), url('${DEFAULT_BACKGROUND_IMAGE_MOBILE_URL}')`,
30+
backgroundAttachment: "scroll",
31+
backgroundPosition: "center center",
32+
backgroundSize: "cover",
33+
},
34+
}))
35+
36+
const SubscriptionButtonContainer = styled.div(({ theme }) => ({
37+
position: "relative",
38+
minHeight: "38px",
39+
display: "flex",
40+
marginTop: "32px",
41+
[theme.breakpoints.down("sm")]: {
42+
marginTop: "24px",
43+
},
44+
}))
45+
46+
const BackgroundVector = styled("img")(({ theme }) => ({
47+
position: "absolute",
48+
top: 0,
49+
right: 0,
50+
width: 320,
51+
maxWidth: "45%",
52+
height: "auto",
53+
pointerEvents: "none",
54+
zIndex: 0,
55+
[theme.breakpoints.down("md")]: {
56+
width: 100,
57+
maxWidth: "55%",
58+
},
59+
}))
60+
61+
const BannerForeground = styled.div({
62+
position: "relative",
63+
zIndex: 1,
64+
})
65+
66+
const BackgroundCircles = styled("img")(({ theme }) => ({
67+
position: "absolute",
68+
bottom: 0,
69+
left: 0,
70+
width: 800,
71+
height: "auto",
72+
pointerEvents: "none",
73+
zIndex: 0,
74+
transform: "translate(-50%, 50%)",
75+
[theme.breakpoints.down("md")]: {
76+
width: 320,
77+
},
78+
}))
79+
80+
const BannerTitle = styled("h1")(({ theme }) => ({
81+
color: theme.custom.colors.lightRed,
82+
...theme.typography.h1,
83+
margin: 0,
84+
[theme.breakpoints.down("md")]: {
85+
...theme.typography.h2,
86+
},
87+
[theme.breakpoints.down("sm")]: {
88+
...theme.typography.h3,
89+
marginTop: "8px",
90+
},
91+
}))
92+
93+
const BannerDescription = styled("p")(({ theme }) => ({
94+
color: theme.custom.colors.white,
95+
...theme.typography.h3,
96+
margin: 0,
97+
[theme.breakpoints.down("md")]: {
98+
...theme.typography.h2,
99+
},
100+
[theme.breakpoints.down("sm")]: {
101+
...theme.typography.h5,
102+
marginTop: "8px",
103+
},
104+
}))
105+
106+
const BannerContent = styled.div(({ theme }) => ({
107+
display: "flex",
108+
flexDirection: "row",
109+
alignItems: "center",
110+
justifyContent: "space-between",
111+
[theme.breakpoints.down("md")]: {
112+
flexDirection: "column",
113+
},
114+
}))
115+
116+
const LeftGrid = styled(Grid2)({
117+
alignItems: "center",
118+
display: "flex",
119+
})
120+
121+
const ContentContainer = styled(Container)(({ theme }) => ({
122+
[theme.breakpoints.down("md")]: {
123+
paddingLeft: "24px",
124+
paddingRight: "24px",
125+
},
126+
}))
127+
128+
const LearningResourceCard = styled(ResourceCard)(({ theme }) => ({
129+
marginBottom: "32px",
130+
[theme.breakpoints.down("sm")]: {
131+
marginBottom: 0,
132+
gap: "16px",
133+
},
134+
}))
135+
136+
const PodcastsSection = styled.div(({ theme }) => ({
137+
padding: "48px 0 64px",
138+
backgroundColor: theme.custom.colors.white,
139+
[theme.breakpoints.down("md")]: {
140+
padding: "32px 0 40px",
141+
},
142+
[theme.breakpoints.down("sm")]: {
143+
padding: "24px 0 40px",
144+
},
145+
}))
146+
147+
const BelowMdOnly = styled.div(({ theme }) => ({
148+
[theme.breakpoints.up("md")]: {
149+
display: "none",
150+
},
151+
}))
152+
153+
const AboveMdOnly = styled.div(({ theme }) => ({
154+
[theme.breakpoints.down("md")]: {
155+
display: "none",
156+
},
157+
}))
158+
159+
const MobilePodcastList = styled.div(({ theme }) => ({
160+
display: "flex",
161+
flexDirection: "column",
162+
gap: "12px",
163+
padding: `0 ${theme.spacing(2)}`,
164+
[theme.breakpoints.down("sm")]: {
165+
gap: "16px",
166+
},
167+
}))
168+
169+
const PodcastsGrid = styled.div(({ theme }) => ({
170+
display: "grid",
171+
gridTemplateColumns: "repeat(6, 1fr)",
172+
gap: "24px",
173+
[theme.breakpoints.down("lg")]: {
174+
gridTemplateColumns: "repeat(4, 1fr)",
175+
},
176+
[theme.breakpoints.down("md")]: {
177+
gridTemplateColumns: "repeat(4, 1fr)",
178+
gap: "16px",
179+
},
180+
[theme.breakpoints.down("sm")]: {
181+
gridTemplateColumns: "repeat(2, 1fr)",
182+
gap: "12px",
183+
},
184+
}))
185+
186+
interface PodcastPageTemplateProps {
187+
children?: React.ReactNode
188+
episodes: PodcastEpisodeResource[]
189+
podcasts: LearningResource[]
190+
}
191+
192+
const PodcastPageTemplate: React.FC<PodcastPageTemplateProps> = ({
193+
episodes,
194+
podcasts,
195+
}) => {
196+
return (
197+
<>
198+
<StyledBannerBackground
199+
backgroundUrl={DEFAULT_BACKGROUND_IMAGE_URL}
200+
backgroundDim={50}
201+
>
202+
<BackgroundVector src="/images/Vector.svg" alt="" aria-hidden="true" />
203+
<BackgroundCircles
204+
src="/images/circles.svg"
205+
alt=""
206+
aria-hidden="true"
207+
/>
208+
<BannerForeground>
209+
<ContentContainer>
210+
<Breadcrumbs
211+
variant="dark"
212+
ancestors={[{ href: HOME_URL, label: "Home" }]}
213+
current="podcasts"
214+
/>
215+
216+
<BannerContent>
217+
<Grid2
218+
container
219+
spacing={{ xs: 2, sm: 2, md: 20 }}
220+
style={{ width: "100%" }}
221+
>
222+
<LeftGrid size={{ xs: 12, sm: 6, md: 7 }}>
223+
<Stack>
224+
<BannerTitle>MIT Podcasts</BannerTitle>
225+
226+
<BannerDescription>
227+
for learners, thinkers, and innovators.
228+
</BannerDescription>
229+
230+
<Stack
231+
flexDirection="row"
232+
alignItems="end"
233+
sx={{
234+
flexGrow: 0,
235+
width: "100%",
236+
flexShrink: 1,
237+
}}
238+
>
239+
<SubscriptionButtonContainer>
240+
<PodcastSubscribePopover
241+
buttonLabel="Subscribe to new episodes"
242+
buttonIcon={<RiMailLine size={20} />}
243+
/>
244+
</SubscriptionButtonContainer>
245+
</Stack>
246+
</Stack>
247+
</LeftGrid>
248+
<Grid2
249+
size={{ xs: 12, sm: 6, md: 5 }}
250+
style={{ display: "flex", alignSelf: "center" }}
251+
>
252+
<RecentEpisodesPanel episodes={episodes} />
253+
</Grid2>
254+
</Grid2>
255+
</BannerContent>
256+
</ContentContainer>
257+
</BannerForeground>
258+
</StyledBannerBackground>
259+
260+
{podcasts.length > 0 ? (
261+
<PodcastsSection>
262+
<BelowMdOnly>
263+
<MobilePodcastList>
264+
{podcasts.map((resource) => (
265+
<LearningResourceCard
266+
key={resource.id}
267+
resource={resource}
268+
footerLabel={
269+
resource.resource_type === ResourceTypeEnum.Podcast
270+
? `${resource.podcast?.episode_count} Episodes`
271+
: undefined
272+
}
273+
parentHeadingEl="h4"
274+
list
275+
/>
276+
))}
277+
</MobilePodcastList>
278+
</BelowMdOnly>
279+
<AboveMdOnly>
280+
<Container>
281+
<PodcastsGrid>
282+
{podcasts.map((resource) => (
283+
<LearningResourceCard
284+
key={resource.id}
285+
resource={resource}
286+
footerLabel={
287+
resource.resource_type === ResourceTypeEnum.Podcast
288+
? `${resource.podcast?.episode_count} Episodes`
289+
: undefined
290+
}
291+
parentHeadingEl="h4"
292+
size="small"
293+
/>
294+
))}
295+
</PodcastsGrid>
296+
</Container>
297+
</AboveMdOnly>
298+
</PodcastsSection>
299+
) : null}
300+
</>
301+
)
302+
}
303+
304+
export default PodcastPageTemplate

0 commit comments

Comments
 (0)