Skip to content

Commit 17b4ba2

Browse files
authored
Merge pull request #14 from devguru99/dev
completed single post page
2 parents 71dae64 + 11bc28c commit 17b4ba2

File tree

18 files changed

+609
-49
lines changed

18 files changed

+609
-49
lines changed

src/components/About/Recovery.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import Typography from '@mui/material/Typography';
77
import { motion } from 'framer-motion';
88
import { Button } from '@mui/material';
99
import { ChevronRight } from '@mui/icons-material';
10+
import { Link } from 'react-router-dom';
1011

1112

1213
export default function Recovery() {
@@ -109,6 +110,8 @@ export default function Recovery() {
109110
}}
110111
>
111112
<Button variant="contained"
113+
component={Link}
114+
to="/contact"
112115
sx={{
113116
padding: '10px 20px',
114117
fontSize: '1rem',

src/components/Blogpost/Banner.tsx

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
import React from 'react';
2+
import { Box, Typography, Button, Container, Stack, TextField } from '@mui/material';
3+
import { motion } from 'framer-motion';
4+
import { Link } from 'react-router-dom';
5+
6+
const backgroundImage = '/banner.png';
7+
8+
export default function Banner () {
9+
return (
10+
11+
<Box
12+
sx={{
13+
display: 'flex',
14+
backgroundImage: `url(${backgroundImage})`,
15+
backgroundSize: 'cover',
16+
color: '#fff',
17+
height: 300,
18+
}}
19+
>
20+
<Container id="blogs-banner"
21+
sx={{
22+
pt: { xs: 4, sm: 12 },
23+
pb: { xs: 8, sm: 16 },
24+
position: 'relative',
25+
display: 'flex',
26+
flexDirection: 'column',
27+
justifyContent: 'center',
28+
WebkitAlignItems: 'flex-start',
29+
gap: { xs: 3, sm: 6 },
30+
}}
31+
>
32+
<motion.div
33+
initial={{ opacity: 0, y: 30 }}
34+
whileInView={{ opacity: 1, y: 0 }}
35+
viewport={{ once: true }}
36+
transition={{
37+
duration: 1,
38+
delay: 0.1,
39+
ease: [0.215, 0.61, 0.355, 1]
40+
}}
41+
>
42+
<Typography
43+
component="h2"
44+
variant="h3"
45+
gutterBottom
46+
sx={{ color: 'text.primary', fontWeight: 700 , display: 'flex', flexDirection:'row', flexWrap: 'wrap', alignItems: 'center', textAlign: 'center' }}
47+
>
48+
Stay Connected
49+
</Typography>
50+
<Typography
51+
sx={{
52+
textAlign: 'left',
53+
color: 'text.secondary',
54+
width: '70%',
55+
mb: 2,
56+
}}
57+
>
58+
Want to stay updated with new posts and tips for your recovery journey? Subscribe to our newsletter and never miss an article!
59+
</Typography>
60+
<Stack direction="row" spacing={1} useFlexGap>
61+
<TextField
62+
id="email-newsletter-blogpost"
63+
hiddenLabel
64+
size="small"
65+
variant="outlined"
66+
fullWidth
67+
aria-label="Enter your email address"
68+
placeholder="Your email address"
69+
slotProps={{
70+
htmlInput: {
71+
autoComplete: 'off',
72+
'aria-label': 'Enter your email address',
73+
},
74+
}}
75+
sx={{ width: '250px' }}
76+
/>
77+
<Button
78+
variant="contained"
79+
color="primary"
80+
size="small"
81+
sx={{ flexShrink: 0 }}
82+
>
83+
Subscribe
84+
</Button>
85+
</Stack>
86+
</motion.div>
87+
</Container>
88+
</Box>
89+
);
90+
};
Lines changed: 262 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,262 @@
1+
import * as React from 'react';
2+
import Avatar from '@mui/material/Avatar';
3+
import AvatarGroup from '@mui/material/AvatarGroup';
4+
import Box from '@mui/material/Box';
5+
import Card from '@mui/material/Card';
6+
import CardContent from '@mui/material/CardContent';
7+
import CardMedia from '@mui/material/CardMedia';
8+
import Chip from '@mui/material/Chip';
9+
import Grid from '@mui/material/Grid2';
10+
import Typography from '@mui/material/Typography';
11+
import { styled } from '@mui/material/styles';
12+
import Container from '@mui/material/Container';
13+
import Stack from '@mui/material/Stack';
14+
import blogsData from '@/utils/blogs';
15+
import { Link } from 'react-router-dom';
16+
import { useParams } from 'react-router-dom';
17+
18+
const StyledCard = styled(Card)(({ theme }) => ({
19+
display: 'flex',
20+
flexDirection: 'column',
21+
padding: 0,
22+
height: '100%',
23+
backgroundColor: 'transparent',
24+
'&:hover': {
25+
boxShadow: '0 4px 12px 0 rgba(0,0,0,0.12)',
26+
cursor: 'pointer',
27+
},
28+
'&:focus-visible': {
29+
outline: '3px solid',
30+
outlineColor: 'hsla(210, 98%, 48%, 0.5)',
31+
outlineOffset: '2px',
32+
},
33+
}));
34+
35+
const StyledCardContent = styled(CardContent)({
36+
display: 'flex',
37+
flexDirection: 'column',
38+
gap: 4,
39+
padding: 16,
40+
flexGrow: 1,
41+
'&:last-child': {
42+
paddingBottom: 16,
43+
},
44+
});
45+
46+
const StyledTypography = styled(Typography)({
47+
display: '-webkit-box',
48+
WebkitBoxOrient: 'vertical',
49+
WebkitLineClamp: 2,
50+
overflow: 'hidden',
51+
textOverflow: 'ellipsis',
52+
});
53+
54+
function Author({ authors }: { authors: { name: string; avatar: string }[] }) {
55+
return (
56+
<Box
57+
sx={{
58+
display: 'flex',
59+
flexDirection: 'row',
60+
gap: 2,
61+
alignItems: 'center',
62+
justifyContent: 'space-between',
63+
padding: '16px',
64+
}}
65+
>
66+
<Box
67+
sx={{ display: 'flex', flexDirection: 'row', gap: 1, alignItems: 'center' }}
68+
>
69+
<AvatarGroup max={3}>
70+
{authors.map((author, index) => (
71+
<Avatar
72+
key={index}
73+
alt={author.name}
74+
src={author.avatar}
75+
sx={{ width: 24, height: 24 }}
76+
/>
77+
))}
78+
</AvatarGroup>
79+
<Typography variant="caption">
80+
{authors.map((author) => author.name).join(', ')}
81+
</Typography>
82+
</Box>
83+
<Typography variant="caption">July 14, 2024</Typography>
84+
</Box>
85+
);
86+
}
87+
88+
interface AuthorType {
89+
name: string;
90+
avatar: string;
91+
}
92+
93+
interface CardData {
94+
img: string;
95+
tag: string;
96+
title: string;
97+
slug: string;
98+
description: string;
99+
authors: AuthorType[];
100+
}
101+
102+
interface CardComponentProps {
103+
card: CardData;
104+
isFocused: boolean;
105+
handleFocus: (index: number) => void;
106+
handleBlur: () => void;
107+
cardIndex: number;
108+
}
109+
110+
const getRandomItemsExcludingSlug = (
111+
array: CardData[],
112+
count: number,
113+
excludeSlug: string
114+
): CardData[] => {
115+
const filteredArray = array.filter((item) => item.slug !== excludeSlug);
116+
const shuffled = [...filteredArray].sort(() => 0.5 - Math.random());
117+
return shuffled.slice(0, count);
118+
};
119+
120+
const CardComponent: React.FC<CardComponentProps> = ({
121+
card,
122+
isFocused,
123+
handleFocus,
124+
handleBlur,
125+
cardIndex,
126+
}) => (
127+
<StyledCard
128+
variant="outlined"
129+
onFocus={() => handleFocus(cardIndex)}
130+
onBlur={handleBlur}
131+
tabIndex={0}
132+
className={isFocused ? 'Mui-focused' : ''}
133+
>
134+
<CardMedia
135+
component="img"
136+
alt={card.title}
137+
image={card.img}
138+
sx={{
139+
height: { sm: 'auto', md: '50%' },
140+
aspectRatio: { sm: '16 / 9', md: '' },
141+
}}
142+
/>
143+
<StyledCardContent>
144+
<Typography gutterBottom variant="caption" component="div">
145+
{card.tag}
146+
</Typography>
147+
<Typography gutterBottom variant="h6" component="div">
148+
{card.title}
149+
</Typography>
150+
<StyledTypography variant="body2" color="text.secondary" gutterBottom>
151+
{card.description}
152+
</StyledTypography>
153+
</StyledCardContent>
154+
<Author authors={card.authors} />
155+
</StyledCard>
156+
);
157+
158+
export default function Related() {
159+
const [focusedCardIndex, setFocusedCardIndex] = React.useState<number | null>(
160+
null,
161+
);
162+
const { slug } = useParams<{ slug: string }>();
163+
164+
const cardData = React.useMemo(() => {
165+
return getRandomItemsExcludingSlug(blogsData, 3, slug || '');
166+
}, [blogsData, slug]);
167+
168+
const handleFocus = (index: number) => {
169+
setFocusedCardIndex(index);
170+
};
171+
172+
const handleBlur = () => {
173+
setFocusedCardIndex(null);
174+
};
175+
176+
const handleClick = () => {
177+
console.info('You clicked the filter chip.');
178+
};
179+
180+
return (
181+
<Container
182+
id="blogs-related"
183+
maxWidth="lg"
184+
component="main"
185+
sx={{ display: 'flex', flexDirection: 'column', my: 10, gap: 4 }}
186+
>
187+
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
188+
<div>
189+
<Typography variant="h1" gutterBottom>
190+
Related Articles
191+
</Typography>
192+
</div>
193+
<Box
194+
sx={{
195+
display: 'flex',
196+
flexDirection: { xs: 'column-reverse', md: 'row' },
197+
width: '100%',
198+
justifyContent: 'space-between',
199+
alignItems: { xs: 'start', md: 'center' },
200+
gap: 4,
201+
overflow: 'auto',
202+
}}
203+
>
204+
</Box>
205+
<Grid container spacing={2} columns={12}>
206+
{cardData.map((card, index) => (
207+
<Grid key={index} size={{ xs: 12, md: 4 }}>
208+
<Link to={`/blog/${card.slug}`} style={{ textDecoration: 'none' }}>
209+
<CardComponent
210+
card={card}
211+
isFocused={focusedCardIndex === index}
212+
handleFocus={handleFocus}
213+
handleBlur={handleBlur}
214+
cardIndex={index}
215+
/>
216+
</Link>
217+
</Grid>
218+
))}
219+
</Grid>
220+
<Stack
221+
direction={{ xs: 'row', sm: 'row' }}
222+
spacing={1}
223+
useFlexGap
224+
sx={{ width: { xs: '100%', sm: 'auto' }, justifyContent: 'flex-end' }}
225+
>
226+
<Box
227+
component="img"
228+
src='/social_facebook.png'
229+
sx={{
230+
width: 35,
231+
height: 35,
232+
}}
233+
/>
234+
<Box
235+
component="img"
236+
src='/social_twitter.png'
237+
sx={{
238+
width: 35,
239+
height: 35,
240+
}}
241+
/>
242+
<Box
243+
component="img"
244+
src='/social_instagram.png'
245+
sx={{
246+
width: 35,
247+
height: 35,
248+
}}
249+
/>
250+
<Box
251+
component="img"
252+
src='/social_linkedin.png'
253+
sx={{
254+
width: 35,
255+
height: 35,
256+
}}
257+
/>
258+
</Stack>
259+
</Box>
260+
</Container>
261+
);
262+
}

0 commit comments

Comments
 (0)