|
1 | 1 | import {BlogPost} from "../../types/blogPost"; |
2 | 2 | import classNames from "classnames"; |
3 | 3 | import Container from "../common/uiLibrary/container"; |
4 | | -import {toAgoTime} from "../../utils/timeUtils"; |
5 | 4 | import PageSectionTitle from "../common/uiLibrary/pageSectionTitle"; |
| 5 | +import Button from "../common/uiLibrary/Button"; |
6 | 6 |
|
7 | | -interface NewsCardProps { |
| 7 | +interface PostPreviewCardProps { |
8 | 8 | post: BlogPost, |
9 | 9 | isMain?: boolean, |
10 | 10 | className?: string |
11 | 11 | } |
12 | 12 |
|
13 | 13 | interface LatestNewsProps { |
14 | 14 | posts: BlogPost[] |
| 15 | + className?: string |
15 | 16 | } |
16 | 17 |
|
17 | | -const NewsCard = (props: NewsCardProps) => { |
18 | | - const {title, summary, socials_image, date_created, slug} = props.post; |
19 | | - const {className: classes} = props; |
20 | | - const isMain = props.isMain ?? false; |
| 18 | +const PostPreviewImage = ({post, isMain = false, className}: PostPreviewCardProps) => { |
| 19 | + const outerContainerStyles = classNames( |
| 20 | + {'flex-1 overflow-hidden': isMain}, |
| 21 | + {'w-full xl:w-1/3 overflow-hidden rounded-lg': !isMain}, |
| 22 | + ) |
21 | 23 |
|
22 | | - const style = { |
23 | | - backgroundImage: `url(${socials_image})`, |
24 | | - backgroundRepeat: 'no-repeat', |
25 | | - backgroundSize: 'cover', |
26 | | - backgroundBlendMode: 'multiply', |
27 | | - backgroundPosition: 'center', |
28 | | - } |
| 24 | + const innerContainerStyles = classNames( |
| 25 | + {'w-full h-32 lg:h-40': !isMain}, |
| 26 | + 'overflow-hidden', 'rounded-lg', |
| 27 | + {'h-full' : isMain} |
| 28 | + ) |
| 29 | + |
| 30 | + return ( |
| 31 | + <div className={classNames(outerContainerStyles, {className})}> |
| 32 | + <div className={innerContainerStyles}> |
| 33 | + <img |
| 34 | + src={post.socials_image} |
| 35 | + alt={post.title} |
| 36 | + className="w-full h-full object-cover transition-transform duration-300 ease-in-out transform group-hover:scale-105" |
| 37 | + loading="lazy" |
| 38 | + /> |
| 39 | + </div> |
| 40 | + </div> |
| 41 | + ) |
| 42 | +} |
| 43 | + |
| 44 | +const PostPreviewBody = ({post, isMain = false, className}: PostPreviewCardProps) => { |
| 45 | + const truncateSummary = (text: string) => { |
| 46 | + const maxChars = 100; |
29 | 47 |
|
30 | | - const truncateSummary = (summary: string, isMain: boolean) => { |
31 | 48 | if (isMain) { |
32 | | - return summary; |
| 49 | + return text; |
33 | 50 | } |
| 51 | + return text.length > maxChars ? text.slice(0, maxChars - 3) + '...' : text; |
| 52 | + }; |
| 53 | + |
| 54 | + const styles = classNames( |
| 55 | + 'mt-4', |
| 56 | + {'lg:mt-0 lg:ml-4 lg:flex-1': !isMain} |
| 57 | + |
| 58 | + ); |
34 | 59 |
|
35 | | - return summary.slice(0, 175) + '...'; |
36 | | - } |
| 60 | + const titleStyles = classNames( |
| 61 | + {'text-xl': isMain}, |
| 62 | + {'text-l': !isMain}, |
| 63 | + 'font-bold text-white-800 mb-2' |
| 64 | + ); |
37 | 65 |
|
38 | 66 | return ( |
39 | | - <a href={`/blog/${slug}`} className={classNames('relative overflow-hidden', classes)}> |
40 | | - <div className="absolute inset-0 rounded-lg shadow-lg bg-gray-400" style={style}></div> |
41 | | - <div className="relative p-3 h-full flex flex-col justify-between"> |
42 | | - <div> |
43 | | - <div |
44 | | - className={ |
45 | | - classNames('text-2xl text-white leading-tight border-b hover:border-dashed hover:border-gray-500', |
46 | | - {'lg:text-5xl': isMain})}>{title}</div> |
47 | | - <div className="text-normal text-gray-300"> |
48 | | - <span className=" pb-1">{toAgoTime(date_created)}</span> |
49 | | - </div> |
50 | | - </div> |
51 | | - <p className="">{truncateSummary(summary, isMain)}</p> |
52 | | - </div> |
53 | | - </a> |
| 67 | + <div className={classNames(styles, {className})}> |
| 68 | + <h3 className={titleStyles}>{post.title}</h3> |
| 69 | + <p className="text-sm text-gray-600 mb-2">{new Date(post.date_created).toLocaleDateString()}</p> |
| 70 | + <p className="text-gray-400">{truncateSummary(post.summary)}</p> |
| 71 | + </div> |
54 | 72 | ) |
55 | 73 | } |
56 | 74 |
|
| 75 | +const PostPreviewCard = ({post, isMain}: PostPreviewCardProps) => { |
| 76 | + const style = classNames( |
| 77 | + 'flex', |
| 78 | + 'flex-col', |
| 79 | + 'flex-1', |
| 80 | + 'hover:border-l hover:border-r hover:border-b hover:border-t hover:border-gray-700 hover:rounded-lg', |
| 81 | + 'group', |
| 82 | + 'mb-8 xl:mb-0', |
| 83 | + {'xl:max-w-[600px]': isMain}, |
| 84 | + {'xl:max-w-[550px] xl:flex-row': !isMain}, |
| 85 | + ); |
| 86 | + |
| 87 | + return ( |
| 88 | + <a href={`/blog/${post.slug}`} className={style}> |
| 89 | + <PostPreviewImage post={post} isMain={isMain} /> |
| 90 | + <PostPreviewBody post={post} isMain={isMain} /> |
| 91 | + </a> |
| 92 | + ); |
| 93 | +}; |
| 94 | + |
57 | 95 | const LatestNews = (props: LatestNewsProps) => { |
58 | | - const {posts} = props; |
| 96 | + const {posts, className} = props; |
59 | 97 |
|
60 | 98 | return ( |
61 | | - <> |
| 99 | + <div className={className}> |
62 | 100 | <PageSectionTitle>Latest News</PageSectionTitle> |
63 | 101 | <Container> |
64 | | - <div className="flex flex-col sm:flex-col md:flex-col lg:flex-row gap-5"> |
65 | | - <div className="w-full lg:w-1/2 flex flex-col"> |
66 | | - <NewsCard post={posts[0]} isMain className="h-full" /> |
| 102 | + <div className="flex flex-col gap-6"> |
| 103 | + <div className="flex flex-col xl:flex-row gap-6 justify-center"> |
| 104 | + <PostPreviewCard post={posts[0]} isMain={true} className="flex flex-col"/> |
| 105 | + |
| 106 | + <div className="flex flex-col justify-between gap-6 xl:gap-0"> |
| 107 | + {posts.slice(1).map((post) => ( |
| 108 | + <PostPreviewCard key={post.slug} post={post} isMain={false}/> |
| 109 | + ))} |
| 110 | + </div> |
67 | 111 | </div> |
68 | | - <div className="w-full lg:w-1/2 flex flex-col gap-4"> |
69 | | - {posts.slice(1).map((post, index) => ( |
70 | | - <NewsCard key={index} post={post} className="h-auto flex-grow" /> |
71 | | - ))} |
| 112 | + <div className="flex justify-end px-20"> |
| 113 | + <Button filled={false} > |
| 114 | + Read More |
| 115 | + </Button> |
72 | 116 | </div> |
73 | 117 | </div> |
74 | 118 | </Container> |
75 | | - </> |
| 119 | + </div> |
76 | 120 | ); |
77 | 121 | } |
78 | 122 |
|
|
0 commit comments