Update projects page, add animations

This commit is contained in:
2025-08-01 02:53:12 -04:00
parent c7ce82ec36
commit 08539b8a53
19 changed files with 2082 additions and 1401 deletions
+75 -57
View File
@@ -1,66 +1,84 @@
"use client"
"use client";
import { ArrowUpRight } from 'lucide-react';
import { ArrowUpRight, Newspaper } from "lucide-react";
import Link from "next/link";
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "~/components/ui/card";
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "~/components/ui/card";
import { CardSkeleton } from "~/components/ui/skeletons";
import { useState, useEffect } from 'react';
import { articles } from '~/lib/data';
import { useState, useEffect } from "react";
import { articles } from "~/lib/data";
export default function ArticlesPage() {
const [loading, setLoading] = useState(true);
const [loading, setLoading] = useState(true);
useEffect(() => {
const fetchArticles = async () => {
await new Promise(resolve => setTimeout(resolve, 0));
setLoading(false);
};
fetchArticles();
}, []);
useEffect(() => {
const fetchArticles = async () => {
await new Promise((resolve) => setTimeout(resolve, 0));
setLoading(false);
};
fetchArticles();
}, []);
return (
<div className="space-y-12">
<section className="space-y-6">
<div>
<h1 className="text-2xl font-bold">In the Media 📰</h1>
<p className="text-lg text-muted-foreground mt-2">
I have been lucky enough to have a few wonderful articles written about me and my work. Go check them out!
</p>
</div>
{loading ? (
<>
<CardSkeleton />
<CardSkeleton />
<CardSkeleton />
</>
) : (
articles.map((article, index) => (
<Card key={index}>
<CardHeader>
<div className="flex items-center justify-between">
<CardTitle>{article.title}</CardTitle>
<Link
href={article.link}
target="_blank"
rel="noopener noreferrer"
className="text-muted-foreground hover:text-primary"
>
<ArrowUpRight className="h-5 w-5" />
</Link>
</div>
<CardDescription className="text-sm text-muted-foreground">
Written by {article.author}, found in {article.source}.
</CardDescription>
</CardHeader>
<CardContent>
<p className="text-sm text-muted-foreground">
{article.description}
</p>
</CardContent>
</Card>
))
)}
</section>
return (
<div className="space-y-6">
<section className="animate-fade-in-up prose prose-zinc dark:prose-invert max-w-none">
<div className="flex items-start gap-3">
<Newspaper className="h-8 w-8 text-primary" />
<div>
<h1 className="mb-2 text-2xl font-bold">In the Media</h1>
</div>
</div>
);
<p className="mt-2 text-lg text-muted-foreground">
I have been lucky enough to have a few wonderful articles written
about me and my work. Go check them out!
</p>
</section>
<div className="animate-fade-in-up-delay-2 space-y-6">
{loading ? (
<>
<CardSkeleton />
<CardSkeleton />
<CardSkeleton />
</>
) : (
articles.map((article, index) => (
<div
key={index}
className={`animate-fade-in-up-delay-${Math.min(index + 3, 4)} card-hover`}
>
<Card>
<CardHeader>
<div className="flex items-center justify-between">
<CardTitle>{article.title}</CardTitle>
<Link
href={article.link}
target="_blank"
rel="noopener noreferrer"
className="text-muted-foreground hover:text-primary"
>
<ArrowUpRight className="h-5 w-5" />
</Link>
</div>
<CardDescription className="text-sm text-muted-foreground">
Written by {article.author}, found in {article.source}.
</CardDescription>
</CardHeader>
<CardContent>
<p className="text-sm text-muted-foreground">
{article.description}
</p>
</CardContent>
</Card>
</div>
))
)}
</div>
</div>
);
}
+35 -30
View File
@@ -10,6 +10,7 @@ import {
CardDescription,
} from "~/components/ui/card";
import { Button } from "~/components/ui/button";
import {
Download,
ExternalLink,
@@ -21,6 +22,8 @@ import {
ChevronRight,
AlertCircle,
Loader2,
Maximize2,
Minimize2,
Eye,
} from "lucide-react";
import Link from "next/link";
@@ -220,7 +223,7 @@ function PDFViewer({ url, title, type }: PDFViewerProps) {
const downloadBlob = () => {
if (!pdfBlob) return;
const blob = new Blob([pdfBlob], { type: "application/pdf" });
const blob = new Blob([pdfBlob as BlobPart], { type: "application/pdf" });
const url = window.URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
@@ -235,7 +238,7 @@ function PDFViewer({ url, title, type }: PDFViewerProps) {
return (
<div className="space-y-4">
{/* Action Bar */}
<div className="bg-muted/50 flex items-center justify-between gap-4 rounded-lg border p-4">
<div className="flex items-center justify-between gap-4 rounded-lg border bg-muted/50 p-4">
<div className="flex items-center gap-2">
<FileText className="h-5 w-5 text-muted-foreground" />
<div>
@@ -273,7 +276,7 @@ function PDFViewer({ url, title, type }: PDFViewerProps) {
return (
<div className="space-y-4">
{/* Action Bar */}
<div className="bg-muted/50 flex items-center justify-between gap-4 rounded-lg border p-4">
<div className="flex items-center justify-between gap-4 rounded-lg border bg-muted/50 p-4">
<div className="flex items-center gap-2">
<FileText className="h-5 w-5 text-muted-foreground" />
<h3 className="font-medium">{title}</h3>
@@ -348,7 +351,7 @@ function PDFViewer({ url, title, type }: PDFViewerProps) {
return (
<div className="space-y-4">
{/* Action Bar */}
<div className="bg-muted/50 flex items-center justify-between gap-4 rounded-lg border p-4">
<div className="flex items-center justify-between gap-4 rounded-lg border bg-muted/50 p-4">
<div className="flex items-center gap-2">
<FileText className="h-5 w-5 text-muted-foreground" />
<h3 className="font-medium">{title}</h3>
@@ -436,7 +439,7 @@ function PDFViewer({ url, title, type }: PDFViewerProps) {
>
{isLoading && (
<>
<div className="bg-background/80 absolute inset-0 z-10 flex items-center justify-center backdrop-blur-sm">
<div className="absolute inset-0 z-10 flex items-center justify-center bg-background/80 backdrop-blur-sm">
<div className="flex flex-col items-center gap-2">
<Loader2 className="h-6 w-6 animate-spin text-primary" />
<p className="text-sm text-muted-foreground">
@@ -464,7 +467,7 @@ function PDFViewer({ url, title, type }: PDFViewerProps) {
</div>
{/* Mobile Notice */}
<div className="block rounded-lg border border-amber-200 bg-amber-50 p-4 dark:border-amber-800 dark:bg-amber-950/50 md:hidden">
<div className="block rounded-lg border border-amber-200 bg-amber-50 p-4 md:hidden dark:border-amber-800 dark:bg-amber-950/50">
<div className="flex items-start gap-2">
<AlertCircle className="h-5 w-5 text-amber-600 dark:text-amber-400" />
<div>
@@ -487,7 +490,7 @@ export default function CVPage() {
return (
<div className="space-y-6">
<section className="prose prose-zinc dark:prose-invert max-w-none">
<section className="animate-fade-in-up prose prose-zinc dark:prose-invert max-w-none">
<div className="flex items-start gap-3">
<FileText className="h-8 w-8 text-primary" />
<div>
@@ -500,33 +503,35 @@ export default function CVPage() {
</p>
</section>
<Tabs
value={activeTab}
onValueChange={setActiveTab}
className="space-y-6"
>
<TabsList className="grid w-fit grid-cols-2">
<TabsTrigger value="cv" className="gap-2">
<FileText className="h-4 w-4" />
Academic CV
</TabsTrigger>
<TabsTrigger value="resume" className="gap-2">
<FileText className="h-4 w-4" />
Resume
</TabsTrigger>
</TabsList>
<div className="animate-fade-in-up-delay-2">
<Tabs
value={activeTab}
onValueChange={setActiveTab}
className="space-y-6"
>
<TabsList className="grid w-fit grid-cols-2">
<TabsTrigger value="cv" className="gap-2">
<FileText className="h-4 w-4" />
Academic CV
</TabsTrigger>
<TabsTrigger value="resume" className="gap-2">
<FileText className="h-4 w-4" />
Resume
</TabsTrigger>
</TabsList>
<TabsContent value="cv" className="space-y-0">
<PDFViewer url={CV_URL} title="Academic CV" type="cv" />
</TabsContent>
<TabsContent value="cv" className="space-y-0">
<PDFViewer url={CV_URL} title="Academic CV" type="cv" />
</TabsContent>
<TabsContent value="resume" className="space-y-0">
<PDFViewer url={RESUME_URL} title="Resume" type="resume" />
</TabsContent>
</Tabs>
<TabsContent value="resume" className="space-y-0">
<PDFViewer url={RESUME_URL} title="Resume" type="resume" />
</TabsContent>
</Tabs>
</div>
{/* Last Updated */}
<div className="text-center text-xs text-muted-foreground">
<div className="animate-fade-in-up-delay-4 text-center text-xs text-muted-foreground">
Last updated: {new Date().toLocaleDateString()}
</div>
</div>
+268 -234
View File
@@ -23,14 +23,16 @@ export default function HomePage() {
return (
<div className="space-y-12">
{/* Hero Section */}
<section className="space-y-6">
<section className="animate-fade-in-up space-y-6">
<div className="space-y-4">
<h1 className="text-3xl font-bold">Sean O&apos;Connor</h1>
<p className="text-xl text-muted-foreground">
<h1 className="animate-fade-in-up-delay-1 text-3xl font-bold">
Sean O&apos;Connor
</h1>
<p className="animate-fade-in-up-delay-2 text-xl text-muted-foreground">
Computer Science and Engineering student with experience in software
development, robotics research, and technical leadership.
</p>
<div className="flex flex-wrap gap-4 text-sm text-muted-foreground">
<div className="animate-fade-in-up-delay-3 flex flex-wrap gap-4 text-sm text-muted-foreground">
<div className="flex items-center gap-1">
<Mail className="h-4 w-4" />
<a href="mailto:sean@soconnor.dev" className="hover:text-primary">
@@ -46,7 +48,7 @@ export default function HomePage() {
Lewisburg, PA
</div>
</div>
<div className="flex gap-3">
<div className="animate-fade-in-up-delay-4 flex gap-3">
<Button asChild>
<Link href="/cv">
View CV
@@ -61,289 +63,321 @@ export default function HomePage() {
</section>
{/* Current Focus */}
<section className="space-y-6">
<section className="animate-fade-in-up space-y-6">
<h2 className="text-2xl font-bold">Current Focus</h2>
<div className="grid gap-6 md:grid-cols-2">
<Card>
<CardHeader className="pb-3">
<div className="flex items-center gap-2">
<FlaskConical className="h-5 w-5" />
<CardTitle className="mb-1">
Human-Robot Interaction Research
</CardTitle>
</div>
</CardHeader>
<CardContent className="pt-0">
<p className="text-muted-foreground">
Developing a web-based platform for HRI experiments that
addresses reproducibility challenges in Wizard-of-Oz studies.
Published at IEEE RO-MAN 2024 with second publication
forthcoming.
</p>
</CardContent>
</Card>
<div className="animate-fade-in-up-delay-1">
<Card className="card-hover">
<CardHeader className="pb-3">
<div className="flex items-center gap-2">
<FlaskConical className="h-5 w-5" />
<CardTitle className="mb-1">
Human-Robot Interaction Research
</CardTitle>
</div>
</CardHeader>
<CardContent className="pt-0">
<p className="text-muted-foreground">
Developing a web-based platform for HRI experiments that
addresses reproducibility challenges in Wizard-of-Oz studies.
Published at IEEE RO-MAN 2024 with second publication
forthcoming.
</p>
</CardContent>
</Card>
</div>
<Card>
<CardHeader className="pb-3">
<div className="flex items-center gap-2">
<GraduationCap className="h-5 w-5" />
<CardTitle className="mb-1">Academic Excellence</CardTitle>
</div>
</CardHeader>
<CardContent className="pt-0">
<p className="text-muted-foreground">
Bachelor of Science in Computer Science and Engineering at
Bucknell University. 3.86 Engineering GPA, Dean&apos;s List
multiple semesters. Expected graduation: May 2026.
</p>
</CardContent>
</Card>
<div className="animate-fade-in-up-delay-2">
<Card className="card-hover">
<CardHeader className="pb-3">
<div className="flex items-center gap-2">
<GraduationCap className="h-5 w-5" />
<CardTitle className="mb-1">Academic Excellence</CardTitle>
</div>
</CardHeader>
<CardContent className="pt-0">
<p className="text-muted-foreground">
Bachelor of Science in Computer Science and Engineering at
Bucknell University. 3.86 Engineering GPA, Dean&apos;s List
multiple semesters. Expected graduation: May 2026.
</p>
</CardContent>
</Card>
</div>
</div>
</section>
{/* Experience Highlights */}
<section className="space-y-6">
<section className="animate-fade-in-up space-y-6">
<h2 className="text-2xl font-bold">Experience Highlights</h2>
<div className="space-y-6">
<Card>
<CardHeader className="pb-3">
<div className="flex items-center justify-between">
<div>
<CardTitle className="mb-1">
Software Developer - Riverhead Raceway
</CardTitle>
<CardDescription>Oct 2020 Present</CardDescription>
<div className="animate-fade-in-up-delay-1">
<Card className="card-hover">
<CardHeader className="pb-3">
<div className="flex items-center justify-between">
<div>
<CardTitle className="mb-1">
Software Developer - Riverhead Raceway
</CardTitle>
<CardDescription>Oct 2020 Present</CardDescription>
</div>
<Building className="h-5 w-5 text-muted-foreground" />
</div>
<Building className="h-5 w-5 text-muted-foreground" />
</div>
</CardHeader>
<CardContent className="pt-0">
<p className="text-muted-foreground">
Transformed organizational culture by building trust in
data-driven decision making. Revolutionized fan engagement
through a real-time statistics platform serving 1500+ concurrent
users. Modernized entire technical infrastructure through
containerization and automated systems.
</p>
</CardContent>
</Card>
</CardHeader>
<CardContent className="pt-0">
<p className="text-muted-foreground">
Transformed organizational culture by building trust in
data-driven decision making. Revolutionized fan engagement
through a real-time statistics platform serving 1500+
concurrent users. Modernized entire technical infrastructure
through containerization and automated systems.
</p>
</CardContent>
</Card>
</div>
<Card>
<CardHeader className="pb-3">
<div className="flex items-center justify-between">
<div>
<CardTitle className="mb-1">
Computer Science Researcher - Bucknell University
</CardTitle>
<CardDescription>Jan 2023 Present</CardDescription>
<div className="animate-fade-in-up-delay-2">
<Card className="card-hover">
<CardHeader className="pb-3">
<div className="flex items-center justify-between">
<div>
<CardTitle className="mb-1">
Computer Science Researcher - Bucknell University
</CardTitle>
<CardDescription>Jan 2023 Present</CardDescription>
</div>
<FlaskConical className="h-5 w-5 text-muted-foreground" />
</div>
<FlaskConical className="h-5 w-5 text-muted-foreground" />
</div>
</CardHeader>
<CardContent className="pt-0">
<p className="text-muted-foreground">
Led research and authored first-author paper presented at
international conference. Built framework that enables
researchers to conduct experiments across different robot
platforms without specialized programming knowledge.
</p>
</CardContent>
</Card>
</CardHeader>
<CardContent className="pt-0">
<p className="text-muted-foreground">
Led research and authored first-author paper presented at
international conference. Built framework that enables
researchers to conduct experiments across different robot
platforms without specialized programming knowledge.
</p>
</CardContent>
</Card>
</div>
<Card>
<CardHeader className="pb-3">
<div className="flex items-center justify-between">
<div>
<CardTitle className="mb-1">
Teaching Assistant - Computer Science
</CardTitle>
<CardDescription>Jan 2024 Present</CardDescription>
<div className="animate-fade-in-up-delay-3">
<Card className="card-hover">
<CardHeader className="pb-3">
<div className="flex items-center justify-between">
<div>
<CardTitle className="mb-1">
Teaching Assistant - Computer Science
</CardTitle>
<CardDescription>Jan 2024 Present</CardDescription>
</div>
<Users className="h-5 w-5 text-muted-foreground" />
</div>
<Users className="h-5 w-5 text-muted-foreground" />
</div>
</CardHeader>
<CardContent className="pt-0">
<p className="text-muted-foreground">
Mentored 150+ students in software engineering principles,
connecting theoretical concepts to real-world applications.
Developed learning environments that embrace productive failure.
</p>
</CardContent>
</Card>
</CardHeader>
<CardContent className="pt-0">
<p className="text-muted-foreground">
Mentored 150+ students in software engineering principles,
connecting theoretical concepts to real-world applications.
Developed learning environments that embrace productive
failure.
</p>
</CardContent>
</Card>
</div>
</div>
</section>
{/* Technical Skills */}
<section className="space-y-6">
<section className="animate-fade-in-up space-y-6">
<h2 className="flex items-center gap-2 text-2xl font-bold">
<Code className="h-6 w-6" />
Technical Skills
</h2>
<div className="grid gap-6 md:grid-cols-2">
<Card>
<CardHeader className="pb-3">
<CardTitle className="mb-1">Languages & Frameworks</CardTitle>
</CardHeader>
<CardContent className="pt-0">
<p className="text-muted-foreground">
Java, C/C++, Python, JavaScript/TypeScript, React, Next.js, PHP,
SQL
</p>
</CardContent>
</Card>
<div className="animate-fade-in-up-delay-1">
<Card className="card-hover">
<CardHeader className="pb-3">
<CardTitle className="mb-1">Languages & Frameworks</CardTitle>
</CardHeader>
<CardContent className="pt-0">
<p className="text-muted-foreground">
Java, C/C++, Python, JavaScript/TypeScript, React, Next.js,
PHP, SQL
</p>
</CardContent>
</Card>
</div>
<Card>
<CardHeader className="pb-3">
<CardTitle className="mb-1">Backend & DevOps</CardTitle>
</CardHeader>
<CardContent className="pt-0">
<p className="text-muted-foreground">
REST APIs, MySQL, PostgreSQL, Docker, Apache Web Server, NGINX,
ROS2
</p>
</CardContent>
</Card>
<div className="animate-fade-in-up-delay-2">
<Card className="card-hover">
<CardHeader className="pb-3">
<CardTitle className="mb-1">Backend & DevOps</CardTitle>
</CardHeader>
<CardContent className="pt-0">
<p className="text-muted-foreground">
REST APIs, MySQL, PostgreSQL, Docker, Apache Web Server,
NGINX, ROS2
</p>
</CardContent>
</Card>
</div>
<Card>
<CardHeader className="pb-3">
<CardTitle className="mb-1">Cloud & Infrastructure</CardTitle>
</CardHeader>
<CardContent className="pt-0">
<p className="text-muted-foreground">
AWS, GCP, Azure, Backblaze, Linux (RHEL/Debian), CI/CD
</p>
</CardContent>
</Card>
<div className="animate-fade-in-up-delay-3">
<Card className="card-hover">
<CardHeader className="pb-3">
<CardTitle className="mb-1">Cloud & Infrastructure</CardTitle>
</CardHeader>
<CardContent className="pt-0">
<p className="text-muted-foreground">
AWS, GCP, Azure, Backblaze, Linux (RHEL/Debian), CI/CD
</p>
</CardContent>
</Card>
</div>
<Card>
<CardHeader className="pb-3">
<CardTitle className="mb-1">Development Tools</CardTitle>
</CardHeader>
<CardContent className="pt-0">
<p className="text-muted-foreground">
Git, JetBrains Suite, VS Code, Cursor, Linux CLI
</p>
</CardContent>
</Card>
<div className="animate-fade-in-up-delay-4">
<Card className="card-hover">
<CardHeader className="pb-3">
<CardTitle className="mb-1">Development Tools</CardTitle>
</CardHeader>
<CardContent className="pt-0">
<p className="text-muted-foreground">
Git, JetBrains Suite, VS Code, Cursor, Linux CLI
</p>
</CardContent>
</Card>
</div>
</div>
</section>
{/* Leadership & Activities */}
<section className="space-y-6">
<section className="animate-fade-in-up space-y-6">
<h2 className="flex items-center gap-2 text-2xl font-bold">
<Users className="h-6 w-6" />
Leadership & Activities
</h2>
<div className="grid gap-6 md:grid-cols-2">
<Card>
<CardHeader className="pb-3">
<CardTitle className="mb-1">
AIChE Chem-E-Car Competition Team
</CardTitle>
<CardDescription>
Former President, Electrical and Mechanical Team Lead
</CardDescription>
</CardHeader>
<CardContent className="pt-0">
<p className="text-muted-foreground">
Pioneered team&apos;s first custom hardware solution by
designing and fabricating a microcontroller-based control
system. Improved team dynamics by introducing agile development
principles and structured communication protocols.
</p>
</CardContent>
</Card>
<div className="animate-fade-in-up-delay-1">
<Card className="card-hover">
<CardHeader className="pb-3">
<CardTitle className="mb-1">
AIChE Chem-E-Car Competition Team
</CardTitle>
<CardDescription>
Former President, Electrical and Mechanical Team Lead
</CardDescription>
</CardHeader>
<CardContent className="pt-0">
<p className="text-muted-foreground">
Pioneered team&apos;s first custom hardware solution by
designing and fabricating a microcontroller-based control
system. Improved team dynamics by introducing agile
development principles and structured communication protocols.
</p>
</CardContent>
</Card>
</div>
<Card>
<CardHeader className="pb-3">
<CardTitle className="mb-1">Bucknell Coffee Society</CardTitle>
<CardDescription>Treasurer</CardDescription>
</CardHeader>
<CardContent className="pt-0">
<p className="text-muted-foreground">
Co-established and launched a new campus organization, managing
financial operations and coordinating event logistics.
</p>
</CardContent>
</Card>
<div className="animate-fade-in-up-delay-2">
<Card className="card-hover">
<CardHeader className="pb-3">
<CardTitle className="mb-1">Bucknell Coffee Society</CardTitle>
<CardDescription>Treasurer</CardDescription>
</CardHeader>
<CardContent className="pt-0">
<p className="text-muted-foreground">
Co-established and launched a new campus organization,
managing financial operations and coordinating event
logistics.
</p>
</CardContent>
</Card>
</div>
<Card>
<CardHeader className="pb-3">
<CardTitle className="mb-1">RoboLab@Bucknell</CardTitle>
<CardDescription>Founding Member</CardDescription>
</CardHeader>
<CardContent className="pt-0">
<p className="text-muted-foreground">
Led and participated in group discussions in a new lab bridging
computer science and psychology perspectives on human-robot
interaction, working with the complexities of human-robot trust,
job replacement, and autonomy.
</p>
</CardContent>
</Card>
<div className="animate-fade-in-up-delay-3">
<Card className="card-hover">
<CardHeader className="pb-3">
<CardTitle className="mb-1">RoboLab@Bucknell</CardTitle>
<CardDescription>Founding Member</CardDescription>
</CardHeader>
<CardContent className="pt-0">
<p className="text-muted-foreground">
Led and participated in group discussions in a new lab
bridging computer science and psychology perspectives on
human-robot interaction, working with the complexities of
human-robot trust, job replacement, and autonomy.
</p>
</CardContent>
</Card>
</div>
</div>
</section>
{/* Quick Links */}
<section className="space-y-6">
<section className="animate-fade-in-up space-y-6">
<h2 className="text-2xl font-bold">Explore More</h2>
<div className="grid gap-4 md:grid-cols-3">
<Card className="group cursor-pointer transition-colors hover:bg-accent">
<Link href="/publications" className="block p-4">
<CardContent className="p-0">
<div className="flex items-center justify-between">
<div className="flex items-center gap-3">
<FlaskConical className="h-6 w-6 text-primary" />
<div>
<div className="font-medium">Research Publications</div>
<div className="text-sm text-muted-foreground">
IEEE conferences and ongoing research
<div className="animate-fade-in-up-delay-1">
<Card className="card-hover group cursor-pointer transition-colors hover:bg-accent">
<Link href="/publications" className="block p-4">
<CardContent className="p-0">
<div className="flex items-center justify-between">
<div className="flex items-center gap-3">
<FlaskConical className="h-6 w-6 text-primary" />
<div>
<div className="font-medium">Research Publications</div>
<div className="text-sm text-muted-foreground">
IEEE conferences and ongoing research
</div>
</div>
</div>
<ArrowUpRight className="h-4 w-4 text-muted-foreground group-hover:text-primary" />
</div>
<ArrowUpRight className="h-4 w-4 text-muted-foreground group-hover:text-primary" />
</div>
</CardContent>
</Link>
</Card>
</CardContent>
</Link>
</Card>
</div>
<Card className="group cursor-pointer transition-colors hover:bg-accent">
<Link href="/projects" className="block p-4">
<CardContent className="p-0">
<div className="flex items-center justify-between">
<div className="flex items-center gap-3">
<Code className="h-6 w-6 text-primary" />
<div>
<div className="font-medium">Projects</div>
<div className="text-sm text-muted-foreground">
Software development and research work
<div className="animate-fade-in-up-delay-2">
<Card className="card-hover group cursor-pointer transition-colors hover:bg-accent">
<Link href="/projects" className="block p-4">
<CardContent className="p-0">
<div className="flex items-center justify-between">
<div className="flex items-center gap-3">
<Code className="h-6 w-6 text-primary" />
<div>
<div className="font-medium">Projects</div>
<div className="text-sm text-muted-foreground">
Software development and research work
</div>
</div>
</div>
<ArrowUpRight className="h-4 w-4 text-muted-foreground group-hover:text-primary" />
</div>
<ArrowUpRight className="h-4 w-4 text-muted-foreground group-hover:text-primary" />
</div>
</CardContent>
</Link>
</Card>
</CardContent>
</Link>
</Card>
</div>
<Card className="group cursor-pointer transition-colors hover:bg-accent">
<Link href="/cv" className="block p-4">
<CardContent className="p-0">
<div className="flex items-center justify-between">
<div className="flex items-center gap-3">
<GraduationCap className="h-6 w-6 text-primary" />
<div>
<div className="font-medium">Curriculum Vitae</div>
<div className="text-sm text-muted-foreground">
Complete academic and professional record
<div className="animate-fade-in-up-delay-3">
<Card className="card-hover group cursor-pointer transition-colors hover:bg-accent">
<Link href="/cv" className="block p-4">
<CardContent className="p-0">
<div className="flex items-center justify-between">
<div className="flex items-center gap-3">
<GraduationCap className="h-6 w-6 text-primary" />
<div>
<div className="font-medium">Curriculum Vitae</div>
<div className="text-sm text-muted-foreground">
Complete academic and professional record
</div>
</div>
</div>
<ArrowUpRight className="h-4 w-4 text-muted-foreground group-hover:text-primary" />
</div>
<ArrowUpRight className="h-4 w-4 text-muted-foreground group-hover:text-primary" />
</div>
</CardContent>
</Link>
</Card>
</CardContent>
</Link>
</Card>
</div>
</div>
</section>
</div>
+399 -196
View File
@@ -1,6 +1,20 @@
import { Metadata } from "next";
import { projects } from "~/lib/data";
import { Badge } from "~/components/ui/badge";
import { Card, CardContent, CardHeader, CardTitle } from "~/components/ui/card";
import { Button } from "~/components/ui/button";
import {
Eye,
Keyboard,
Palette,
Volume2,
Globe,
CheckCircle,
AlertTriangle,
ExternalLink,
Accessibility,
} from "lucide-react";
import Link from "next/link";
export const metadata: Metadata = {
title: "Accessibility Features",
@@ -9,223 +23,412 @@ export const metadata: Metadata = {
};
export default function AccessibilityPage() {
// Find the Accessibility project data
const project = projects.find((p) => p.title === "Accessibility Features");
return (
<div className="container pb-6 pt-0">
<div className="space-y-6">
<div>
<h1 className="mb-4 text-4xl font-bold tracking-tight">
{project?.title}
</h1>
<p className="text-lg text-muted-foreground">
{project?.longDescription}
</p>
<div className="mt-4 flex flex-wrap gap-2">
{project?.tags.map((tag) => (
<Badge key={tag} variant="secondary">
{tag}
</Badge>
))}
<div className="space-y-8">
<section className="animate-fade-in-up prose prose-zinc dark:prose-invert max-w-none">
<div className="flex items-start gap-3">
<Accessibility className="h-8 w-8 text-primary" />
<div>
<h1 className="mb-2 text-2xl font-bold">{project?.title}</h1>
</div>
</div>
<p className="mt-2 text-lg text-muted-foreground">
Building an inclusive web experience for all users
</p>
<div className="mt-8 space-y-8">
<section>
<h2 className="text-2xl font-bold tracking-tight">
Why Accessibility Matters
</h2>
<p className="mt-4">
<div className="mt-4 flex flex-wrap gap-2">
{project?.tags.map((tag) => (
<Badge key={tag} variant="secondary">
{tag}
</Badge>
))}
</div>
</section>
{/* Why Accessibility Matters */}
<section className="animate-fade-in-up-delay-2 space-y-4">
<h2 className="text-2xl font-bold">Why Accessibility Matters</h2>
<Card>
<CardContent className="space-y-4 p-6">
<p className="text-muted-foreground">
As a portfolio website aimed at showcasing my technical skills and
projects to potential employers and collaborators, ensuring
accessibility is not just a legal or ethical requirement
it&apos;s a demonstration of my professional competence and
inclusive design thinking. Here&apos;s why accessibility is
particularly important for my website:
inclusive design thinking.
</p>
<ul className="mt-4 list-disc space-y-2 pl-6">
<li>
<strong>Universal Access:</strong> A portfolio should be
accessible to all potential viewers, including hiring managers,
colleagues, and collaborators who may have disabilities or
situational limitations.
</li>
<li>
<strong>Professional Credibility:</strong> As a computer science
and engineering student, demonstrating knowledge of
accessibility standards reflects technical competence and
attention to detail that employers value.
</li>
<li>
<strong>Technical Showcase:</strong> Implementing accessibility
features serves as a practical example of the technical skills
being presented.
</li>
<li>
<strong>Academic Integrity:</strong> In an academic context,
ensuring that educational resources (like the LaTeX tutorial)
are accessible to all students reflects a commitment to
educational equity.
</li>
</ul>
</section>
<section>
<h2 className="text-2xl font-bold tracking-tight">
Accessibility Features Implemented
</h2>
<div className="mt-4 space-y-6">
<div className="grid gap-4 md:grid-cols-2">
<div>
<h3 className="text-xl font-semibold">
1. Comprehensive Image Alt Text
</h3>
<p className="mt-2">
Every image on my website has been carefully evaluated and
provided with appropriate alt text. I distinguish between:
</p>
<ul className="my-2 list-disc pl-6">
<li>
<strong>Decorative images:</strong> Images that are purely
decorative, such as the LaTeX tutorial thumbnail, have alt
text that indicates their decorative nature (e.g.,
&quot;Decorative thumbnail showing LaTeX code and formatting
example&quot;).
</li>
<li>
<strong>Informative images:</strong> Images that convey
information, such as project screenshots, have detailed alt
text describing their content (e.g., &quot;Screenshot of
HRIStudio application showing the robot control dashboard on
a laptop&quot;).
</li>
<h3 className="mb-2 font-semibold">Professional Impact</h3>
<ul className="space-y-1 text-sm text-muted-foreground">
<li> Demonstrates technical competence</li>
<li> Shows attention to detail</li>
<li> Reflects inclusive design thinking</li>
<li> Increases potential audience reach</li>
</ul>
<strong>Implementation details:</strong> I implemented this by
adding custom &quot;imageAlt&quot; properties to all projects
and &quot;alts&quot; arrays for travel items in my data
structure, ensuring consistent image descriptions throughout the
site.
</div>
<div>
<h3 className="text-xl font-semibold">
2. Accessible Video Player
</h3>
<p className="mt-2">
I developed a custom video player for the LaTeX tutorial with
several accessibility features:
</p>
<ul className="my-2 list-disc pl-6">
<li>Closed captions that can be toggled on/off</li>
<li>
Full transcript available with expandable details element
</li>
<li>
Keyboard-accessible controls for play/pause, volume, and
caption toggling
</li>
<li>
ARIA labels for all controls to improve screen reader
compatibility
</li>
<li>Video poster image with appropriate alt text</li>
<h3 className="mb-2 font-semibold">Academic Responsibility</h3>
<ul className="space-y-1 text-sm text-muted-foreground">
<li> Ensures universal access to content</li>
<li> Supports educational equity</li>
<li> Serves as practical skills example</li>
<li> Demonstrates social awareness</li>
</ul>
<strong>Implementation details:</strong> I created a custom
AccessibleVideo component that wraps the HTML5 video element
with additional accessibility features, including captions
integration, keyboard navigation, and proper ARIA attributes.
</div>
<div>
<h3 className="text-xl font-semibold">
3. Semantic HTML and ARIA
</h3>
<p className="mt-2">
My website uses semantic HTML throughout to ensure proper
structure and meaning:
</p>
<ul className="my-2 list-disc pl-6">
<li>
Proper heading hierarchy (h1, h2, h3) for logical document
structure
</li>
<li>
Semantic elements like &lt;nav&gt;, &lt;main&gt;,
&lt;section&gt;, and &lt;article&gt;
</li>
<li>
ARIA attributes for components without native semantics
</li>
<li>Skip-to-content links for keyboard users</li>
</ul>
<strong>Implementation details:</strong> I audited all pages for
proper heading structure and added semantic HTML elements. For
custom components like cards and badges, I ensured proper ARIA
attributes were used to convey their purpose and state.
</div>
<div>
<h3 className="text-xl font-semibold">
4. Color Contrast and Dark Mode
</h3>
<p className="mt-2">
I carefully selected color choices on my website to ensure
readability:
</p>
<ul className="my-2 list-disc pl-6">
<li>
All text meets WCAG AA contrast requirements (4.5:1 for
normal text, 3:1 for large text)
</li>
<li>
Dark mode support for users who prefer or require reduced
brightness
</li>
<li>
Color is never used as the sole means of conveying
information
</li>
<li>
Interactive elements have visual focus indicators that
don&apos;t rely solely on color
</li>
</ul>
<strong>Implementation details:</strong> I used the TailwindCSS
color palette with careful consideration of contrast ratios. The
dark mode implementation respects user system preferences and
provides consistent contrast ratios in both modes.
</div>
</div>
</section>
</CardContent>
</Card>
</section>
<section>
<h2 className="text-2xl font-bold tracking-tight">
Platform Limitations and Workarounds
</h2>
<p className="mt-4">
While building this website, I encountered some limitations in the
Next.js framework and implemented workarounds:
</p>
<ul className="mt-4 list-disc space-y-2 pl-6">
<li>
<strong>Next.js Client/Server Components:</strong> Next.js
divides components into client and server components, which can
create hydration issues when implementing certain interactive
accessibility features. I addressed this by creating client-side
wrapper components for features requiring interactivity, such as
the video player and navigation menu.
</li>
<li>
<strong>PDF Accessibility:</strong> The CV section uses PDF
rendering which has inherent accessibility limitations. As a
workaround, I provide the same information in HTML format
elsewhere on the site, ensuring that users who cannot access the
PDF can still view the content.
</li>
</ul>
</section>
{/* Accessibility Features */}
<section className="animate-fade-in-up-delay-3 space-y-6">
<h2 className="text-2xl font-bold">Implemented Features</h2>
<div className="grid gap-6 md:grid-cols-2">
{/* Image Alt Text */}
<div className="animate-fade-in-up-delay-4 card-hover">
<Card>
<CardHeader className="pb-3">
<CardTitle className="flex items-center gap-2">
<Eye className="h-5 w-5" />
Image Accessibility
</CardTitle>
</CardHeader>
<CardContent className="pt-0">
<p className="text-sm text-muted-foreground">
All images include descriptive alt text for screen readers
</p>
</CardContent>
</Card>
</div>
<div className="animate-fade-in-up-delay-4 card-hover">
<Card>
<CardHeader className="pb-3">
<CardTitle className="flex items-center gap-2">
<Keyboard className="h-5 w-5" />
Keyboard Navigation
</CardTitle>
</CardHeader>
<CardContent className="pt-0">
<p className="text-sm text-muted-foreground">
Full keyboard support for all interactive elements
</p>
</CardContent>
</Card>
</div>
</div>
</div>
</section>
{/* Additional Features */}
<section className="space-y-6">
<h2 className="text-2xl font-bold">Additional Features</h2>
<div className="grid gap-6 md:grid-cols-2">
{/* Video Accessibility */}
<Card>
<CardHeader className="pb-3">
<CardTitle className="flex items-center gap-2">
<Volume2 className="h-5 w-5" />
Video Accessibility
</CardTitle>
</CardHeader>
<CardContent className="space-y-3 pt-0">
<p className="text-sm text-muted-foreground">
Custom video player with comprehensive accessibility features.
</p>
<div className="space-y-2">
<div className="flex items-start gap-2">
<CheckCircle className="mt-0.5 h-4 w-4 flex-shrink-0 text-green-600" />
<div className="text-sm">Closed captions with toggle</div>
</div>
<div className="flex items-start gap-2">
<CheckCircle className="mt-0.5 h-4 w-4 flex-shrink-0 text-green-600" />
<div className="text-sm">Full transcript available</div>
</div>
<div className="flex items-start gap-2">
<CheckCircle className="mt-0.5 h-4 w-4 flex-shrink-0 text-green-600" />
<div className="text-sm">Keyboard-accessible controls</div>
</div>
</div>
</CardContent>
</Card>
{/* Semantic HTML */}
<Card>
<CardHeader className="pb-3">
<CardTitle className="flex items-center gap-2">
<Globe className="h-5 w-5" />
Semantic Structure
</CardTitle>
</CardHeader>
<CardContent className="space-y-3 pt-0">
<p className="text-sm text-muted-foreground">
Proper HTML structure and ARIA attributes for screen readers.
</p>
<div className="space-y-2">
<div className="flex items-start gap-2">
<CheckCircle className="mt-0.5 h-4 w-4 flex-shrink-0 text-green-600" />
<div className="text-sm">Logical heading hierarchy</div>
</div>
<div className="flex items-start gap-2">
<CheckCircle className="mt-0.5 h-4 w-4 flex-shrink-0 text-green-600" />
<div className="text-sm">Semantic HTML elements</div>
</div>
<div className="flex items-start gap-2">
<CheckCircle className="mt-0.5 h-4 w-4 flex-shrink-0 text-green-600" />
<div className="text-sm">ARIA labels and attributes</div>
</div>
</div>
</CardContent>
</Card>
{/* Color and Contrast */}
<Card>
<CardHeader className="pb-3">
<CardTitle className="flex items-center gap-2">
<Palette className="h-5 w-5" />
Color & Contrast
</CardTitle>
</CardHeader>
<CardContent className="space-y-3 pt-0">
<p className="text-sm text-muted-foreground">
Carefully selected colors ensuring readability for all users.
</p>
<div className="space-y-2">
<div className="flex items-start gap-2">
<CheckCircle className="mt-0.5 h-4 w-4 flex-shrink-0 text-green-600" />
<div className="text-sm">WCAG AA contrast compliance</div>
</div>
<div className="flex items-start gap-2">
<CheckCircle className="mt-0.5 h-4 w-4 flex-shrink-0 text-green-600" />
<div className="text-sm">System dark mode support</div>
</div>
<div className="flex items-start gap-2">
<CheckCircle className="mt-0.5 h-4 w-4 flex-shrink-0 text-green-600" />
<div className="text-sm">Color-independent information</div>
</div>
</div>
</CardContent>
</Card>
</div>
</section>
{/* Implementation Details */}
<section className="space-y-4">
<h2 className="text-2xl font-bold">Technical Implementation</h2>
<div className="space-y-4">
<Card>
<CardHeader className="pb-3">
<CardTitle>Custom Components</CardTitle>
</CardHeader>
<CardContent className="pt-0">
<p className="mb-3 text-sm text-muted-foreground">
Built custom accessible components to ensure consistent user
experience across the site.
</p>
<div className="grid gap-3 md:grid-cols-2">
<div className="rounded border p-3">
<strong className="text-sm">AccessibleVideo</strong>
<p className="mt-1 text-xs text-muted-foreground">
Video player with captions, transcript, and keyboard
navigation
</p>
</div>
<div className="rounded border p-3">
<strong className="text-sm">Image Alt System</strong>
<p className="mt-1 text-xs text-muted-foreground">
Structured data with imageAlt properties for all project
images
</p>
</div>
</div>
</CardContent>
</Card>
<Card>
<CardHeader className="pb-3">
<CardTitle>Standards Compliance</CardTitle>
</CardHeader>
<CardContent className="pt-0">
<div className="grid gap-4 md:grid-cols-3">
<div className="text-center">
<div className="mx-auto mb-2 flex h-12 w-12 items-center justify-center bg-green-100 text-green-700 dark:bg-green-900 dark:text-green-300">
<CheckCircle className="h-6 w-6" />
</div>
<div className="font-medium">WCAG 2.1</div>
<div className="text-xs text-muted-foreground">
Level AA compliance
</div>
</div>
<div className="text-center">
<div className="mx-auto mb-2 flex h-12 w-12 items-center justify-center bg-green-100 text-green-700 dark:bg-green-900 dark:text-green-300">
<Keyboard className="h-6 w-6" />
</div>
<div className="font-medium">Keyboard Navigation</div>
<div className="text-xs text-muted-foreground">
Full keyboard support
</div>
</div>
<div className="text-center">
<div className="mx-auto mb-2 flex h-12 w-12 items-center justify-center bg-green-100 text-green-700 dark:bg-green-900 dark:text-green-300">
<Volume2 className="h-6 w-6" />
</div>
<div className="font-medium">Screen Readers</div>
<div className="text-xs text-muted-foreground">
Optimized for assistive tech
</div>
</div>
</div>
</CardContent>
</Card>
</div>
</section>
{/* Challenges and Limitations */}
<section className="space-y-4">
<h2 className="text-2xl font-bold">Challenges & Solutions</h2>
<Card>
<CardContent className="space-y-4 p-6">
<div className="space-y-4">
<div className="flex items-start gap-3">
<AlertTriangle className="mt-0.5 h-5 w-5 flex-shrink-0 text-amber-500" />
<div>
<h3 className="font-semibold">Next.js Hydration Issues</h3>
<p className="mt-1 text-sm text-muted-foreground">
Client/server component division created challenges for
interactive accessibility features.
</p>
<p className="mt-2 text-sm">
<strong>Solution:</strong> Created client-side wrapper
components for interactive features while maintaining SSR
benefits.
</p>
</div>
</div>
<div className="flex items-start gap-3">
<AlertTriangle className="mt-0.5 h-5 w-5 flex-shrink-0 text-amber-500" />
<div>
<h3 className="font-semibold">
PDF Accessibility Limitations
</h3>
<p className="mt-1 text-sm text-muted-foreground">
PDF rendering has inherent accessibility challenges for
screen readers.
</p>
<p className="mt-2 text-sm">
<strong>Solution:</strong> Provided equivalent information
in HTML format throughout the site for users who cannot
access PDFs.
</p>
</div>
</div>
</div>
</CardContent>
</Card>
</section>
{/* Resources */}
<section className="space-y-4">
<h2 className="text-2xl font-bold">Accessibility Resources</h2>
<div className="grid gap-4 md:grid-cols-2">
<Card className="group cursor-pointer transition-colors hover:bg-accent">
<Link
href="https://www.w3.org/WAI/WCAG21/quickref/"
target="_blank"
rel="noopener noreferrer"
className="block p-4"
>
<CardContent className="p-0">
<div className="flex items-center justify-between">
<div className="flex items-center gap-3">
<CheckCircle className="h-6 w-6 text-primary" />
<div>
<div className="font-medium">WCAG Quick Reference</div>
<div className="text-sm text-muted-foreground">
Official guidelines and techniques
</div>
</div>
</div>
<ExternalLink className="h-4 w-4 text-muted-foreground group-hover:text-primary" />
</div>
</CardContent>
</Link>
</Card>
<Card className="group cursor-pointer transition-colors hover:bg-accent">
<Link
href="https://webaim.org/"
target="_blank"
rel="noopener noreferrer"
className="block p-4"
>
<CardContent className="p-0">
<div className="flex items-center justify-between">
<div className="flex items-center gap-3">
<Eye className="h-6 w-6 text-primary" />
<div>
<div className="font-medium">WebAIM</div>
<div className="text-sm text-muted-foreground">
Accessibility testing and resources
</div>
</div>
</div>
<ExternalLink className="h-4 w-4 text-muted-foreground group-hover:text-primary" />
</div>
</CardContent>
</Link>
</Card>
</div>
</section>
{/* Continuous Improvement */}
<section className="space-y-4">
<h2 className="text-2xl font-bold">Ongoing Commitment</h2>
<Card>
<CardContent className="p-6">
<div className="space-y-4">
<p className="text-muted-foreground">
Accessibility is not a one-time implementation but an ongoing
commitment to inclusive design. This site continues to evolve
with user feedback and updated standards.
</p>
<div className="grid gap-4 md:grid-cols-2">
<div>
<h3 className="mb-2 font-semibold">Regular Testing</h3>
<ul className="space-y-1 text-sm text-muted-foreground">
<li> Screen reader compatibility testing</li>
<li> Keyboard navigation verification</li>
<li> Color contrast validation</li>
</ul>
</div>
<div>
<h3 className="mb-2 font-semibold">Future Enhancements</h3>
<ul className="space-y-1 text-sm text-muted-foreground">
<li> User preference settings</li>
<li> Enhanced keyboard shortcuts</li>
<li> Voice navigation support</li>
</ul>
</div>
</div>
<div className="pt-4">
<Button asChild variant="outline">
<Link href="/projects">
<Eye className="mr-2 h-4 w-4" />
View All Projects
</Link>
</Button>
</div>
</div>
</CardContent>
</Card>
</section>
</div>
);
}
+259 -46
View File
@@ -1,11 +1,16 @@
import { Metadata } from "next";
import { projects } from "~/lib/data";
import { Badge } from "~/components/ui/badge";
import { Card, CardContent, CardHeader, CardTitle } from "~/components/ui/card";
import { Button } from "~/components/ui/button";
import { AccessibleVideo } from "~/components/AccessibleVideo";
import { ExternalLink, Play, BookOpen, BookOpenText, Code } from "lucide-react";
import Link from "next/link";
export const metadata: Metadata = {
title: "LaTeX Introduction Tutorial",
description: "A comprehensive 5-minute introduction to LaTeX document preparation system for beginners.",
description:
"A comprehensive 5-minute introduction to LaTeX document preparation system for beginners.",
};
const transcript = `
@@ -24,58 +29,266 @@ const transcript = `
`;
export default function LatexTutorialPage() {
// Find the LaTeX project data
const project = projects.find((p) => p.title === "LaTeX Introduction Tutorial");
const project = projects.find(
(p) => p.title === "LaTeX Introduction Tutorial",
);
return (
<div className="container pt-0 pb-6">
<div className="space-y-6">
<div>
<h1 className="text-4xl font-bold tracking-tight mb-4">{project?.title}</h1>
<p className="text-lg text-muted-foreground">{project?.longDescription}</p>
<div className="mt-4 flex flex-wrap gap-2">
{project?.tags.map((tag) => (
<Badge key={tag} variant="secondary">
{tag}
</Badge>
))}
<div className="space-y-8">
<section className="animate-fade-in-up prose prose-zinc dark:prose-invert max-w-none">
<div className="flex items-start gap-3">
<BookOpenText className="h-8 w-8 text-primary" />
<div>
<h1 className="mb-2 text-2xl font-bold">{project?.title}</h1>
</div>
</div>
<p className="mt-2 text-lg text-muted-foreground">
A beginner-friendly introduction to LaTeX document preparation system
</p>
<div className="mt-8">
<AccessibleVideo
src="/videos/latex-intro.mp4"
poster="/latex-thumbnail.jpg"
captionSrc="/videos/latex-intro.vtt"
title="LaTeX Introduction Tutorial"
description="A 5-minute introduction to LaTeX, covering basic syntax, document structure, and common use cases."
transcript={transcript}
posterAlt="Decorative thumbnail showing LaTeX code and formatting example"
/>
<div className="mt-4 flex flex-wrap gap-2">
{project?.tags.map((tag) => (
<Badge key={tag} variant="secondary">
{tag}
</Badge>
))}
</div>
</section>
<div className="mt-8 space-y-6">
<h2 className="text-2xl font-bold tracking-tight">Why Learn LaTeX?</h2>
<p>LaTeX is a document preparation system widely used in academia, especially in fields like mathematics, computer science, physics, and engineering. It excels at:</p>
<ul className="list-disc pl-6 space-y-2">
<li>Professional typesetting of mathematical equations</li>
<li>Consistent document formatting</li>
<li>Automated handling of citations and references</li>
<li>Version control compatibility</li>
<li>Cross-platform document creation</li>
</ul>
<p>This tutorial provides a gentle introduction to get you started with your first LaTeX document.</p>
<h2 className="text-2xl font-bold tracking-tight">Key Resources</h2>
<ul className="list-disc pl-6 space-y-2">
<li><a href="https://www.overleaf.com" className="text-primary hover:underline" target="_blank" rel="noopener noreferrer">Overleaf</a> - A popular online LaTeX editor</li>
<li><a href="https://www.latex-project.org/get/" className="text-primary hover:underline" target="_blank" rel="noopener noreferrer">LaTeX Project</a> - Official downloads for local installation</li>
<li><a href="https://en.wikibooks.org/wiki/LaTeX" className="text-primary hover:underline" target="_blank" rel="noopener noreferrer">LaTeX Wikibook</a> - Comprehensive reference guide</li>
</ul>
{/* Video Section */}
<section className="animate-fade-in-up-delay-2 space-y-4">
<h2 className="text-2xl font-bold">Tutorial Video</h2>
<Card>
<CardContent className="p-6">
<AccessibleVideo
src="/videos/latex-intro.mp4"
poster="/latex-thumbnail.jpg"
captionSrc="/videos/latex-intro.vtt"
title="LaTeX Introduction Tutorial"
description="A 5-minute introduction to LaTeX, covering basic syntax, document structure, and common use cases."
transcript={transcript}
posterAlt="Decorative thumbnail showing LaTeX code and formatting example"
/>
</CardContent>
</Card>
</section>
{/* What You'll Learn */}
<section className="animate-fade-in-up-delay-3 space-y-4">
<h2 className="text-2xl font-bold">What You&apos;ll Learn</h2>
<div className="grid gap-4 md:grid-cols-2">
<div className="animate-fade-in-up-delay-4 card-hover">
<Card>
<CardHeader className="pb-3">
<CardTitle className="flex items-center gap-2">
<Play className="h-5 w-5" />
Getting Started
</CardTitle>
</CardHeader>
<CardContent className="pt-0">
<ul className="space-y-1 text-sm text-muted-foreground">
<li> Setting up Overleaf</li>
<li> Basic document structure</li>
<li> Common formatting commands</li>
<li> Creating your first document</li>
</ul>
</CardContent>
</Card>
</div>
<div className="animate-fade-in-up-delay-4 card-hover">
<Card>
<CardHeader className="pb-3">
<CardTitle className="flex items-center gap-2">
<Code className="h-5 w-5" />
Core Features
</CardTitle>
</CardHeader>
<CardContent className="pt-0">
<ul className="space-y-1 text-sm text-muted-foreground">
<li> Mathematical equations</li>
<li> Document organization</li>
<li> Bibliography management</li>
<li> Professional typesetting</li>
</ul>
</CardContent>
</Card>
</div>
</div>
</div>
</section>
{/* Why LaTeX */}
<section className="animate-fade-in-up-delay-4 space-y-4">
<h2 className="text-2xl font-bold">Why LaTeX?</h2>
<Card>
<CardContent className="space-y-4 p-6">
<p className="text-muted-foreground">
LaTeX is the gold standard for academic and technical document
preparation, especially in mathematics, computer science, physics,
and engineering fields.
</p>
<div className="grid gap-4 md:grid-cols-2">
<div>
<h3 className="mb-2 font-semibold">Academic Excellence</h3>
<ul className="space-y-1 text-sm text-muted-foreground">
<li> Professional mathematical typesetting</li>
<li> Consistent document formatting</li>
<li> Automated citations and references</li>
</ul>
</div>
<div>
<h3 className="mb-2 font-semibold">Technical Benefits</h3>
<ul className="space-y-1 text-sm text-muted-foreground">
<li> Version control friendly</li>
<li> Cross-platform compatibility</li>
<li> Separation of content and design</li>
</ul>
</div>
</div>
</CardContent>
</Card>
</section>
{/* Resources */}
<section className="animate-fade-in-up-delay-4 space-y-4">
<h2 className="text-2xl font-bold">Essential Resources</h2>
<div className="grid gap-4 md:grid-cols-3">
<Card className="group cursor-pointer transition-colors hover:bg-accent">
<Link
href="https://www.overleaf.com"
target="_blank"
rel="noopener noreferrer"
className="block p-4"
>
<CardContent className="p-0">
<div className="flex items-center justify-between">
<div className="flex items-center gap-3">
<Play className="h-6 w-6 text-primary" />
<div>
<div className="font-medium">Overleaf</div>
<div className="text-sm text-muted-foreground">
Online LaTeX editor
</div>
</div>
</div>
<ExternalLink className="h-4 w-4 text-muted-foreground group-hover:text-primary" />
</div>
</CardContent>
</Link>
</Card>
<Card className="group cursor-pointer transition-colors hover:bg-accent">
<Link
href="https://www.latex-project.org/get/"
target="_blank"
rel="noopener noreferrer"
className="block p-4"
>
<CardContent className="p-0">
<div className="flex items-center justify-between">
<div className="flex items-center gap-3">
<Code className="h-6 w-6 text-primary" />
<div>
<div className="font-medium">LaTeX Project</div>
<div className="text-sm text-muted-foreground">
Official downloads
</div>
</div>
</div>
<ExternalLink className="h-4 w-4 text-muted-foreground group-hover:text-primary" />
</div>
</CardContent>
</Link>
</Card>
<Card className="group cursor-pointer transition-colors hover:bg-accent">
<Link
href="https://en.wikibooks.org/wiki/LaTeX"
target="_blank"
rel="noopener noreferrer"
className="block p-4"
>
<CardContent className="p-0">
<div className="flex items-center justify-between">
<div className="flex items-center gap-3">
<BookOpen className="h-6 w-6 text-primary" />
<div>
<div className="font-medium">LaTeX Wikibook</div>
<div className="text-sm text-muted-foreground">
Comprehensive guide
</div>
</div>
</div>
<ExternalLink className="h-4 w-4 text-muted-foreground group-hover:text-primary" />
</div>
</CardContent>
</Link>
</Card>
</div>
</section>
{/* Next Steps */}
<section className="animate-fade-in-up-delay-4 space-y-4">
<h2 className="text-2xl font-bold">Next Steps</h2>
<Card>
<CardContent className="p-6">
<div className="space-y-4">
<p className="text-muted-foreground">
Ready to start your LaTeX journey? Here&apos;s what to do next:
</p>
<div className="space-y-3">
<div className="flex items-center gap-3">
<div className="flex h-6 w-6 items-center justify-center bg-primary text-xs font-bold text-primary-foreground">
1
</div>
<span className="text-sm">
Create a free account on Overleaf
</span>
</div>
<div className="flex items-center gap-3">
<div className="flex h-6 w-6 items-center justify-center bg-primary text-xs font-bold text-primary-foreground">
2
</div>
<span className="text-sm">
Watch the tutorial video above
</span>
</div>
<div className="flex items-center gap-3">
<div className="flex h-6 w-6 items-center justify-center bg-primary text-xs font-bold text-primary-foreground">
3
</div>
<span className="text-sm">
Practice with a simple document
</span>
</div>
<div className="flex items-center gap-3">
<div className="flex h-6 w-6 items-center justify-center bg-primary text-xs font-bold text-primary-foreground">
4
</div>
<span className="text-sm">
Explore the LaTeX Wikibook for advanced features
</span>
</div>
</div>
<div className="pt-4">
<Button asChild>
<Link
href="https://www.overleaf.com"
target="_blank"
rel="noopener noreferrer"
>
<ExternalLink className="mr-2 h-4 w-4" />
Get Started with Overleaf
</Link>
</Button>
</div>
</div>
</CardContent>
</Card>
</section>
</div>
);
}
}
+152 -207
View File
@@ -11,7 +11,7 @@ import {
import { Badge } from "~/components/ui/badge";
import { Button } from "~/components/ui/button";
import Link from "next/link";
import { ArrowUpRight, Play, BookOpen, Code } from "lucide-react";
import { ArrowUpRight, Play, BookOpen, FolderGit2 } from "lucide-react";
import { projects } from "~/lib/data";
import Image from "next/image";
import { CardSkeleton } from "~/components/ui/skeletons";
@@ -28,23 +28,21 @@ export default function ProjectsPage() {
return (
<div className="space-y-8">
{/* Header */}
<section className="space-y-4">
<div className="flex items-center gap-3">
<Code className="h-8 w-8 text-primary" />
<section className="prose prose-zinc dark:prose-invert max-w-none">
<div className="flex items-start gap-3">
<FolderGit2 className="h-8 w-8 text-primary" />
<div>
<h1 className="text-3xl font-bold">Projects</h1>
<p className="text-lg text-muted-foreground">
A collection of my academic research, professional work, and
personal projects spanning robotics, web development, and embedded
systems.
</p>
<h1 className="mb-2 text-2xl font-bold">Projects</h1>
</div>
</div>
<p className="mt-2 text-lg text-muted-foreground">
A collection of my academic research, professional work, and personal
projects spanning robotics, web development, and embedded systems.
</p>
</section>
{/* Featured Projects */}
<section className="space-y-6">
<section className="animate-fade-in-up space-y-6">
<h2 className="text-2xl font-bold">Featured Work</h2>
<div className="space-y-8">
{loading ? (
@@ -55,38 +53,31 @@ export default function ProjectsPage() {
</>
) : (
featuredProjects.map((project, index) => (
<Card key={index} className="overflow-hidden">
<div className="flex flex-col lg:flex-row">
{/* Project Image */}
{project.image && (
<div className="relative lg:w-2/5">
<div className="aspect-[16/10] lg:aspect-square">
<Image
src={project.image}
alt={project.imageAlt || project.title}
width={600}
height={400}
className="h-full w-full object-cover"
priority={index === 0}
/>
{project.title === "LaTeX Introduction Tutorial" && (
<div className="absolute inset-0 flex items-center justify-center bg-black/20">
<div className="flex h-16 w-16 items-center justify-center bg-white/90 transition-transform hover:scale-110">
<Play
className="h-8 w-8 text-primary"
fill="currentColor"
/>
</div>
</div>
)}
<div
key={index}
className={`animate-fade-in-up-delay-${Math.min(index + 1, 4)} card-hover`}
>
<Card className="overflow-hidden">
<div className="flex flex-col lg:flex-row">
{/* Project Image */}
{project.image && (
<div className="lg:w-1/3">
<div className="flex items-center justify-center p-4 lg:h-full">
<Image
src={project.image}
alt={project.imageAlt || project.title}
width={400}
height={300}
className="h-auto w-full object-contain"
priority={index === 0}
/>
</div>
</div>
</div>
)}
)}
{/* Project Content */}
<div className="flex flex-1 flex-col justify-between p-6">
<div className="space-y-4">
<div className="flex items-start justify-between">
{/* Project Content */}
<div className="flex flex-1 flex-col p-6">
<div className="flex-1 space-y-4">
<div>
<CardTitle className="text-xl">
{project.title}
@@ -95,67 +86,59 @@ export default function ProjectsPage() {
{project.description}
</CardDescription>
</div>
{project.link && !project.link.startsWith("/") && (
<Link
href={project.link}
target="_blank"
rel="noopener noreferrer"
className="ml-4 text-muted-foreground transition-colors hover:text-primary"
>
<ArrowUpRight className="h-5 w-5" />
</Link>
)}
<p className="text-muted-foreground">
{project.longDescription}
</p>
</div>
<p className="text-muted-foreground">
{project.longDescription}
</p>
<div className="mt-6 flex items-center justify-between gap-4">
<div className="flex flex-wrap gap-2">
{project.tags.map((tag) => (
<Badge key={tag} variant="secondary">
{tag}
</Badge>
))}
</div>
<div className="flex flex-wrap gap-2">
{project.tags.map((tag) => (
<Badge key={tag} variant="secondary">
{tag}
</Badge>
))}
<div className="flex gap-2">
{project.link && project.link.startsWith("/") && (
<Button variant="outline" asChild>
<Link href={project.link}>
{project.title ===
"LaTeX Introduction Tutorial" ? (
<>
<Play className="mr-2 h-4 w-4" />
Watch Tutorial
</>
) : (
<>
<BookOpen className="mr-2 h-4 w-4" />
Learn More
</>
)}
</Link>
</Button>
)}
{project.link && !project.link.startsWith("/") && (
<Button variant="outline" asChild>
<Link
href={project.link}
target="_blank"
rel="noopener noreferrer"
>
<ArrowUpRight className="mr-2 h-4 w-4" />
View Project
</Link>
</Button>
)}
</div>
</div>
</div>
{/* Action Buttons */}
<div className="mt-6 flex flex-wrap gap-3">
{project.link && project.link.startsWith("/") && (
<Button asChild>
<Link href={project.link}>
{project.title === "LaTeX Introduction Tutorial" ? (
<>
<Play className="mr-2 h-4 w-4" />
Watch Tutorial
</>
) : (
<>
<BookOpen className="mr-2 h-4 w-4" />
Learn More
</>
)}
</Link>
</Button>
)}
{project.link && !project.link.startsWith("/") && (
<Button variant="outline" asChild>
<Link
href={project.link}
target="_blank"
rel="noopener noreferrer"
>
<ArrowUpRight className="mr-2 h-4 w-4" />
View Project
</Link>
</Button>
)}
</div>
</div>
</div>
</Card>
</Card>
</div>
))
)}
</div>
@@ -163,7 +146,7 @@ export default function ProjectsPage() {
{/* Other Projects */}
{otherProjects.length > 0 && (
<section className="space-y-6">
<section className="animate-fade-in-up space-y-6">
<h2 className="text-2xl font-bold">Additional Projects</h2>
<div className="grid gap-6 md:grid-cols-2">
{loading ? (
@@ -173,122 +156,84 @@ export default function ProjectsPage() {
</>
) : (
otherProjects.map((project, index) => (
<Card key={index} className="flex flex-col">
{project.image && (
<div className="relative aspect-[16/10] overflow-hidden">
<Image
src={project.image}
alt={project.imageAlt || project.title}
width={400}
height={250}
className="h-full w-full object-cover"
/>
</div>
)}
<div className="flex flex-1 flex-col p-6">
<CardHeader className="p-0">
<div className="flex items-start justify-between">
<CardTitle className="text-lg">
{project.title}
</CardTitle>
{project.link && !project.link.startsWith("/") && (
<Link
href={project.link}
target="_blank"
rel="noopener noreferrer"
className="text-muted-foreground transition-colors hover:text-primary"
>
<ArrowUpRight className="h-4 w-4" />
</Link>
)}
</div>
<CardDescription className="mt-2">
{project.description}
</CardDescription>
</CardHeader>
<CardContent className="flex-1 p-0 pt-4">
<div className="flex flex-wrap gap-2">
{project.tags.map((tag) => (
<Badge
key={tag}
variant="secondary"
className="text-xs"
>
{tag}
</Badge>
))}
</div>
</CardContent>
{project.link && (
<div className="mt-4">
<Button
size="sm"
variant="outline"
asChild
className="w-full"
>
<Link
href={project.link}
{...(!project.link.startsWith("/") && {
target: "_blank",
rel: "noopener noreferrer",
})}
>
{project.link.startsWith("/") ? (
<>
<BookOpen className="mr-2 h-4 w-4" />
Learn More
</>
) : (
<>
<ArrowUpRight className="mr-2 h-4 w-4" />
View Project
</>
)}
</Link>
</Button>
<div
key={index}
className={`animate-fade-in-up-delay-${Math.min(index + 1, 4)} card-hover`}
>
<Card className="flex flex-col">
{project.image && (
<div className="flex h-48 items-center justify-center p-4">
<Image
src={project.image}
alt={project.imageAlt || project.title}
width={400}
height={250}
className="h-auto max-h-full w-full object-contain"
/>
</div>
)}
</div>
</Card>
<div className="flex flex-1 flex-col p-6">
<div className="flex-1">
<CardHeader className="p-0">
<div>
<CardTitle className="text-lg">
{project.title}
</CardTitle>
</div>
<CardDescription className="mt-2">
{project.description}
</CardDescription>
</CardHeader>
</div>
<CardContent className="p-0 pt-4">
<div className="flex items-center justify-between gap-4">
<div className="flex flex-wrap gap-2">
{project.tags.map((tag) => (
<Badge
key={tag}
variant="secondary"
className="text-xs"
>
{tag}
</Badge>
))}
</div>
{project.link && (
<Button variant="outline" asChild>
<Link
href={project.link}
{...(!project.link.startsWith("/") && {
target: "_blank",
rel: "noopener noreferrer",
})}
>
{project.link.startsWith("/") ? (
<>
<BookOpen className="mr-2 h-4 w-4" />
Learn More
</>
) : (
<>
<ArrowUpRight className="mr-2 h-4 w-4" />
View Project
</>
)}
</Link>
</Button>
)}
</div>
</CardContent>
</div>
</Card>
</div>
))
)}
</div>
</section>
)}
{/* Project Stats */}
<section className="border-t pt-8">
<div className="grid gap-4 md:grid-cols-3">
<Card className="text-center">
<CardContent className="pt-6">
<div className="text-2xl font-bold">{projects.length}</div>
<div className="text-sm text-muted-foreground">
Total Projects
</div>
</CardContent>
</Card>
<Card className="text-center">
<CardContent className="pt-6">
<div className="text-2xl font-bold">
{[...new Set(projects.flatMap((p) => p.tags))].length}
</div>
<div className="text-sm text-muted-foreground">Technologies</div>
</CardContent>
</Card>
<Card className="text-center">
<CardContent className="pt-6">
<div className="text-2xl font-bold">
{projects.filter((p) => p.featured).length}
</div>
<div className="text-sm text-muted-foreground">Featured</div>
</CardContent>
</Card>
</div>
</section>
</div>
);
}
+103 -94
View File
@@ -81,17 +81,21 @@ export default function PublicationsPage() {
return (
<div className="space-y-6">
<section className="prose prose-zinc dark:prose-invert max-w-none">
<h1 className="flex items-center gap-2 text-2xl font-bold">
<BookOpen className="h-6 w-6" />
Peer-Reviewed Publications
</h1>
<section className="animate-fade-in-up prose prose-zinc dark:prose-invert max-w-none">
<div className="flex items-start gap-3">
<BookOpenText className="h-8 w-8 text-primary" />
<div>
<h1 className="mb-2 text-2xl font-bold">
Peer-Reviewed Publications
</h1>
</div>
</div>
<p className="mt-2 text-lg text-muted-foreground">
My research publications in human-robot interaction and robotics.
</p>
</section>
<div className="space-y-6">
<div className="animate-fade-in-up-delay-2 space-y-6">
{loading ? (
<>
<CardSkeleton />
@@ -100,97 +104,102 @@ export default function PublicationsPage() {
</>
) : (
publications.map((pub, index) => (
<Card key={index}>
<CardHeader className="pb-2">
<div className="flex items-center justify-between">
<CardTitle>{pub.title}</CardTitle>
{pub.paperUrl && (
<Link
href={pub.paperUrl}
target="_blank"
rel="noopener noreferrer"
className="text-muted-foreground hover:text-primary"
>
<ArrowUpRight className="h-5 w-5" />
</Link>
)}
</div>
<CardDescription className="text-base">
{pub.authors.join(", ")}
</CardDescription>
<CardDescription className="text-sm">
{pub.venue} ({pub.year})
</CardDescription>
{pub.address && (
<CardDescription className="text-sm text-muted-foreground">
{pub.address}
<div
key={index}
className={`animate-fade-in-up-delay-${Math.min(index + 3, 4)} card-hover`}
>
<Card>
<CardHeader className="pb-2">
<div className="flex items-center justify-between">
<CardTitle>{pub.title}</CardTitle>
{pub.paperUrl && (
<Link
href={pub.paperUrl}
target="_blank"
rel="noopener noreferrer"
className="text-muted-foreground hover:text-primary"
>
<ArrowUpRight className="h-5 w-5" />
</Link>
)}
</div>
<CardDescription className="text-base">
{pub.authors.join(", ")}
</CardDescription>
)}
{pub.notes && (
<div className="mt-1">
<Badge variant="outline" className="text-xs">
{pub.notes}
<CardDescription className="text-sm">
{pub.venue} ({pub.year})
</CardDescription>
{pub.address && (
<CardDescription className="text-sm text-muted-foreground">
{pub.address}
</CardDescription>
)}
{pub.notes && (
<div className="mt-1">
<Badge variant="outline" className="text-xs">
{pub.notes}
</Badge>
</div>
)}
</CardHeader>
<CardContent className="pt-0">
{pub.abstract && (
<p className="text-sm text-muted-foreground">
{pub.abstract}
</p>
)}
<div className="mt-4 flex flex-wrap gap-2">
<Badge variant="secondary" className="capitalize">
{pub.type}
</Badge>
{pub.doi && (
<Link
href={`https://doi.org/${pub.doi}`}
target="_blank"
rel="noopener noreferrer"
>
<Badge variant="secondary" className="capitalize">
<ArrowUpRight className="mr-1 h-3 w-3" />
DOI
</Badge>
</Link>
)}
{pub.paperUrl && (
<Link
href={pub.paperUrl}
target="_blank"
rel="noopener noreferrer"
>
<Badge variant="secondary" className="capitalize">
<FileText className="mr-1 h-3 w-3" />
Paper
</Badge>
</Link>
)}
{pub.posterUrl && (
<Link
href={pub.posterUrl}
target="_blank"
rel="noopener noreferrer"
>
<Badge variant="secondary" className="capitalize">
<Presentation className="mr-1 h-3 w-3" />
Poster
</Badge>
</Link>
)}
<Badge
onClick={() => downloadBibtex(pub)}
className="cursor-pointer capitalize"
variant="secondary"
>
<BookOpenText className="mr-1 h-3 w-3" />
BibTeX
</Badge>
</div>
)}
</CardHeader>
<CardContent className="pt-0">
{pub.abstract && (
<p className="text-sm text-muted-foreground">
{pub.abstract}
</p>
)}
<div className="mt-4 flex flex-wrap gap-2">
<Badge variant="secondary" className="capitalize">
{pub.type}
</Badge>
{pub.doi && (
<Link
href={`https://doi.org/${pub.doi}`}
target="_blank"
rel="noopener noreferrer"
>
<Badge variant="secondary" className="capitalize">
<ArrowUpRight className="mr-1 h-3 w-3" />
DOI
</Badge>
</Link>
)}
{pub.paperUrl && (
<Link
href={pub.paperUrl}
target="_blank"
rel="noopener noreferrer"
>
<Badge variant="secondary" className="capitalize">
<FileText className="mr-1 h-3 w-3" />
Paper
</Badge>
</Link>
)}
{pub.posterUrl && (
<Link
href={pub.posterUrl}
target="_blank"
rel="noopener noreferrer"
>
<Badge variant="secondary" className="capitalize">
<Presentation className="mr-1 h-3 w-3" />
Poster
</Badge>
</Link>
)}
<Badge
onClick={() => downloadBibtex(pub)}
className="cursor-pointer capitalize"
variant="secondary"
>
<BookOpenText className="mr-1 h-3 w-3" />
BibTeX
</Badge>
</div>
</CardContent>
</Card>
</CardContent>
</Card>
</div>
))
)}
</div>
+52 -40
View File
@@ -11,7 +11,9 @@ import {
import { CardSkeleton } from "~/components/ui/skeletons";
import Image from "next/image";
import { Badge } from "~/components/ui/badge";
import { Plane } from "lucide-react";
import { travel } from "~/lib/data";
export default function TripsPage() {
const [loading, setLoading] = useState(true);
@@ -24,14 +26,19 @@ export default function TripsPage() {
return (
<div className="space-y-6">
<section className="prose prose-zinc dark:prose-invert max-w-none">
<h1 className="text-2xl font-bold">My Trips & Events 🌍</h1>
<section className="animate-fade-in-up prose prose-zinc dark:prose-invert max-w-none">
<div className="flex items-start gap-3">
<Plane className="h-8 w-8 text-primary" />
<div>
<h1 className="mb-2 text-2xl font-bold">My Trips & Events</h1>
</div>
</div>
<p className="mt-2 text-lg text-muted-foreground">
A collection of memorable trips and events I&apos;ve attended.
</p>
</section>
<div className="grid grid-cols-1 gap-6 md:grid-cols-2">
<div className="animate-fade-in-up-delay-2 grid grid-cols-1 gap-6 md:grid-cols-2">
{loading ? (
<>
<CardSkeleton />
@@ -43,45 +50,50 @@ export default function TripsPage() {
</>
) : (
travel.map((trip, index) => (
<Card key={index} className="overflow-hidden rounded-lg">
<CardHeader className="p-0">
<div className="flex flex-col">
<div className="flex space-x-0 overflow-x-auto">
{trip.images.map((image, imgIndex) => (
<div key={imgIndex} className="flex-shrink-0">
<Image
src={image}
alt={
trip.alts && trip.alts[imgIndex]
? trip.alts[imgIndex]
: `${trip.title} - image ${imgIndex + 1}`
}
width={250}
height={200}
className="max-h-[200px] min-h-[200px] object-cover"
/>
</div>
))}
<div
key={index}
className={`animate-fade-in-up-delay-${Math.min(index + 3, 4)} card-hover`}
>
<Card className="overflow-hidden rounded-lg">
<CardHeader className="p-0">
<div className="flex flex-col">
<div className="flex space-x-0 overflow-x-auto">
{trip.images.map((image, imgIndex) => (
<div key={imgIndex} className="flex-shrink-0">
<Image
src={image}
alt={
trip.alts && trip.alts[imgIndex]
? trip.alts[imgIndex]
: `${trip.title} - image ${imgIndex + 1}`
}
width={250}
height={200}
className="max-h-[200px] min-h-[200px] object-cover"
/>
</div>
))}
</div>
</div>
</div>
</CardHeader>
<CardContent>
<div className="flex flex-col content-between justify-items-start">
<CardTitle className="mb-2 mt-6">{trip.title}</CardTitle>
<CardDescription className="">
{trip.description}
</CardDescription>
{/* Show badges for tags */}
<div className="mt-4 flex flex-wrap gap-2">
{trip.tags.map((tag, tagIndex) => (
<Badge key={tagIndex} variant="secondary">
{tag}
</Badge>
))}
</CardHeader>
<CardContent>
<div className="flex flex-col content-between justify-items-start">
<CardTitle className="mb-2 mt-6">{trip.title}</CardTitle>
<CardDescription className="">
{trip.description}
</CardDescription>
{/* Show badges for tags */}
<div className="mt-4 flex flex-wrap gap-2">
{trip.tags.map((tag, tagIndex) => (
<Badge key={tagIndex} variant="secondary">
{tag}
</Badge>
))}
</div>
</div>
</div>
</CardContent>
</Card>
</CardContent>
</Card>
</div>
))
)}
</div>