Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ export default async function EventPage({
Date
</h3>
<p className="text-neutral-01 font-normal text-sm sm:text-base">
{new Date(event.date).toLocaleDateString("en-US", {
{new Date(event.date).toLocaleDateString(navigator.language, {
day: "numeric",
month: "long",
year: "numeric",
Expand Down
170 changes: 97 additions & 73 deletions src/app/projects/[slug]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { supabase } from '@/lib/supabaseClient';
import Link from 'next/link';
import Image from 'next/image';
import { routes } from '@/lib/routes';
import { ProjectAnalytics } from '@/components/ProjectAnalytics';
import { Hero } from '@/components/Hero';

const DUMMY_IMAGE = 'https://images.unsplash.com/photo-1506744038136-46273834b3fb?auto=format&fit=crop&w=900&q=80';

Expand All @@ -15,8 +15,23 @@ export default async function ProjectDetailPage({ params }: { params: Promise<{
.eq('slug', slug)
.single();

if (error || !project) {
return <div className="p-8 text-red-600">Project not found.</div>;
if (error) {
console.error(error);
return (
<div className="container mx-auto px-4 sm:px-6 lg:px-8 text-red-600 py-14">
Failed to load project
<br />
{error?.message || "Error fetching project"}
</div>
);
}

if (!project) {
return (
<div className="container mx-auto px-4 sm:px-6 lg:px-8 text-color-01 py-14">
Project Not Found
</div>
);
}

const { data: category } = await supabase
Expand All @@ -32,87 +47,96 @@ export default async function ProjectDetailPage({ params }: { params: Promise<{
categoryName={category?.name || 'N/A'}
projectSlug={slug}
/>
<div className="bg-neutral-01 min-h-screen">
{/* hero section */}
<div className="bg-color-02 py-16 text-center">
<h1 className="text-3xl md:text-5xl font-bold text-white mb-6">{project.name}</h1>
<Link href={routes.projects}>
<span className="inline-block border border-white text-white rounded-full px-6 py-2 text-base font-medium hover:bg-white hover:text-color-02 transition">Back to all Projects</span>
</Link>
</div>
<div className="max-w-6xl mx-auto px-4 py-12">
{/* main image */}
<div className="mb-10 relative" style={{ aspectRatio: '16/7' }}>
<Image
src={project.image_url || DUMMY_IMAGE}
alt={project.name}
fill
className="rounded-2xl object-cover"
sizes="(max-width: 1152px) 100vw, 1152px"
/>
</div>
<div className="flex flex-col lg:flex-row gap-10">
{/* description */}
<div className="flex-1 min-w-0">
<section className="mb-8">
<h2 className="text-xl font-semibold mb-2">The Problem</h2>
<p className="text-neutral-03 leading-relaxed">{project.problem}</p>
</section>
<section className="mb-8">
<h2 className="text-xl font-semibold mb-2">Tech4R&apos;s Solution</h2>
<p className="text-neutral-03 leading-relaxed">{project.solution}</p>
</section>
<section className="mb-8">
<h2 className="text-xl font-semibold mb-2">Outcome</h2>
<p className="text-neutral-03 leading-relaxed">{project.outcome}</p>
</section>
<Hero
title={project.name}
buttonText="Back to all Projects"
buttonUrl={routes.projects}
/>
<section className="w-full py-30 space-y-30">
<div className="container mx-auto px-4 sm:px-6 lg:px-8">
{/* main image */}
<div className="mb-10 relative" style={{ aspectRatio: '16/7' }}>
<Image
src={project.image_url || DUMMY_IMAGE}
alt={project.name}
fill
className="rounded-2xl object-cover"
sizes="(max-width: 1152px) 100vw, 1152px"
/>
</div>
<div className="flex flex-col gap-20 md:flex-row">
<div className="flex flex-col gap-8 flex-2/4">
<h3 className="text-neutral-04 text-2xl sm:text-[32px] font-medium leading-[140%] tracking-[-1px]">
{project.name}
</h3>
<div className="flex flex-col gap-6">
<div>
<h4 className="text-xl font-semibold mb-2">The Problem</h4>
<p className="text-neutral-03 font-light text-sm sm:text-base leading-[170%] tracking-[0px]">{project.problem}</p>
</div>
<div>
<h4 className="text-xl font-semibold mb-2">Tech4R&apos;s Solution</h4>
<p className="text-neutral-03 font-light text-sm sm:text-base leading-[170%] tracking-[0px]">{project.solution}</p>
</div>
<div>
<h4 className="text-xl font-semibold mb-2">Outcome</h4>
<p className="text-neutral-03 font-light text-sm sm:text-base leading-[170%] tracking-[0px]">{project.outcome}</p>
</div>
</div>
</div>
{/* sub information */}
<aside className="w-full lg:w-80 flex-shrink-0">
<div className="bg-color-02 rounded-2xl p-6 text-white flex flex-col gap-6">
<div>
<div className="text-base font-semibold mb-2">Category</div>
<span className="inline-block border border-white text-white text-xs px-3 py-1 rounded-full font-medium">
{category?.name || 'N/A'}
</span>
<div className="bg-color-02 rounded-2xl text-white p-8 flex flex-col gap-6 flex-[1.5] h-fit">
<div className="flex flex-col gap-4">
<h3 className="text-xl sm:text-[32px] font-medium leading-[140%] tracking-[-1px]">
Category
</h3>
<div>
<span className="inline-block border border-white text-white text-xs px-3 py-1 rounded-full font-medium uppercase">
{category?.name || 'N/A'}
</span>
</div>
</div>
<div>
<div className="text-base font-semibold mb-2">Tools</div>
<div className="flex flex-col gap-4">
<h3 className="text-xl sm:text-[32px] font-medium leading-[140%] tracking-[-1px]">
Tools
</h3>
<div className="flex flex-wrap gap-2">
{(Array.isArray(project.tools_used) ? project.tools_used : [project.tools_used]).map((tool: string, idx: number) => (
<span key={idx} className="inline-block border border-white text-white text-xs px-3 py-1 rounded-full font-medium">
<span key={idx} className="inline-block border border-white text-white text-xs px-3 py-1 rounded-full font-medium uppercase">
{tool}
</span>
))}
</div>
</div>
<div>
<div className="text-base font-semibold mb-2">Date Completed</div>
<div className="text-white/80 text-sm">{project.completion_date ? new Date(project.completion_date).toLocaleDateString('en-GB', {
day: 'numeric',
month: 'long',
year: 'numeric'
}).replace(/(\d+)/, (day) => {
const num = parseInt(day);
if (num % 10 === 1 && num !== 11) return `${num}st`;
if (num % 10 === 2 && num !== 12) return `${num}nd`;
if (num % 10 === 3 && num !== 13) return `${num}rd`;
return `${num}th`;
}).replace(' ', ' ').replace(/ ([A-Za-z]+) /, ' $1, ') : 'N/A'}</div>
</div>
<div>
<div className="text-base font-semibold mb-2">Link to Project</div>
{project.link ? (
<a href={project.link} target="_blank" rel="noopener noreferrer" className="underline text-white/90 hover:text-white">{project.link}</a>
) : (
<span className="text-white/60">N/A</span>
)}
</div>
{project.completion_date && (
<div className="flex flex-col gap-4">
<h3 className="text-xl sm:text-[32px] font-medium leading-[140%] tracking-[-1px]">
Date Completed
</h3>
<p className="text-neutral-01 font-normal text-sm sm:text-base">
{new Date(project.completion_date).toLocaleDateString(navigator.language, {
day: 'numeric',
month: 'long',
year: 'numeric'
})}
</p>
</div>
)}
{project.link && (
<div className="flex flex-col gap-4">
<h3 className="text-xl sm:text-[32px] font-medium leading-[140%] tracking-[-1px]">
Link to Project
</h3>
<p className="text-neutral-01 font-normal text-sm sm:text-base">
<a href={project.link} target="_blank" rel="noopener noreferrer" className="underline text-neutral-01 hover:text-white">
{project.link}
</a>
</p>
</div>
)}
</div>
</aside>
</div>
</div>
</div>
</div>
</section>
</>
);
}
2 changes: 1 addition & 1 deletion src/app/sitemap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ export default async function sitemap(): Promise<MetadataRoute.Sitemap> {

if (events) {
eventPages = events.map((event) => ({
url: `${baseUrl}/event/${event.slug}`,
url: `${baseUrl}/events/${event.slug}`,
lastModified: new Date(event.date),
changeFrequency: 'monthly' as const,
priority: 0.5,
Expand Down
2 changes: 1 addition & 1 deletion src/components/Events/EventCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ const EventCard = ({ event }: { event: Event }) => {
const { name, description, date } = event;
return (
<article className="bg-neutral-01 rounded-2xl shadow-md hover:shadow-lg transition-shadow duration-300 overflow-hidden">
<Link href={`/event/${event.slug}`}>
<Link href={`/events/${event.slug}`}>
<div className="px-6 py-8">
<p className="text-[16px] text-color-01 font-[400] mb-2">
{new Date(date).toLocaleDateString("en-US", {
Expand Down
2 changes: 1 addition & 1 deletion src/components/StructuredData.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ export function StructuredData({ type, data }: StructuredDataProps) {
},
eventStatus: 'https://schema.org/EventScheduled',
eventAttendanceMode: 'https://schema.org/OfflineEventAttendanceMode',
url: `${baseUrl}/event/${eventData.slug}`,
url: `${baseUrl}/events/${eventData.slug}`,
...(eventData.event_categories && {
category: eventData.event_categories.name,
}),
Expand Down