Skip to content

Commit 48e2a4b

Browse files
committed
refactor: projects.
1 parent 6dcc88a commit 48e2a4b

File tree

6 files changed

+229
-136
lines changed

6 files changed

+229
-136
lines changed

public/roadmap/roadmap.piml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,3 +49,14 @@
4949
(priority) High
5050
(created_at) 2025-11-29T18:00:00+03:00
5151
(notes) Research Gantt chart libraries.
52+
53+
> (issues)
54+
(id) FEZ-5
55+
(title) Refactor Projects Page
56+
(description) Better styles & UI/UX for project related pages and components.
57+
(category) UI/UX
58+
(status) Completed
59+
(priority) High
60+
(created_at) 2025-11-28T18:00:00+03:00
61+
(due_date) 2025-11-30T00:00:00+03:00
62+
(notes) Futuristic Cyan Theme.

public/timeline/timeline.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,12 @@
11
[
2+
{
3+
"date": "2025-11-29",
4+
"title": "Projects Overhaul Refactor",
5+
"description": "Modernized Projects section with a futuristic cyan theme, enhanced animations, and improved metadata cards.",
6+
"type": "refactor",
7+
"icon": "SparkleIcon",
8+
"link": "/#/projects"
9+
},
210
{
311
"date": "2025-11-29",
412
"title": "Fezzilla: Issue Tracking Roadmap",

src/components/ProjectCard.js

Lines changed: 88 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import React, { useState, useEffect, useRef, useCallback } from 'react';
2-
import { Link, useLocation } from 'react-router-dom'; // Import useLocation
3-
import { FaExternalLinkAlt } from 'react-icons/fa';
2+
import { Link, useLocation } from 'react-router-dom';
3+
import { FaExternalLinkAlt, FaChevronRight } from 'react-icons/fa';
44
import Dot from './Dot';
5-
import { useAnimation } from '../context/AnimationContext'; // Import useAnimation
5+
import { useAnimation } from '../context/AnimationContext';
66

77
const ProjectCard = ({ project, size = 1 }) => {
88
const colSpanClass =
@@ -15,8 +15,8 @@ const ProjectCard = ({ project, size = 1 }) => {
1515
isAnimationEnabled,
1616
showAnimationsHomepage,
1717
showAnimationsInnerPages,
18-
} = useAnimation(); // Use the animation context
19-
const location = useLocation(); // Get current location
18+
} = useAnimation();
19+
const location = useLocation();
2020

2121
const handleAnimationEnd = useCallback((id) => {
2222
setDots((prevDots) => prevDots.filter((dot) => dot.id !== id));
@@ -29,23 +29,23 @@ const ProjectCard = ({ project, size = 1 }) => {
2929
((location.pathname === '/' && showAnimationsHomepage) ||
3030
(location.pathname !== '/' && showAnimationsInnerPages))
3131
) {
32-
// Only spawn dots if animations are enabled and on homepage or everywhere
3332
const spawnDot = () => {
34-
if (cardRef.current && dots.length < 10) {
33+
if (cardRef.current && dots.length < 8) {
3534
const cardRect = cardRef.current.getBoundingClientRect();
3635
const newDot = {
3736
id: dotIdRef.current++,
38-
size: Math.floor(Math.random() * 4) + 5, // Size between 5 and 8
39-
color: `hsl(0, ${Math.floor(Math.random() * 30) + 70}%, ${Math.floor(Math.random() * 20) + 60}%)`, // Tones of red
37+
size: Math.floor(Math.random() * 3) + 2,
38+
// Cyan/Teal/Greenish Matrix tones
39+
color: `hsl(${Math.floor(Math.random() * 60) + 160}, ${Math.floor(Math.random() * 50) + 50}%, ${Math.floor(Math.random() * 30) + 60}%)`,
4040
initialX: Math.random() * cardRect.width,
41-
initialY: -5, // Start slightly off-screen top
42-
animationDuration: Math.random() * 3 + 2, // Duration between 2 and 5 seconds
41+
initialY: -10, // Start slightly off-screen top
42+
animationDuration: Math.random() * 4 + 3,
4343
};
4444
setDots((prevDots) => [...prevDots, newDot]);
4545
}
4646
};
4747

48-
interval = setInterval(spawnDot, 500); // Spawn a new dot every 0.5 seconds
48+
interval = setInterval(spawnDot, 600);
4949
}
5050

5151
return () => clearInterval(interval);
@@ -60,40 +60,90 @@ const ProjectCard = ({ project, size = 1 }) => {
6060
return (
6161
<div
6262
ref={cardRef}
63-
className={`block bg-gray-500/10 p-6 rounded-lg shadow-lg hover:bg-gray-500/20 transition-colors border border-gray-700/50 cursor-pointer flex flex-col relative overflow-hidden ${colSpanClass}`}
63+
className={`group relative flex flex-col overflow-hidden rounded-xl bg-gray-900/90 border border-gray-800 hover:border-cyan-500/50 transition-all duration-500 ease-out hover:shadow-[0_0_20px_-5px_rgba(6,182,212,0.4)] hover:-translate-y-1 ${colSpanClass}`}
6464
>
65-
{dots.map((dot) => (
66-
<Dot
67-
key={dot.id}
68-
id={dot.id}
69-
size={dot.size}
70-
color={dot.color}
71-
initialX={dot.initialX}
72-
initialY={dot.initialY}
73-
animationDuration={dot.animationDuration}
74-
onAnimationEnd={handleAnimationEnd}
75-
/>
76-
))}
65+
{/* Background Grid Effect */}
66+
<div className="absolute inset-0 z-0 opacity-20 pointer-events-none bg-[linear-gradient(to_right,#4f4f4f2e_1px,transparent_1px),linear-gradient(to_bottom,#4f4f4f2e_1px,transparent_1px)] bg-[size:14px_24px] [mask-image:radial-gradient(ellipse_60%_50%_at_50%_0%,#000_70%,transparent_100%)]" />
67+
68+
{/* Tech Accents - Top Right */}
69+
<div className="absolute top-0 right-0 p-2 z-20">
70+
<div className="flex gap-1">
71+
<div className="w-1 h-1 rounded-full bg-gray-700 group-hover:bg-cyan-500/50 transition-colors duration-300 delay-75"></div>
72+
<div className="w-1 h-1 rounded-full bg-gray-700 group-hover:bg-cyan-500/50 transition-colors duration-300 delay-100"></div>
73+
<div className="w-1 h-1 rounded-full bg-gray-700 group-hover:bg-cyan-500/50 transition-colors duration-300 delay-150"></div>
74+
</div>
75+
</div>
76+
77+
{/* Dots Layer */}
78+
<div className="absolute inset-0 pointer-events-none z-0 overflow-hidden rounded-xl">
79+
{dots.map((dot) => (
80+
<Dot
81+
key={dot.id}
82+
id={dot.id}
83+
size={dot.size}
84+
color={dot.color}
85+
initialX={dot.initialX}
86+
initialY={dot.initialY}
87+
animationDuration={dot.animationDuration}
88+
onAnimationEnd={handleAnimationEnd}
89+
/>
90+
))}
91+
</div>
92+
93+
{/* Watermark */}
94+
<div className="absolute bottom-0 right-0 p-4 pointer-events-none z-0 opacity-5 group-hover:opacity-10 transition-opacity duration-500">
95+
<span className="text-6xl font-black font-mono text-cyan-500 tracking-tighter select-none">
96+
FCX
97+
</span>
98+
</div>
99+
100+
{/* Content */}
77101
<Link
78102
to={`/projects/${project.slug}`}
79-
className="flex flex-col flex-grow relative z-10"
103+
className="flex flex-col flex-grow relative z-10 p-6"
80104
>
81-
<h3 className="font-arvo text-xl text-orange-400">{project.title}</h3>
82-
<hr className="border-gray-700 -mx-6 mb-4 mt-4" />
83-
<p className="text-gray-400 flex-grow">{project.description}</p>
105+
<div className="flex justify-between items-start mb-3">
106+
<h3 className="font-mono text-xl font-bold text-gray-100 group-hover:text-cyan-400 transition-colors tracking-tight">
107+
{project.title}
108+
</h3>
109+
</div>
110+
111+
<div className="h-px w-12 bg-gradient-to-r from-cyan-500 to-transparent mb-4 group-hover:w-full transition-all duration-500 ease-out" />
112+
113+
<p className="text-gray-400 text-sm leading-relaxed flex-grow font-sans">
114+
{project.description}
115+
</p>
116+
117+
<div className="mt-6 flex items-center justify-between">
118+
<div className="flex items-center gap-2">
119+
<span className="text-[10px] font-mono text-cyan-500/70 border border-cyan-900/30 bg-cyan-950/30 px-2 py-1 rounded uppercase tracking-wider">
120+
ID: {project.slug.split('-')[0].toUpperCase()}
121+
</span>
122+
</div>
123+
<FaChevronRight
124+
className="text-gray-600 group-hover:text-cyan-400 transform group-hover:translate-x-1 transition-all duration-300"
125+
size={14}
126+
/>
127+
</div>
84128
</Link>
129+
130+
{/* External Link footer if exists */}
85131
{project.link && (
86-
<a
87-
href={project.link}
88-
target="_blank"
89-
rel="noopener noreferrer"
90-
className="text-red-500 hover:text-red-300 transition-colors mt-auto flex items-center relative z-10"
91-
>
92-
View Project <FaExternalLinkAlt className="ml-1" size={12} />
93-
</a>
132+
<div className="relative z-10 px-6 pb-4">
133+
<a
134+
href={project.link}
135+
target="_blank"
136+
rel="noopener noreferrer"
137+
className="inline-flex items-center gap-2 text-[10px] font-bold text-gray-500 hover:text-cyan-400 uppercase tracking-widest transition-colors border-t border-gray-800 pt-3 w-full"
138+
>
139+
<span className="w-1.5 h-1.5 bg-current rounded-full animate-pulse" />
140+
Live System
141+
<FaExternalLinkAlt size={9} className="ml-auto" />
142+
</a>
143+
</div>
94144
)}
95145
</div>
96146
);
97147
};
98148

99-
export default ProjectCard;
149+
export default ProjectCard;

src/components/metadata-cards/ProjectMetadata.js

Lines changed: 36 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,70 @@
11
import React from 'react';
22
import Label from '../Label';
3-
import { FaExternalLinkAlt } from 'react-icons/fa';
3+
import {FaExternalLinkAlt} from 'react-icons/fa';
44

5-
const ProjectMetadata = ({ project }) => {
5+
const ProjectMetadata = ({project}) => {
66
if (!project) {
77
return null;
88
}
99

1010
return (
11-
<aside className="sticky top-24">
12-
<div className="p-6 bg-gray-800/50 rounded-lg border border-gray-700/50">
13-
<h3 className="text-lg font-semibold text-gray-100 mb-4 border-b pb-2 border-gray-500">
14-
About Project
11+
<aside>
12+
<div
13+
className="p-6 bg-gray-900/80 backdrop-blur-md rounded-xl border border-gray-800 shadow-lg relative overflow-hidden group">
14+
{/* Decor element */}
15+
<div className="absolute top-0 right-0 p-3 opacity-50">
16+
<div className="flex gap-1">
17+
<div className="w-1 h-1 bg-cyan-500 rounded-full"></div>
18+
<div className="w-1 h-1 bg-gray-600 rounded-full"></div>
19+
</div>
20+
</div>
21+
22+
<h3
23+
className="text-sm font-mono font-bold text-cyan-400 mb-6 border-b border-gray-800 pb-3 uppercase tracking-widest flex items-center gap-2">
24+
<span className="w-2 h-2 bg-cyan-500 rounded-sm"></span>
25+
Project Data
1526
</h3>
16-
<div className="space-y-4">
27+
28+
<div className="space-y-6">
1729
<div>
18-
<Label>Title</Label>
19-
<p className="text-gray-300 ml-1 mt-1">{project.title}</p>
30+
<Label className="text-gray-500 text-xs uppercase tracking-wider font-mono">Title</Label>
31+
<p className="text-gray-200 mt-1 font-medium">{project.title}</p>
2032
</div>
33+
2134
{project.link && (
2235
<div>
23-
<Label>Link</Label>
24-
<p className="text-gray-300 ml-1 mt-1">
36+
<Label className="text-gray-500 text-xs uppercase tracking-wider font-mono">Deployment</Label>
37+
<p className="text-gray-300 mt-1">
2538
<a
2639
href={project.link}
2740
target="_blank"
2841
rel="noopener noreferrer"
29-
className="text-amber-500 hover:text-amber-400 flex items-center"
42+
className="text-cyan-400 hover:text-cyan-300 flex items-center gap-2 group-hover:translate-x-1 transition-transform duration-300"
3043
>
31-
View Project <FaExternalLinkAlt className="ml-1" size={12} />
44+
View System <FaExternalLinkAlt size={12}/>
3245
</a>
3346
</p>
3447
</div>
3548
)}
49+
3650
{project.pinned && (
3751
<div>
38-
<Label>Status</Label>
39-
<p className="text-gray-300 ml-1 mt-1">Pinned</p>
52+
<Label className="text-gray-500 text-xs uppercase tracking-wider font-mono">Status</Label>
53+
<div className="mt-2 flex items-center gap-2">
54+
<span className="w-2 h-2 bg-green-500 rounded-full animate-pulse"></span>
55+
<p className="text-green-400 font-mono text-sm">PINNED / ACTIVE</p>
56+
</div>
4057
</div>
4158
)}
59+
4260
{project.technologies && project.technologies.length > 0 && (
4361
<div>
44-
<Label>Technologies</Label>
45-
<div className="flex flex-wrap gap-2 mt-2">
62+
<Label className="text-gray-500 text-xs uppercase tracking-wider font-mono">Stack</Label>
63+
<div className="flex flex-wrap gap-2 mt-3">
4664
{project.technologies.map((tech) => (
4765
<span
4866
key={tech}
49-
className="bg-primary-400/10 text-primary-400 text-xs font-medium px-2.5 py-1 rounded-full"
67+
className="bg-gray-800 text-cyan-300 border border-cyan-900/50 text-xs font-mono px-2.5 py-1 rounded hover:bg-cyan-900/20 transition-colors cursor-default"
5068
>
5169
{tech}
5270
</span>

0 commit comments

Comments
 (0)