Skip to content

Commit b043ff5

Browse files
feat(web): add video testimonials section
1 parent 81dcec9 commit b043ff5

File tree

1 file changed

+96
-3
lines changed

1 file changed

+96
-3
lines changed

apps/web/src/app/(home)/_components/testimonials.tsx

Lines changed: 96 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,24 @@
11
"use client";
22

3-
import { motion } from "framer-motion";
4-
import { Terminal } from "lucide-react";
3+
import { Play, Terminal } from "lucide-react";
4+
import { motion } from "motion/react";
55
import Image from "next/image";
66
import { Suspense } from "react";
77
import { Tweet, TweetSkeleton, type TwitterComponents } from "react-tweet";
88

9+
const YOUTUBE_VIDEOS = [
10+
{
11+
id: "VIDEO_001",
12+
embedId: "g-ynSAdL6Ak",
13+
title: "This tool cured my JavaScript fatigue",
14+
},
15+
{
16+
id: "VIDEO_002",
17+
embedId: "uHUgw-Hi8HE",
18+
title: "I tried React again after 2 years of Svelte",
19+
},
20+
];
21+
922
const TWEET_IDS = [
1023
"1930194170418999437",
1124
"1907728148294447538",
@@ -114,6 +127,45 @@ export default function Testimonials() {
114127
},
115128
};
116129

130+
const VideoCard = ({
131+
video,
132+
index,
133+
}: {
134+
video: (typeof YOUTUBE_VIDEOS)[0];
135+
index: number;
136+
}) => (
137+
<motion.div
138+
className="w-full min-w-0"
139+
initial={{ opacity: 0, y: 20, scale: 0.95 }}
140+
animate={{ opacity: 1, y: 0, scale: 1 }}
141+
transition={{
142+
delay: index * 0.1,
143+
duration: 0.4,
144+
ease: "easeOut",
145+
}}
146+
>
147+
<div className="w-full min-w-0 overflow-hidden rounded border border-border">
148+
<div className="sticky top-0 z-10 border-border border-b px-3 py-2">
149+
<div className="flex items-center gap-2">
150+
<Play className="h-3 w-3 text-primary" />
151+
<span className="font-semibold text-xs">[{video.id}]</span>
152+
</div>
153+
</div>
154+
<div className="w-full min-w-0 overflow-hidden">
155+
<div className="relative aspect-video w-full">
156+
<iframe
157+
src={`https://www.youtube.com/embed/${video.embedId}`}
158+
title={video.title}
159+
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
160+
allowFullScreen
161+
className="absolute inset-0 h-full w-full"
162+
/>
163+
</div>
164+
</div>
165+
</div>
166+
</motion.div>
167+
);
168+
117169
const TweetCard = ({
118170
tweetId,
119171
index,
@@ -135,7 +187,7 @@ export default function Testimonials() {
135187
<div className="sticky top-0 z-10 border-border border-b px-3 py-2">
136188
<div className="flex items-center gap-2">
137189
<span className="text-primary text-xs"></span>
138-
<span className=" font-semibold text-xs">
190+
<span className="font-semibold text-xs">
139191
[TWEET_{String(index + 1).padStart(3, "0")}]
140192
</span>
141193
</div>
@@ -153,6 +205,47 @@ export default function Testimonials() {
153205

154206
return (
155207
<div className="mb-12 w-full max-w-full overflow-hidden px-4">
208+
<div className="mb-8">
209+
<div className="mb-6 flex flex-wrap items-center justify-between gap-2 sm:flex-nowrap">
210+
<div className="flex items-center gap-2">
211+
<Play className="h-5 w-5 text-primary" />
212+
<span className="font-bold text-lg sm:text-xl">
213+
VIDEO_TESTIMONIALS.LOG
214+
</span>
215+
</div>
216+
<div className="hidden h-px flex-1 bg-border sm:block" />
217+
<span className="w-full text-right text-muted-foreground text-xs sm:w-auto sm:text-left">
218+
[{YOUTUBE_VIDEOS.length} ENTRIES]
219+
</span>
220+
</div>
221+
222+
<div className="block sm:hidden">
223+
<motion.div
224+
className="flex flex-col gap-4"
225+
variants={containerVariants}
226+
initial="hidden"
227+
animate="visible"
228+
>
229+
{YOUTUBE_VIDEOS.map((video, index) => (
230+
<VideoCard key={video.id} video={video} index={index} />
231+
))}
232+
</motion.div>
233+
</div>
234+
235+
<div className="hidden sm:block">
236+
<motion.div
237+
className="grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-3"
238+
variants={containerVariants}
239+
initial="hidden"
240+
animate="visible"
241+
>
242+
{YOUTUBE_VIDEOS.map((video, index) => (
243+
<VideoCard key={video.id} video={video} index={index} />
244+
))}
245+
</motion.div>
246+
</div>
247+
</div>
248+
156249
<div className="mb-6 flex flex-wrap items-center justify-between gap-2 sm:flex-nowrap">
157250
<div className="flex items-center gap-2">
158251
<Terminal className="h-5 w-5 text-primary" />

0 commit comments

Comments
 (0)