Skip to content

Commit 346a493

Browse files
author
digin
committed
ui refinements and projects added
1 parent e1f2d3e commit 346a493

23 files changed

Lines changed: 1000 additions & 181 deletions

app/contact/ContactClient.jsx

Lines changed: 389 additions & 0 deletions
Large diffs are not rendered by default.

app/contact/page.jsx

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { getContentById } from '@/lib/content';
2+
import ContactClient from './ContactClient';
3+
4+
export const metadata = {
5+
title: 'Contact',
6+
description: 'Get in touch with Digin Dominic. Send a message or connect via email, phone, or social media for collaboration opportunities.',
7+
keywords: ['contact', 'Digin Dominic', 'hire developer', 'freelance', 'collaboration', 'get in touch'],
8+
openGraph: {
9+
title: 'Contact | Digin Dominic',
10+
description: 'Get in touch with Digin Dominic for collaboration opportunities.',
11+
url: 'https://digindominic.me/contact',
12+
type: 'website',
13+
},
14+
twitter: {
15+
card: 'summary_large_image',
16+
title: 'Contact | Digin Dominic',
17+
description: 'Get in touch with Digin Dominic for collaboration opportunities.',
18+
},
19+
alternates: {
20+
canonical: 'https://digindominic.me/contact',
21+
},
22+
};
23+
24+
export default async function ContactPage() {
25+
const aboutContent = await getContentById('about', 'about');
26+
27+
return <ContactClient aboutContent={aboutContent} />;
28+
}

app/not-found.jsx

Lines changed: 77 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3,23 +3,41 @@
33
import Link from 'next/link';
44
import { motion } from 'framer-motion';
55
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
6-
import { faHome, faArrowLeft } from '@fortawesome/free-solid-svg-icons';
6+
import { faHome, faArrowLeft, faFolder, faNewspaper, faUser, faEnvelope } from '@fortawesome/free-solid-svg-icons';
7+
8+
const staggerContainer = {
9+
hidden: { opacity: 0 },
10+
visible: { opacity: 1, transition: { staggerChildren: 0.1 } }
11+
};
12+
13+
const staggerItem = {
14+
hidden: { opacity: 0, y: 10 },
15+
visible: { opacity: 1, y: 0 }
16+
};
717

