feat(projects): update showcase and support dark thumbnails

This commit is contained in:
2026-09-06 23:44:20 +03:30
parent 6c860db2d9
commit 8be07aea32
10 changed files with 82 additions and 50 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 978 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

After

Width:  |  Height:  |  Size: 905 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

View File

@@ -0,0 +1,45 @@
'use client';
import Image from 'next/image';
import { useState, type FC } from 'react';
import { useTheme } from '@/context/ThemeContext';
interface ProjectThumbnailProps {
image: string;
title: string;
priority?: boolean;
className?: string;
}
const getDarkImagePath = (image: string) =>
image.replace(/(\.[^./]+)$/, '-dark$1');
export const ProjectThumbnail: FC<ProjectThumbnailProps> = ({
image,
title,
priority = false,
className,
}) => {
const { theme } = useTheme();
const darkImage = getDarkImagePath(image);
const [unavailableDarkImage, setUnavailableDarkImage] = useState<string | null>(
null,
);
const useDarkImage = theme === 'dark' && unavailableDarkImage !== darkImage;
return (
<Image
key={useDarkImage ? darkImage : image}
src={useDarkImage ? darkImage : image}
alt={title}
fill
sizes="(max-width: 650px) 100vw, (max-width: 1100px) 100vw, 360px"
priority={priority}
draggable={false}
className={className}
onError={() => {
if (useDarkImage) setUnavailableDarkImage(darkImage);
}}
/>
);
};

View File

