Skip to content

Commit 9fb157b

Browse files
committed
feat: slight redesign of landing page
1 parent 9729be4 commit 9fb157b

File tree

3 files changed

+177
-57
lines changed

3 files changed

+177
-57
lines changed

app/(home)/HomeBannerClient.tsx

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
'use client';
2+
3+
import layoutChildren from "../../types/layoutChildren";
4+
import {useEffect, useState} from "react";
5+
6+
const images: string[] = [
7+
"https://unitystationfile.b-cdn.net/Website-Statics/heroImages/bar-engine.png",
8+
"https://unitystationfile.b-cdn.net/Website-Statics/heroImages/clowns.png",
9+
"https://unitystationfile.b-cdn.net/Website-Statics/heroImages/conveyor.jpg",
10+
"https://unitystationfile.b-cdn.net/Website-Statics/heroImages/df.jpg",
11+
"https://unitystationfile.b-cdn.net/Website-Statics/heroImages/go-outsid.png",
12+
"https://unitystationfile.b-cdn.net/Website-Statics/heroImages/honk.jpg",
13+
"https://unitystationfile.b-cdn.net/Website-Statics/heroImages/hugger.png",
14+
"https://unitystationfile.b-cdn.net/Website-Statics/heroImages/lemons.png",
15+
"https://unitystationfile.b-cdn.net/Website-Statics/heroImages/shuttlecrash.png",
16+
"https://unitystationfile.b-cdn.net/Website-Statics/heroImages/chairs.jpg",
17+
];
18+
19+
let currentIndex = 0;
20+
21+
const shuffleImages = () => {
22+
for (let i = images.length - 1; i > 0; i--) {
23+
const j = Math.floor(Math.random() * (i + 1));
24+
[images[i], images[j]] = [images[j], images[i]];
25+
}
26+
};
27+
28+
const getNextImage = () => {
29+
currentIndex = (currentIndex + 1) % images.length;
30+
return images[currentIndex];
31+
};
32+
33+
const HomeBannerClient = (props: layoutChildren) => {
34+
const {children} = props;
35+
36+
shuffleImages();
37+
38+
const [image1, setImage1] = useState<string>(getNextImage());
39+
const [image2, setImage2] = useState<string>(getNextImage());
40+
const [showImage1, setShowImage1] = useState<boolean>(true);
41+
42+
useEffect(() => {
43+
const intervalId = setInterval(() => {
44+
setShowImage1(!showImage1);
45+
if (showImage1) {
46+
setImage2(getNextImage());
47+
} else {
48+
setImage1(getNextImage());
49+
}
50+
}, 10000);
51+
52+
return () => clearInterval(intervalId);
53+
}, [showImage1]);
54+
55+
56+
return (
57+
<div className="relative w-full h-full p-32">
58+
<div className="absolute inset-0 z-0 overflow-hidden">
59+
<div
60+
className={`absolute inset-0 bg-cover bg-center bg-no-repeat filter blur-xs transition-opacity duration-1000 ease-in-out ${showImage1 ? 'opacity-100' : 'opacity-0'}`}
61+
style={{backgroundImage: `url(${image1})`, backgroundPosition: `center`}}></div>
62+
<div
63+
className={`absolute inset-0 bg-cover bg-center bg-no-repeat filter blur-xs transition-opacity duration-1000 ease-in-out ${showImage1 ? 'opacity-0' : 'opacity-100'}`}
64+
style={{backgroundImage: `url(${image2})`, backgroundPosition: `center`}}></div>
65+
<div className="absolute inset-0 bg-black opacity-50 "></div>
66+
</div>
67+
<div className="relative z-10">
68+
{children}
69+
</div>
70+
</div>
71+
)
72+
73+
}
74+
75+
export default HomeBannerClient;

app/(home)/latestNews.tsx

Lines changed: 86 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,78 +1,122 @@
11
import {BlogPost} from "../../types/blogPost";
22
import classNames from "classnames";
33
import Container from "../common/uiLibrary/container";
4-
import {toAgoTime} from "../../utils/timeUtils";
54
import PageSectionTitle from "../common/uiLibrary/pageSectionTitle";
5+
import Button from "../common/uiLibrary/Button";
66

7-
interface NewsCardProps {
7+
interface PostPreviewCardProps {
88
post: BlogPost,
99
isMain?: boolean,
1010
className?: string
1111
}
1212

1313
interface LatestNewsProps {
1414
posts: BlogPost[]
15+
className?: string
1516
}
1617

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+
)
2123

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;
2947

30-
const truncateSummary = (summary: string, isMain: boolean) => {
3148
if (isMain) {
32-
return summary;
49+
return text;
3350
}
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+
);
3459

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+
);
3765

3866
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>
5472
)
5573
}
5674

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+
5795
const LatestNews = (props: LatestNewsProps) => {
58-
const {posts} = props;
96+
const {posts, className} = props;
5997

6098
return (
61-
<>
99+
<div className={className}>
62100
<PageSectionTitle>Latest News</PageSectionTitle>
63101
<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>
67111
</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>
72116
</div>
73117
</div>
74118
</Container>
75-
</>
119+
</div>
76120
);
77121
}
78122

app/(home)/page.tsx

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -50,26 +50,27 @@ const fetchLatestBlogPost = async (): Promise<BlogPost[]> => {
5050
}
5151

5252
const HomePage = async () => {
53-
5453
const latestBlogPosts: BlogPost[] = await fetchLatestBlogPost();
55-
const NoSsrHeroImageClient = dynamic(() => import('./HeroRandomImageClient'), {ssr: false});
54+
const NoSsrHomebannerClient = dynamic(() => import('./HomeBannerClient'), { ssr: false });
5655

5756
return (
5857
<>
59-
<PageSection className="gap-16">
60-
<NoSsrHeroImageClient>
61-
<LandingText mainText={mainText} secondaryText={secondaryText}/>
62-
<DownloadButtonClient/>
63-
<LandingButtonsServer/>
64-
</NoSsrHeroImageClient>
65-
<FeaturesList features={features}/>
66-
</PageSection>
67-
<PageSection verticalCenter={false}>
68-
<LatestNews posts={latestBlogPosts}/>
69-
</PageSection>
58+
<div className="flex flex-col justify-center gap-6">
59+
<div className="flex-[1]">
60+
<NoSsrHomebannerClient>
61+
<LandingText mainText={mainText} secondaryText={secondaryText}/>
62+
<DownloadButtonClient/>
63+
<LandingButtonsServer/>
64+
</NoSsrHomebannerClient>
65+
</div>
66+
<div className="flex-[1]">
67+
<LatestNews posts={latestBlogPosts}/>
68+
</div>
69+
</div>
70+
7071
<ContactInformation/>
7172
</>
72-
)
73-
}
73+
);
74+
};
7475

7576
export default HomePage;

0 commit comments

Comments
 (0)