818
export default function NotFound() {
19+
const quickLinks = [
20+
{ name: 'Home', path: '/', icon: faHome, description: 'Back to the main page' },
21+
{ name: 'Projects', path: '/projects', icon: faFolder, description: 'Browse my portfolio' },
22+
{ name: 'Blog', path: '/blog', icon: faNewspaper, description: 'Read my articles' },
23+
{ name: 'About', path: '/about', icon: faUser, description: 'Learn more about me' },
24+
{ name: 'Contact', path: '/contact', icon: faEnvelope, description: 'Get in touch' },
25+
];
26+
927
return (
10-
<div className="min-h-[70vh] flex items-center justify-center px-4">
28+
<div className="min-h-[80vh] flex items-center justify-center px-4 py-12">
1129
<motion.div
12-
className="text-center glass-card p-8 md:p-12 max-w-xl"
13-
initial={{ opacity: 0, scale: 0.95 }}
14-
animate={{ opacity: 1, scale: 1 }}
30+
className="text-center max-w-2xl w-full"
31+
initial={{ opacity: 0, y: 20 }}
32+
animate={{ opacity: 1, y: 0 }}
1533
transition={{ duration: 0.5 }}
1634
>
1735
{/* Error Code */}
1836
<motion.div
1937
className="mb-6"
20-
initial={{ opacity: 0, y: -20 }}
21-
animate={{ opacity: 1, y: 0 }}
22-
transition={{ delay: 0.2 }}
38+
initial={{ opacity: 0, scale: 0.8 }}
39+
animate={{ opacity: 1, scale: 1 }}
40+
transition={{ delay: 0.1, duration: 0.4 }}
2341
>
2442
<span className="text-8xl md:text-9xl font-display font-bold text-gradient">
2543
404
@@ -28,37 +46,65 @@ export default function NotFound() {
2846

2947
{/* Terminal-style message */}
3048
<motion.div
31-
className="bg-light-surface dark:bg-deep-space rounded-lg p-4 mb-8 font-mono text-sm text-left border border-light-border dark:border-slate-700/50"
49+
className="glass-card p-4 mb-8 font-mono text-sm text-left max-w-md mx-auto"
3250
initial={{ opacity: 0 }}
3351
animate={{ opacity: 1 }}
34-
transition={{ delay: 0.4 }}
52+
transition={{ delay: 0.2 }}
3553
>
3654
<div className="flex items-center gap-2 mb-2 pb-2 border-b border-light-border dark:border-slate-700/50">
37-
<span className="w-3 h-3 rounded-full bg-red-500" />
38-
<span className="w-3 h-3 rounded-full bg-yellow-500" />
39-
<span className="w-3 h-3 rounded-full bg-green-500" />
40-
<span className="ml-2 text-light-text-secondary dark:text-muted-steel text-xs">error.log</span>
55+
<span className="w-3 h-3 rounded-full bg-red-500" aria-hidden="true" />
56+
<span className="w-3 h-3 rounded-full bg-yellow-500" aria-hidden="true" />
57+
<span className="w-3 h-3 rounded-full bg-green-500" aria-hidden="true" />
58+
<span className="ml-2 text-light-text-secondary dark:text-muted-steel text-xs">terminal</span>
4159
</div>
4260
<p className="text-red-500">$ ERROR: Page not found</p>
4361
<p className="text-light-text-secondary dark:text-muted-steel">
44-
&gt; The requested resource does not exist
62+
&gt; The requested URL does not exist on this server
4563
</p>
46-
<p className="text-signal-green">$ Suggestion: Navigate to a valid route</p>
64+
<p className="text-signal-green">$ Redirecting to helpful links...</p>
4765
</motion.div>
4866

4967
{/* Title and description */}
50-
<h1 className="text-2xl md:text-3xl font-display font-bold text-light-text dark:text-ghost-white mb-4">
51-
Page Not Found
68+
<h1 className="text-2xl md:text-3xl font-display font-bold text-light-text dark:text-ghost-white mb-3">
69+
Oops! Page Not Found
5270
</h1>
53-
<p className="text-light-text-secondary dark:text-muted-steel mb-8">
54-
The page you're looking for doesn't exist or has been moved.
71+
<p className="text-light-text-secondary dark:text-muted-steel mb-8 max-w-md mx-auto">
72+
The page you're looking for might have been moved, deleted, or perhaps never existed.
73+
Let me help you find your way.
5574
</p>
5675

76+
{/* Quick Navigation Links */}
77+
<motion.div
78+
className="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-5 gap-3 mb-8"
79+
variants={staggerContainer}
80+
initial="hidden"
81+
animate="visible"
82+
>
83+
{quickLinks.map((link) => (
84+
<motion.div key={link.path} variants={staggerItem}>
85+
<Link
86+
href={link.path}
87+
className="group flex flex-col items-center p-4 rounded-xl bg-light-surface dark:bg-midnight-steel/50 border border-light-border dark:border-slate-700/50 hover:border-neural-blue/50 transition-all duration-300 hover:-translate-y-1"
88+
>
89+
<span className="w-10 h-10 rounded-lg bg-neural-blue/10 flex items-center justify-center mb-2 group-hover:bg-neural-blue/20 transition-colors">
90+
<FontAwesomeIcon icon={link.icon} className="text-neural-blue" />
91+
</span>
92+
<span className="text-sm font-medium text-light-text dark:text-ghost-white group-hover:text-neural-blue transition-colors">
93+
{link.name}
94+
</span>
95+
</Link>
96+
</motion.div>
97+
))}
98+
</motion.div>
99+
57100
{/* Action buttons */}
58101
<div className="flex flex-col sm:flex-row gap-4 justify-center">
59-
<Link href="/" className="btn-primary inline-flex items-center justify-center gap-2">
102+
<Link
103+
href="/"
104+
className="btn-primary inline-flex items-center justify-center gap-2"
105+
>
60106
<FontAwesomeIcon icon={faHome} />
61-
Go Home
107+
Go to Homepage
62108
</Link>
63109
<button
64110
onClick={() => window.history.back()}
@@ -68,6 +114,15 @@ export default function NotFound() {
68114
Go Back
69115
</button>
70116
</div>
117+
118+
{/* Help text */}
119+
<p className="mt-8 text-sm text-light-text-secondary dark:text-muted-steel">
120+
Still can't find what you're looking for?{' '}
121+
<Link href="/contact" className="text-neural-blue hover:text-synapse-cyan transition-colors">
122+
Contact me
123+
</Link>
124+
{' '}and I'll help you out.
125+
</p>
71126
</motion.div>
72127
</div>
73128
);

app/projects/ProjectsClient.jsx

Lines changed: 72 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { useState, useMemo } from 'react';
44
import Link from 'next/link';
55
import { motion, AnimatePresence } from 'framer-motion';
66
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
7-
import { faFolder, faTimes, faChevronRight } from '@fortawesome/free-solid-svg-icons';
7+
import { faFolder, faTimes, faChevronRight, faSearch } from '@fortawesome/free-solid-svg-icons';
88

99
function ProjectCard({ project, index }) {
1010
const { title, image, summary, description, date, tag } = project.metadata || {};
@@ -73,6 +73,7 @@ function ProjectCard({ project, index }) {
7373

7474
export default function ProjectsClient({ projects = [] }) {
7575
const [activeTag, setActiveTag] = useState('');
76+
const [searchQuery, setSearchQuery] = useState('');
7677

7778
const allTags = useMemo(() => {
7879
const tagsSet = new Set();
@@ -85,13 +86,31 @@ export default function ProjectsClient({ projects = [] }) {
8586
}, [projects]);
8687

8788
const filteredProjects = useMemo(() => {
88-
if (!activeTag) return projects;
89-
return projects.filter(proj => {
90-
if (!proj.metadata?.tag) return false;
91-
const projTags = proj.metadata.tag.split(',').map(t => t.trim());
92-
return projTags.includes(activeTag);
93-
});
94-
}, [projects, activeTag]);
89+
let result = projects;
90+
91+
// Filter by search query
92+
if (searchQuery.trim()) {
93+
const query = searchQuery.toLowerCase();
94+
result = result.filter(proj => {
95+
const title = proj.metadata?.title?.toLowerCase() || '';
96+
const summary = proj.metadata?.summary?.toLowerCase() || '';
97+
const description = proj.metadata?.description?.toLowerCase() || '';
98+
const tags = proj.metadata?.tag?.toLowerCase() || '';
99+
return title.includes(query) || summary.includes(query) || description.includes(query) || tags.includes(query);
100+
});
101+
}
102+
103+
// Filter by tag
104+
if (activeTag) {
105+
result = result.filter(proj => {
106+
if (!proj.metadata?.tag) return false;
107+
const projTags = proj.metadata.tag.split(',').map(t => t.trim());
108+
return projTags.includes(activeTag);
109+
});
110+
}
111+
112+
return result;
113+
}, [projects, activeTag, searchQuery]);
95114

96115
const sortedProjects = useMemo(() => {
97116
return [...filteredProjects].sort((a, b) => {
@@ -101,6 +120,13 @@ export default function ProjectsClient({ projects = [] }) {
101120
});
102121
}, [filteredProjects]);
103122

123+
const clearFilters = () => {
124+
setActiveTag('');
125+
setSearchQuery('');
126+
};
127+
128+
const hasActiveFilters = activeTag || searchQuery.trim();
129+
104130
return (
105131
<div className="container mx-auto px-4 sm:px-6 lg:px-8 py-12">
106132
<motion.div
@@ -119,6 +145,34 @@ export default function ProjectsClient({ projects = [] }) {
119145
Explore the projects I've been working on
120146
</p>
121147

148+
{/* Search Input */}
149+
<div className="max-w-md mx-auto mb-6">
150+
<div className="relative">
151+
<FontAwesomeIcon
152+
icon={faSearch}
153+
className="absolute left-4 top-1/2 -translate-y-1/2 text-light-text-secondary dark:text-muted-steel"
154+
/>
155+
<input
156+
type="text"
157+
value={searchQuery}
158+
onChange={(e) => setSearchQuery(e.target.value)}
159+
placeholder="Search projects by title, description, or technology..."
160+
className="w-full pl-11 pr-4 py-3 rounded-lg bg-light-surface dark:bg-midnight-steel/50 border border-light-border dark:border-slate-700/50 text-light-text dark:text-ghost-white placeholder-light-text-secondary dark:placeholder-muted-steel focus:outline-none focus:ring-2 focus:ring-neural-blue focus:border-transparent transition-all"
161+
aria-label="Search projects"
162+
/>
163+
{searchQuery && (
164+
<button
165+
onClick={() => setSearchQuery('')}
166+
className="absolute right-3 top-1/2 -translate-y-1/2 p-1 text-light-text-secondary dark:text-muted-steel hover:text-light-text dark:hover:text-ghost-white transition-colors"
167+
aria-label="Clear search"
168+
>
169+
<FontAwesomeIcon icon={faTimes} className="w-4 h-4" />
170+
</button>
171+
)}
172+
</div>
173+
</div>
174+
175+
{/* Tag Filters */}
122176
{allTags.length > 0 && (
123177
<div className="flex flex-wrap justify-center gap-2">
124178
{allTags.map(tag => (
@@ -136,19 +190,26 @@ export default function ProjectsClient({ projects = [] }) {
136190
{tag}
137191
</motion.button>
138192
))}
139-
{activeTag && (
193+
{hasActiveFilters && (
140194
<motion.button
141-
onClick={() => setActiveTag('')}
195+
onClick={clearFilters}
142196
className="px-4 py-2 rounded-lg text-sm font-medium bg-red-500/10 text-red-500 border border-red-500/20 hover:bg-red-500/20 transition-colors"
143197
whileHover={{ scale: 1.02 }}
144198
whileTap={{ scale: 0.98 }}
145199
>
146200
<FontAwesomeIcon icon={faTimes} className="mr-2" />
147-
Clear
201+
Clear All
148202
</motion.button>
149203
)}
150204
</div>
151205
)}
206+
207+
{/* Results count */}
208+
{hasActiveFilters && (
209+
<p className="text-center mt-4 text-sm text-light-text-secondary dark:text-muted-steel">
210+
Showing {sortedProjects.length} of {projects.length} projects
211+
</p>
212+
)}
152213
</motion.div>
153214

154215
<AnimatePresence mode="wait">

app/robots.js

Lines changed: 0 additions & 12 deletions
This file was deleted.

app/sitemap.js

Lines changed: 0 additions & 53 deletions
This file was deleted.

components/layout/Footer.jsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ export default function Footer() {
2020
{ name: 'Projects', path: '/projects' },
2121
{ name: 'Blog', path: '/blog' },
2222
{ name: 'About', path: '/about' },
23+
{ name: 'Contact', path: '/contact' },
2324
];
2425

2526
return (

0 commit comments

Comments
 (0)