@@ -1,9 +1,9 @@
'use client'; 'use client';
import React, { useEffect, useRef, useState } from 'react'; import React, { useEffect, useRef, useState } from 'react';
import Image from 'next/image';
import { ArrowLeft, ArrowRight, ArrowUpRight } from 'lucide-react'; import { ArrowLeft, ArrowRight, ArrowUpRight } from 'lucide-react';
import { projects } from '@/data/projects'; import { projects } from '@/data/projects';
import { ProjectThumbnail } from './ProjectThumbnail';
import styles from './ProjectsSection.module.css'; import styles from './ProjectsSection.module.css';
export const ProjectsSection: React.FC = () => { export const ProjectsSection: React.FC = () => {
@@ -255,13 +255,10 @@ export const ProjectsSection: React.FC = () => {
{projects.map((project) => ( {projects.map((project) => (
<article key={project.id} className={styles.card} aria-label={project.title}> <article key={project.id} className={styles.card} aria-label={project.title}>
<div className={styles.mediaContainer}> <div className={styles.mediaContainer}>
<Image <ProjectThumbnail
src={project.image} image={project.image}
alt={project.title} title={project.title}
fill
sizes="(max-width: 650px) 100vw, (max-width: 1100px) 100vw, 360px"
priority={project.id === 'qlockify'} priority={project.id === 'qlockify'}
draggable={false}
className={styles.projectImage} className={styles.projectImage}
/> />
</div> </div>
@@ -269,12 +266,14 @@ export const ProjectsSection: React.FC = () => {
<div className={styles.cardBody}> <div className={styles.cardBody}>
<div className={styles.cardTitleRow}> <div className={styles.cardTitleRow}>
<h3 className={styles.cardTitle}>{project.title}</h3> <h3 className={styles.cardTitle}>{project.title}</h3>
<ArrowUpRight {project.link && (
size={19} <ArrowUpRight
strokeWidth={2.5} size={19}
className={styles.titleLinkIcon} strokeWidth={2.5}
aria-hidden="true" className={styles.titleLinkIcon}
/> aria-hidden="true"
/>
)}
</div> </div>
<div className={styles.cardDate}>{project.date}</div> <div className={styles.cardDate}>{project.date}</div>

View File

@@ -12,7 +12,7 @@ export const projects: Project[] = [
{ {
id: 'qlockify', id: 'qlockify',
title: 'Qlockify', title: 'Qlockify',
link: '#', link: 'https://qlockfiy.ir',
date: 'FEB 2026 - PRESENT', date: 'FEB 2026 - PRESENT',
description: description:
'Built a production-ready time tracking platform for teams and freelancers to manage workspaces, projects, billable hours, and reports in one place. Users can track their work time, organize it by project, manage access inside each workspace, generate reports for admins and clients, and export insights.', 'Built a production-ready time tracking platform for teams and freelancers to manage workspaces, projects, billable hours, and reports in one place. Users can track their work time, organize it by project, manage access inside each workspace, generate reports for admins and clients, and export insights.',
@@ -22,7 +22,7 @@ export const projects: Project[] = [
{ {
id: 'guilan-ace', id: 'guilan-ace',
title: 'Guilan ACE', title: 'Guilan ACE',
link: '#', link: 'https://east-guilan-ce.ir/',
date: 'JUL 2025 - PRESENT', date: 'JUL 2025 - PRESENT',
description: description:
'Developed the Scientific Association of Computer Engineering website for event management and article publishing, helping members organize activities and users register for events, read blog posts, and share ideas through comments.', 'Developed the Scientific Association of Computer Engineering website for event management and article publishing, helping members organize activities and users register for events, read blog posts, and share ideas through comments.',
@@ -32,7 +32,7 @@ export const projects: Project[] = [
{ {
id: 'zarinpal', id: 'zarinpal',
title: 'Zarinpal Analytic Dashboard', title: 'Zarinpal Analytic Dashboard',
link: '#', link: 'https://zarinpal.amiirkhl.ir/',
date: 'AUG 2026', date: 'AUG 2026',
description: description:
"AI-based dashboard for analytics insight on merchant's transactions in their different terminals and suggesting profitable campaigns to hold in special occasions based on their historical transactions and upcoming events in Persian calendar and international events that affect Iranian markets.", "AI-based dashboard for analytics insight on merchant's transactions in their different terminals and suggesting profitable campaigns to hold in special occasions based on their historical transactions and upcoming events in Persian calendar and international events that affect Iranian markets.",
@@ -42,51 +42,39 @@ export const projects: Project[] = [
{ {
id: 'iran-implant', id: 'iran-implant',
title: 'Iran Implant', title: 'Iran Implant',
link: '#', link: 'http://impinternal.com/',
date: 'NOV 2025 - MAR 2026', date: 'NOV 2025 - MAR 2026',
description: description:
'Developed the backend of a comprehensive dental clinic and implant center management system for appointment scheduling, inventory tracking, patient records, and billing, enabling clinic staff to manage daily clinic operations seamlessly from a centralized platform.', 'Developed the backend of Iran Implant, an internal management system for a specialized dental clinic, enabling staff to manage appointments, treatment rooms, payments, prescriptions, and laboratory orders through a centralized platform.',
tags: ['Django', 'Nginx', 'PostgreSQL', 'Docker'], tags: ['Django', 'Nginx', 'Docker', 'Label'],
image: '/images/project-iran-implant.png', image: '/images/project-iran-implant.png',
}, },
{ {
id: 'agentic-workflow-engine', id: 'interanet-solutions',
title: 'Agentic Workflow Engine', title: 'Interanet Solutions',
link: '#', date: 'MAR - JUN 2026',
date: 'JAN 2026 - FEB 2026',
description: description:
'Engineered an autonomous multi-agent task execution system utilizing advanced LLM reasoning, background task scheduling, and structured tool calling for end-to-end automated code refactoring and project analysis.', 'Developed a collection of self-hosted infrastructure and deployment solutions for environments with restricted international Internet access, including automated server bootstrap scripts and deployments for tools such as GitLab/Gitea (GitHub), Penpot (Figma), and Jitsi (Google Meet).',
tags: ['Python', 'FastAPI', 'Redis', 'Docker'], tags: ['Bash', 'Caddy', 'Docker', 'DevOps'],
image: '/images/project-qlockify.png', image: '/images/project-interanet-solutions.png',
}, },
{ {
id: 'rapidapi-scraper-suite', id: 'postgresql-job-queue',
title: 'RapidAPI Scraping Suite', title: 'PostgreSQL Job Queue',
link: '#', date: 'JUN 2026',
date: 'OCT 2025 - NOV 2025',
description: description:
'Architected resilient web scraping and data extraction microservices deployed on RapidAPI with automated proxy rotation, anti-bot bypass techniques, and high-concurrency async pipelines.', 'Built a lightweight background job system for running slow or unreliable tasks outside the main request flow. The project focuses on making job execution safe and predictable: multiple workers can pick up work at the same time without blocking each other, failed jobs are retried safely, and work remains recoverable.',
tags: ['FastAPI', 'Caddy', 'Locust', 'PyTest'], tags: ['Go', 'SvelteKit', 'Docker'],
image: '/images/project-guilan-ace.png', image: '/images/project-postgresql-job-queue.png',
}, },
{ {
id: 'sharif-contest-platform', id: 'subsource',
title: 'Tech Olympics Problem Engine', title: 'Subsource',
link: '#', link: 'http://subsource.net/',
date: 'DEC 2025 - JAN 2026', date: 'MAY 2024',
description: description:
'Designed algorithmic and implementation problem suites and automated grading sandbox test runners for Python and Django challenges in national programming competitions.', 'Designed the end-to-end UI/UX for SubSource, a subtitle discovery and download platform serving movie and TV audiences across multiple languages. Focused on simplifying content discovery, organizing information-heavy interfaces, and creating intuitive user flows across the product.',
tags: ['Python', 'Django', 'PostgreSQL', 'Docker'], tags: ['Product Design', 'Figma', 'Prototyping'],
image: '/images/project-zarinpal.png', image: '/images/project-subsource.png',
},
{
id: 'home-lab-infra',
title: 'Private Cloud & Home Lab',
link: '#',
date: '2024 - PRESENT',
description:
'Designed and deployed private on-premise infrastructure hosting containerized services, databases, CI/CD runners, reverse proxies, and self-hosted AI workloads with proactive Grafana/Prometheus monitoring.',
tags: ['Linux', 'Docker', 'Nginx', 'Grafana'],
image: '/images/project-iran-implant.png',
}, },
]; ];