Skip to content

Commit d22196d

Browse files
committed
feat: enhance project navigation and improve footer layout
1 parent 4cde20c commit d22196d

3 files changed

Lines changed: 49 additions & 37 deletions

File tree

src/app/projects/[slug]/page.tsx

Lines changed: 32 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { Badge } from '@components/ui/badge';
1010
import { Button } from '@components/ui/button';
1111
import "../../../styles/code-block-node.css"
1212
import { Avatar, AvatarFallback, AvatarImage } from '@components/ui/avatar';
13-
import { ArrowLeftIcon, Calendar, Clock, ExternalLink, Globe, User } from 'lucide-react';
13+
import { ArrowLeftIcon, ArrowRightIcon, Calendar, Clock, ExternalLink, Globe, User } from 'lucide-react';
1414
import BreadcrumbStructuredData from '@components/breadcrumb-structured-data';
1515
import SoftwareApplicationStructuredData from '@components/data-structured/software-application';
1616
import { MarkdownRenderer } from '@components/ui/markdown-renderer';
@@ -73,6 +73,7 @@ export async function generateStaticParams() {
7373
export default async function ProjectDetail(props: Params) {
7474
const params = await props.params;
7575
const project = getProjectBySlug(params.slug);
76+
const projects = getPublishedProjects();
7677

7778
if (!project) {
7879
return (
@@ -82,7 +83,7 @@ export default async function ProjectDetail(props: Params) {
8283
height={30}
8384
x={-1}
8485
y={-1}
85-
className={'[mask-image:linear-gradient(to_bottom_right,white,transparent,transparent)] '}
86+
className={'mask-[linear-gradient(to_bottom_right,white,transparent,transparent)] '}
8687
/>
8788

8889
<NavigationBar className='sticky' />
@@ -106,6 +107,9 @@ export default async function ProjectDetail(props: Params) {
106107
const createdAtValid = isValidDateValue(project.createdAt);
107108
const createdDate = createdAtValid ? new Date(project.createdAt) : null;
108109
const screenshot = project.image ? [project.image] : [];
110+
const currentIndex = projects.findIndex((item) => item.slug === project.slug);
111+
const previousProject = currentIndex > 0 ? projects[currentIndex - 1] : null;
112+
const nextProject = currentIndex >= 0 && currentIndex < projects.length - 1 ? projects[currentIndex + 1] : null;
109113

110114
return (
111115
<>
@@ -135,13 +139,13 @@ export default async function ProjectDetail(props: Params) {
135139
height={30}
136140
x={-1}
137141
y={-1}
138-
className={'[mask-image:linear-gradient(to_bottom_right,white,transparent,transparent)] '}
142+
className={'mask-[linear-gradient(to_bottom_right,white,transparent,transparent)] '}
139143
/>
140144
</div>
141145

142146
<article className='max-w-5xl sm:px-4 relative mx-auto max-xs:pt-0 sm:mt-16 py-8'>
143147
{project.image && (
144-
<div className='relative w-full sm:p-2 ring-1 rounded-3xl ring-foreground/10 h-full max-xs:max-h-96 md:h-[29rem] max-xs:rounded-none max-xs:rounded-b-4xl overflow-hidden'>
148+
<div className='relative w-full sm:p-2 ring-1 rounded-3xl ring-foreground/10 h-full max-xs:max-h-96 md:h-116 max-xs:rounded-none max-xs:rounded-b-4xl overflow-hidden'>
145149
<PostCoverImage src={project.image} alt={project.title} />
146150
</div>
147151
)}
@@ -153,7 +157,7 @@ export default async function ProjectDetail(props: Params) {
153157
</Link>
154158
</Button>
155159

156-
<div className='flex justify-end max-sm:pr-3 items-center py-4 gap-2 flex-wrap'>
160+
<div className='flex justify-end items-center py-4 gap-2 flex-wrap'>
157161
{(project.source ?? []).map((source) => (
158162
<Button asChild key={`${source.type}-${source.url}`} className='mt-0 border'>
159163
<Link href={source.url} target='_blank' rel='noopener noreferrer'>
@@ -169,7 +173,7 @@ export default async function ProjectDetail(props: Params) {
169173
</div>
170174
</div>
171175

172-
<div className='flex flex-wrap items-center mb-4 justify-between'>
176+
<div className='flex px-3 xs:px-0 gap-2 flex-wrap items-center mb-4 justify-between'>
173177
{(project.languages?.length || 0) > 0 && (
174178
<div className='flex w-fit justify-center rounded-full p-0.5 sm:p-1 ring-1 ring-foreground/10 gap-1 bg-background'>
175179
{(project.languages ?? []).map((language) => (
@@ -216,7 +220,7 @@ export default async function ProjectDetail(props: Params) {
216220

217221
<div className='max-sm:px-3 flex flex-col relative order-1 mb-4'>
218222
<h1 className='text-4xl md:text-5xl font-bold leading-tight'>
219-
<span className="text-left bg-background bg-clip-text bg-no-repeat text-transparent bg-gradient-to-r from-sky-500 via-teal-500 to-green-500 [text-shadow:0_0_rgba(0,0,0,0.1)]"> {project.title} </span>
223+
<span className="text-left bg-background bg-clip-text bg-no-repeat text-transparent bg-linear-to-r from-sky-500 via-teal-500 to-green-500 [text-shadow:0_0_rgba(0,0,0,0.1)]"> {project.title} </span>
220224
</h1>
221225
<p className='text-base text-foreground/80 mt-3 leading-relaxed font-sans'>{project.description}</p>
222226
</div>
@@ -253,20 +257,28 @@ export default async function ProjectDetail(props: Params) {
253257
<MarkdownRenderer content={project.content} />
254258
</div>
255259

256-
<div className='flex items-center mx-auto justify-between max-xs:px-3 mt-8'>
257-
<div className='flex flex-wrap gap-2'>
258-
{project.tags?.map((tag) => (
259-
<Badge key={`${project.slug}-${tag}`} variant='outline'>
260-
#{tag}
261-
</Badge>
262-
))}
260+
<div className='flex flex-col gap-3 max-xs:px-3 mt-10'>
261+
<div className='flex items-center mx-auto justify-between w-full gap-3'>
262+
{previousProject ? (
263+
<Button asChild>
264+
<Link href={`/projects/${previousProject.slug}`}>
265+
<ArrowLeftIcon className='w-4 h-4 mr-2 shrink-0' />
266+
<span className='line-clamp-1 max-xs:hidden'>{previousProject.title}</span>
267+
<span className='sm:hidden'>Prev</span>
268+
</Link>
269+
</Button>
270+
) : ( <div /> )}
271+
272+
{nextProject ? (
273+
<Button asChild>
274+
<Link href={`/projects/${nextProject.slug}`}>
275+
<span className='line-clamp-1 max-xs:hidden'>{nextProject.title}</span>
276+
<span className='sm:hidden'>Next</span>
277+
<ArrowRightIcon className='w-4 h-4 ml-2 shrink-0' />
278+
</Link>
279+
</Button>
280+
) : ( <div /> )}
263281
</div>
264-
265-
<Button asChild>
266-
<Link href='/projects'>
267-
<ArrowLeftIcon className='w-4 h-4 mr-2' /> All Projects
268-
</Link>
269-
</Button>
270282
</div>
271283
</div>
272284

src/app/projects/page.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ const ProjectsContent = () => {
144144
<div className="flex flex-wrap max-sm:justify-center items-center gap-1.5">
145145
<p className="text-xs font-medium">Tag: </p>
146146
<Button
147-
className={cn("mt-0 py-1 sm:py-2 px-2.5 !h-fit text-xs leading-4 text-foreground/80 hover:text-primary transition-all", !selectedTag && "ring-1 !px-4 bg-primary/5 text-primary")}
147+
className={cn("mt-0 py-1 sm:py-2 px-2.5 h-fit! text-xs leading-4 text-foreground/80 hover:text-primary transition-all", !selectedTag && "ring-1 px-4! bg-primary/5 text-primary")}
148148
onClick={() => handleTagChange("")}
149149
aria-pressed={!selectedTag}
150150
>
@@ -159,7 +159,7 @@ const ProjectsContent = () => {
159159
type="button"
160160
onClick={() => handleTagChange(isActive ? "" : tag)}
161161
className={cn(
162-
"mt-0 py-1 sm:py-2 px-2.5 !h-fit text-xs leading-4 text-foreground/80 hover:text-primary transition-all",
162+
"mt-0 py-1 sm:py-2 px-2.5 h-fit! text-xs leading-4 text-foreground/80 hover:text-primary transition-all",
163163
isActive && "ring-1 bg-primary/5 text-primary"
164164
)}
165165
aria-pressed={isActive}
@@ -171,7 +171,7 @@ const ProjectsContent = () => {
171171
</div>
172172
</div>
173173
)}
174-
<article className="grid max-w-5xl mx-auto p-5 grid-cols-1 xs:grid-cols-2 lg:grid-cols-3 gap-4 min-h-[300px] relative">
174+
<article className="grid max-w-5xl mx-auto p-5 grid-cols-1 xs:grid-cols-2 lg:grid-cols-3 gap-4 min-h-75 relative">
175175
{filteredProjects.map((project) => (<ProjectCard key={project.id || project.title} project={project} />))}
176176
{filteredProjects.length === 0 && (searchQuery || selectedTag) && (
177177
<div className="col-span-full text-center py-12">

src/components/layouts/footer.tsx

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -54,10 +54,10 @@ export default function Footer() {
5454
const currentYear = new Date().getFullYear();
5555

5656
return (
57-
<footer className="w-full border-t px-3 border-border/40 bg-linear-to-b from-background via-background/95 to-background backdrop-blur supports-backdrop-filter:bg-background/60">
57+
<footer className="w-full border-t sm:px-3 border-border/40 bg-linear-to-b from-background via-background/95 to-background backdrop-blur supports-backdrop-filter:bg-background/60">
5858
<div className="max-w-5xl mx-auto max-sm:px-5 py-12 md:py-16">
5959
{/* Main Footer Content */}
60-
<div className="grid grid-cols-2 md:grid-cols-4 lg:grid-cols-5 gap-8 md:gap-12 mb-12 sm:px-5">
60+
<div className="grid grid-cols-2 md:grid-cols-4 lg:grid-cols-5 gap-8 md:gap-12 mb-12 md:px-5">
6161
{/* Brand Section */}
6262
<div className="col-span-2 md:col-span-4 lg:col-span-1 space-y-4">
6363
<Link href="/" className="inline-block group">
@@ -68,11 +68,11 @@ export default function Footer() {
6868

6969
{/* Product Links */}
7070
<div className="space-y-4">
71-
<h3 className="font-semibold text-base text-primary pl-2">Product</h3>
72-
<ul className="gap-2">
71+
<h3 className="font-semibold text-base text-primary sm:pl-2">Product</h3>
72+
<ul className="max-sm:gap-2 flex flex-col">
7373
{footerLinks.product.map((link) => (
7474
<li key={link.href}>
75-
<Button asChild className="mt-0 text-foreground/70 h-7 text-xs hover:text-primary -translate-x-2.5">
75+
<Button asChild className="mt-0 text-foreground/70 h-7 text-xs hover:text-primary xs:-translate-x-2.5">
7676
<Link href={link.href}>
7777
<ArrowRightIcon className='w-4 h-4' /> {link.label}
7878
</Link>
@@ -84,11 +84,11 @@ export default function Footer() {
8484

8585
{/* Company Links */}
8686
<div className="space-y-4">
87-
<h3 className="font-semibold text-base text-primary pl-2">Company</h3>
88-
<ul className="gap-2">
87+
<h3 className="font-semibold text-base text-primary sm:pl-2">Company</h3>
88+
<ul className="max-sm:gap-2 flex flex-col">
8989
{footerLinks.company.map((link) => (
9090
<li key={link.href}>
91-
<Button asChild className="mt-0 text-foreground/70 h-7 text-xs hover:text-primary -translate-x-2.5">
91+
<Button asChild className="mt-0 text-foreground/70 h-7 text-xs hover:text-primary xs:-translate-x-2.5">
9292
<Link href={link.href}>
9393
<ArrowRightIcon className='w-4 h-4' /> {link.label}
9494
</Link>
@@ -100,11 +100,11 @@ export default function Footer() {
100100

101101
{/* Resources Links */}
102102
<div className="space-y-4">
103-
<h3 className="font-semibold text-base text-primary pl-2">Resources</h3>
104-
<ul className="gap-2">
103+
<h3 className="font-semibold text-base text-primary sm:pl-2">Resources</h3>
104+
<ul className="max-sm:gap-2 flex flex-col">
105105
{footerLinks.resources.map((link) => (
106106
<li key={link.href}>
107-
<Button asChild className="mt-0 text-foreground/70 h-7 text-xs hover:text-primary -translate-x-2.5">
107+
<Button asChild className="mt-0 text-foreground/70 h-7 text-xs hover:text-primary xs:-translate-x-2.5">
108108
<Link href={link.href}>
109109
<ArrowRightIcon className='w-4 h-4' /> {link.label}
110110
</Link>
@@ -116,14 +116,14 @@ export default function Footer() {
116116

117117
{/* Social Links */}
118118
<div className="space-y-4">
119-
<h3 className="font-semibold text-base pl-2 text-primary">Social</h3>
120-
<ul className="gap-2">
119+
<h3 className="font-semibold text-base sm:pl-2 text-primary">Social</h3>
120+
<ul className="max-sm:gap-2 flex flex-col">
121121
{socialLinks.map((link) => {
122122
const Icon = link.icon;
123123

124124
return (
125125
<li key={link.href}>
126-
<Button asChild className="mt-0 text-foreground/70 h-7 text-xs hover:text-primary -translate-x-2.5">
126+
<Button asChild className="mt-0 text-foreground/70 h-7 text-xs hover:text-primary xs:-translate-x-2.5">
127127
<Link href={link.href}>
128128
<ArrowRightIcon className='w-4 h-4' /> {link.label}
129129
</Link>

0 commit comments

Comments
 (0)