Skip to content

Commit 52de20b

Browse files
committed
feat: add testimonial section
1 parent 20115c2 commit 52de20b

File tree

13 files changed

+11436
-1007
lines changed

13 files changed

+11436
-1007
lines changed

package-lock.json

Lines changed: 10509 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@
5858
"@typescript-eslint/eslint-plugin": "^8.0.1",
5959
"@typescript-eslint/parser": "^8.0.1",
6060
"@vitejs/plugin-react": "^4.3.3",
61+
"daisyui": "^5.0.3",
6162
"eslint": "^8",
6263
"eslint-config-next": "14.2.5",
6364
"eslint-config-prettier": "^9.1.0",

src/app/[locale]/globals.css

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
@tailwind base;
22
@tailwind components;
33
@tailwind utilities;
4+
@plugin "daisyui";
45

56
@layer base {
67
:root {

src/app/[locale]/testimonial/page.tsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { TestimonialPage } from "@/features/testimonial";
2+
3+
const Testimony = () => {
4+
return <TestimonialPage />;
5+
};
6+
7+
export default Testimony;

src/components/layout/Navbar/Navbar.test.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ const messages = {
88
navbar: {
99
"link-1": "About Us",
1010
"link-2": "Events",
11-
"link-3": "Donate",
11+
"link-3": "Testimonial",
12+
"link-4": "Donate",
1213
},
1314
},
1415
Announcement: {

src/components/layout/Navbar/constant.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@ export const LINKS: NavLinkProps<ValidPathnames>[] = [
1212
},
1313
{
1414
id: "3",
15+
href: "/testimonial",
16+
},
17+
{
18+
id: "4",
1519
href: "/support-us",
1620
},
1721
];

src/features/home/constants.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,7 @@ export const testimonialData: TestimonialType[] = [
197197
},
198198
{
199199
id: 7,
200-
name: "Faldi",
200+
name: "Faldiansyah",
201201
role: "Ex Software Engineer Intern",
202202
quote:
203203
"Banyak hal yang bisa kita ambil dalam komunitas ini, selain untuk belajar kita juga mendapatkan banyak jaringan. Bersama dengan teman-teman dan mentor yang saling memotivasi untuk kemajuan kedepan.",
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
"use client";
2+
import React, { useState, useEffect, useRef } from "react";
3+
import { useTranslations } from "next-intl";
4+
import { testimonialData } from "../home/constants";
5+
import { Card, CardContent, CardHeader } from "@/components/ui/Card";
6+
import Image from "next/image";
7+
import Link from "next/link";
8+
import { Dialog, DialogTrigger } from "@/components/ui/Dialog";
9+
import DetailTestimoni from "../home/components/DetailTestimoni";
10+
11+
const TestimonialPage = () => {
12+
const t = useTranslations("TestimonialPage");
13+
const [visibleCount, setVisibleCount] = useState(0); // 0 dulu biar shimmer
14+
const [isLoading, setIsLoading] = useState(true);
15+
const observerRef = useRef<IntersectionObserver | null>(null);
16+
17+
// jumlah data awal sesuai ukuran layar
18+
const getInitialVisibleCount = () => {
19+
if (window.innerWidth >= 1024) return 6;
20+
if (window.innerWidth >= 768) return 4;
21+
return 2;
22+
};
23+
24+
// shimmer tampilkan data awal
25+
useEffect(() => {
26+
const initialCount = getInitialVisibleCount();
27+
setTimeout(() => {
28+
setVisibleCount(initialCount);
29+
setIsLoading(false);
30+
}, 1000);
31+
}, []);
32+
33+
// lazy load buat scroll
34+
useEffect(() => {
35+
observerRef.current = new IntersectionObserver(
36+
(entries) => {
37+
const entry = entries[0];
38+
if (entry.isIntersecting) {
39+
setIsLoading(true);
40+
setTimeout(() => {
41+
setVisibleCount((prevCount) => {
42+
const cols = getInitialVisibleCount();
43+
return Math.min(prevCount + cols, testimonialData.length);
44+
});
45+
setIsLoading(false);
46+
}, 1000);
47+
}
48+
},
49+
{ threshold: 1 }
50+
);
51+
52+
const loadMoreTrigger = document.getElementById("loadMoreTrigger");
53+
if (loadMoreTrigger) {
54+
observerRef.current?.observe(loadMoreTrigger);
55+
}
56+
57+
return () => {
58+
if (observerRef.current) observerRef.current?.disconnect();
59+
};
60+
}, [visibleCount]);
61+
62+
return (
63+
<>
64+
<div className="container mx-auto px-5 pt-24 pb-28">
65+
<div className="w-full">
66+
<h1 className="text-hmc-base-blue text-xl sm:text-3xl font-semibold">{t("title")}</h1>
67+
<p className="text-gray-500 text-xs sm:text-base">{t("description")}</p>
68+
</div>
69+
<div className="mt-6">
70+
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
71+
{isLoading
72+
? Array(getInitialVisibleCount()) // shimmer jumlah sesuai grid
73+
.fill(null)
74+
.map((_, index) => <ShimmerCard key={index} />)
75+
: testimonialData.slice(0, visibleCount).map((data) => (
76+
<Card key={data.id}>
77+
<CardHeader>
78+
<div className="flex justify-center w-full">
79+
<div className="space-y-4">
80+
<Image
81+
src={data.avatar_url}
82+
alt={data.name}
83+
width={140}
84+
height={140}
85+
loading="lazy"
86+
className="w-28 md:w-32 lg:w-36 rounded-full object-cover border-4"
87+
/>
88+
</div>
89+
</div>
90+
</CardHeader>
91+
<CardContent>
92+
<div className="flex flex-col items-center gap-1">
93+
<h3 className="font-semibold">{data.name}</h3>
94+
<p className="text-center text-slate-400 dark:bg-slate-400 text-sm">
95+
{`${data.role} at `}
96+
<Link className="font-semibold" href={data.company_url} target="_blank">
97+
{data.company_name}
98+
</Link>
99+
</p>
100+
<Image
101+
className="mt-4 self-start"
102+
src="/assets/icons/ic_qoute.svg"
103+
height={24}
104+
width={24}
105+
alt="qoute"
106+
/>
107+
<Dialog>
108+
<DialogTrigger asChild>
109+
<p className="cursor-pointer text-slate-400 dark:text-slate-400 text-sm leading-6 pt-3 line-clamp-4">
110+
{data.quote}
111+
</p>
112+
</DialogTrigger>
113+
<DetailTestimoni data={data} />
114+
</Dialog>
115+
</div>
116+
</CardContent>
117+
</Card>
118+
))}
119+
</div>
120+
</div>
121+
122+
{visibleCount < testimonialData.length && <div id="loadMoreTrigger" className="h-10"></div>}
123+
</div>
124+
</>
125+
);
126+
};
127+
128+
const ShimmerCard = () => <div className="bg-gray-200 dark:bg-gray-700 animate-pulse rounded-lg p-4 h-96"></div>;
129+
130+
export default TestimonialPage;

src/features/testimonial/index.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { default as TestimonialPage } from "./TestimonialPage";

src/lib/navigation/config.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@ export const pathnames: Pathnames<Locales> = {
1111
en: "/events",
1212
id: "/events",
1313
},
14+
"/testimonial": {
15+
en: "/testimonial",
16+
id: "/testimonial",
17+
},
1418
"/support-us": {
1519
en: "/support-us",
1620
id: "/support-us",

src/locales/en.json

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,14 @@
33
"navbar": {
44
"link-1": "About Us",
55
"link-2": "Events",
6-
"link-3": "Donate"
6+
"link-3": "Testimonial",
7+
"link-4": "Donate"
78
},
89
"breadcrumb": {
910
"link-1": "About Us",
1011
"link-2": "Events",
11-
"link-3": "Donate"
12+
"link-3": "Testimonial",
13+
"link-4": "Donate"
1214
}
1315
},
1416
"LocaleLayout": {
@@ -149,5 +151,9 @@
149151
},
150152
"Announcement": {
151153
"pdd-2024": "🚀 Hi there! Our annual tech conference Palu Developer Day is around the corner! Check it out!"
154+
},
155+
"TestimonialPage": {
156+
"title": "Testimonial",
157+
"description": "Some friends and alumni who feel the benefits from Hammercode"
152158
}
153159
}

src/locales/id.json

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,14 @@
33
"navbar": {
44
"link-1": "Tentang Kami",
55
"link-2": "Acara",
6-
"link-3": "Donasi"
6+
"link-3": "Testimoni",
7+
"link-4": "Donasi"
78
},
89
"breadcrumb": {
910
"link-1": "Tentang Kami",
1011
"link-2": "Acara",
11-
"link-3": "Donasi"
12+
"link-3": "Testimoni",
13+
"link-4": "Donasi"
1214
}
1315
},
1416
"LocaleLayout": {
@@ -149,5 +151,9 @@
149151
},
150152
"Announcement": {
151153
"pdd-2024": "🚀 Halo! Acara tahunan teknologi Palu Developer Day sudah dekat! Klik di sini!"
154+
},
155+
"TestimonialPage": {
156+
"title": "Testimoni",
157+
"description": "Berbagai testimonial yang dapat membantu kami memahami manfaat Hammercode"
152158
}
153159
}

0 commit comments

Comments
 (0)