feat: add route-backed project case studies
Some checks failed
Test and deploy / Verify production build (push) Has been cancelled
Test and deploy / Deploy production (push) Has been cancelled

This commit is contained in:
2026-09-09 22:00:57 +03:30
parent b3cfcfe057
commit 24cd86ede7
25 changed files with 1892 additions and 65 deletions

View File

@@ -1,100 +1,335 @@
import type { Locale, Localized } from '@/i18n/content';
import { portfolioImage, type PortfolioImage } from '@/types/media';
export type CaseStudyDiscipline = 'product' | 'engineering' | 'design';
export interface CaseStudyDecision {
discipline: CaseStudyDiscipline;
title: string;
description: string;
}
export interface CaseStudyGalleryItem {
image: PortfolioImage;
alt: string;
caption: string;
}
export interface ProjectCaseStudy {
introduction: string;
role: string;
targetUsers: string;
status: string;
challenge: string;
ownership: string[];
decisions: CaseStudyDecision[];
outcomes: string[];
skills: string[];
tools: string[];
team?: string;
constraints?: string[];
metrics?: Array<{ value: string; label: string }>;
lessons?: string[];
gallery: CaseStudyGalleryItem[];
}
export interface Project {
id: string;
title: string;
link?: string;
liveUrl?: string;
date: string;
description: string;
tags: string[];
image: PortfolioImage;
caseStudy: ProjectCaseStudy;
}
type ProjectCopy = Pick<Project, 'title' | 'date' | 'description'>;
type GalleryCopy = Pick<CaseStudyGalleryItem, 'alt' | 'caption'>;
type CaseStudyCopy = Omit<ProjectCaseStudy, 'gallery'>;
type ProjectCopy = Pick<Project, 'title' | 'date' | 'description'> & {
caseStudy: CaseStudyCopy;
};
interface ProjectDefinition extends Omit<Project, keyof ProjectCopy> {
interface GalleryDefinition {
image: PortfolioImage;
content: Localized<GalleryCopy>;
}
interface ProjectDefinition
extends Omit<Project, keyof ProjectCopy | 'caseStudy'> {
gallery: GalleryDefinition[];
content: Localized<ProjectCopy>;
}
const projectDefinitions: ProjectDefinition[] = [
{
id: 'qlockify',
link: 'https://qlockfiy.ir',
liveUrl: 'https://qlockfiy.ir',
tags: ['Django', 'React', 'Docker', 'UI/UX Design'],
image: portfolioImage('/images/project-qlockify.png', 359, 200),
gallery: [
{
image: portfolioImage('/images/project-qlockify.png', 359, 200),
content: {
en: { alt: 'Qlockify time tracking product in light mode', caption: 'The public Qlockify experience in light mode.' },
fa: { alt: 'محصول ثبت زمان Qlockify در حالت روشن', caption: 'نمای عمومی Qlockify در حالت روشن.' },
},
},
{
image: portfolioImage('/images/project-qlockify-dark.png', 358, 199),
content: {
en: { alt: 'Qlockify time tracking product in dark mode', caption: 'The same product experience in dark mode.' },
fa: { alt: 'محصول ثبت زمان Qlockify در حالت تیره', caption: 'همان تجربه محصول در حالت تیره.' },
},
},
],
content: {
en: {
title: 'Qlockify',
date: 'FEB 2026 - PRESENT',
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.',
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.',
caseStudy: {
introduction: 'A single workspace for recording work, organizing billable time, managing access, and turning tracked activity into useful reports.',
role: 'Full-stack developer and UI/UX designer',
targetUsers: 'Teams, freelancers, workspace administrators, and clients',
status: 'In progress',
challenge: 'Time, project context, access control, and reporting are often split across separate workflows. Qlockify brings those jobs into one product while preserving the needs of team members, administrators, and clients.',
ownership: [
'Built time tracking and project organization workflows.',
'Implemented workspace access management.',
'Created reporting and export flows for administrators and clients.',
'Designed the interface across the product’s core workflows.',
],
decisions: [
{ discipline: 'product', title: 'Workspace-centered organization', description: 'Kept projects, people, tracked time, and reports inside a shared workspace model so related work stays connected.' },
{ discipline: 'engineering', title: 'Separated application responsibilities', description: 'Used Django for the backend, React for the interface, and Docker for repeatable deployment.' },
{ discipline: 'design', title: 'Role-aware reporting', description: 'Organized reporting around the different information needs of team members, administrators, and clients.' },
],
outcomes: [
'Consolidated time tracking, project organization, access management, reporting, and exports into one platform.',
'Made tracked work reusable for internal administration and client-facing reporting.',
],
skills: ['Product engineering', 'Backend development', 'Frontend development', 'UI/UX design'],
tools: ['Django', 'React', 'Docker'],
},
},
fa: {
title: 'Qlockify',
date: 'بهمن ۱۴۰۴ تا امروز',
description:
'یک پلتفرم آماده استفاده در محیط واقعی برای ثبت زمان تیم‌ها و فریلنسرها ساختم تا فضاهای کاری، پروژه‌ها، ساعت‌های قابل‌صورتحساب و گزارش‌ها را یک‌جا مدیریت کنند. کاربران می‌توانند زمان کارشان را ثبت و بر اساس پروژه دسته‌بندی کنند، دسترسی‌های هر فضای کاری را مدیریت کنند، برای مدیران و مشتریان گزارش بسازند و خروجی بگیرند.',
description: 'یک پلتفرم آماده استفاده در محیط واقعی برای ثبت زمان تیم‌ها و فریلنسرها ساختم تا فضاهای کاری، پروژه‌ها، ساعت‌های قابل‌صورتحساب و گزارش‌ها را یک‌جا مدیریت کنند. کاربران می‌توانند زمان کارشان را ثبت و بر اساس پروژه دسته‌بندی کنند، دسترسی‌های هر فضای کاری را مدیریت کنند، برای مدیران و مشتریان گزارش بسازند و خروجی بگیرند.',
caseStudy: {
introduction: 'یک فضای کاری یکپارچه برای ثبت فعالیت، سامان‌دهی زمان قابل‌صورتحساب، مدیریت دسترسی و تبدیل داده‌های زمانی به گزارش‌های کاربردی.',
role: 'توسعه‌دهنده Full-stack و طراح UI/UX',
targetUsers: 'تیم‌ها، فریلنسرها، مدیران فضای کاری و مشتریان',
status: 'در حال توسعه',
challenge: 'اطلاعات زمان، پروژه، دسترسی و گزارش معمولا در چند فرایند جدا قرار می‌گیرند. Qlockify این نیازها را در یک محصول جمع می‌کند و تفاوت نیاز اعضای تیم، مدیر و مشتری را حفظ می‌کند.',
ownership: [
'تجربه ثبت زمان و سامان‌دهی فعالیت‌ها بر اساس پروژه را توسعه دادم.',
'مدیریت دسترسی افراد در هر فضای کاری را پیاده‌سازی کردم.',
'فرایند گزارش‌گیری و دریافت خروجی برای مدیران و مشتریان را ساختم.',
'رابط کاربری مسیرهای اصلی محصول را طراحی کردم.',
],
decisions: [
{ discipline: 'product', title: 'سامان‌دهی حول فضای کاری', description: 'پروژه‌ها، افراد، زمان‌های ثبت‌شده و گزارش‌ها در یک مدل فضای کاری قرار گرفتند تا اطلاعات مرتبط از هم جدا نشوند.' },
{ discipline: 'engineering', title: 'تفکیک مسئولیت‌های برنامه', description: 'برای بخش سمت سرور از Django، برای رابط از React و برای استقرار تکرارپذیر از Docker استفاده شد.' },
{ discipline: 'design', title: 'گزارش متناسب با نقش کاربران', description: 'ساختار گزارش‌ها بر اساس نیاز متفاوت اعضای تیم، مدیران و مشتریان شکل گرفت.' },
],
outcomes: [
'ثبت زمان، سامان‌دهی پروژه، مدیریت دسترسی، گزارش و خروجی در یک پلتفرم یکپارچه شد.',
'داده‌های ثبت‌شده هم برای مدیریت داخلی و هم برای گزارش به مشتری قابل‌استفاده شدند.',
],
skills: ['مهندسی محصول', 'توسعه Backend', 'توسعه Frontend', 'طراحی UI/UX'],
tools: ['Django', 'React', 'Docker'],
},
},
},
},
{
id: 'guilan-ace',
link: 'https://east-guilan-ce.ir/',
liveUrl: 'https://east-guilan-ce.ir/',
tags: ['Django Ninja', 'Next.js', 'Docker'],
image: portfolioImage('/images/project-guilan-ace.png', 359, 200),
gallery: [
{
image: portfolioImage('/images/project-guilan-ace.png', 359, 200),
content: {
en: { alt: 'Guilan ACE website in light mode', caption: 'The association website in light mode.' },
fa: { alt: 'وب‌سایت انجمن علمی مهندسی کامپیوتر گیلان در حالت روشن', caption: 'نمای وب‌سایت انجمن در حالت روشن.' },
},
},
{
image: portfolioImage('/images/project-guilan-ace-dark.png', 358, 199),
content: {
en: { alt: 'Guilan ACE website in dark mode', caption: 'The association website in dark mode.' },
fa: { alt: 'وب‌سایت انجمن علمی مهندسی کامپیوتر گیلان در حالت تیره', caption: 'نمای وب‌سایت انجمن در حالت تیره.' },
},
},
],
content: {
en: {
title: 'Guilan ACE',
date: 'JUL 2025 - PRESENT',
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.',
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.',
caseStudy: {
introduction: 'A shared digital home for the association’s events, articles, registrations, and community discussion.',
role: 'Full-stack developer',
targetUsers: 'Association members, students, event attendees, and readers',
status: 'In progress',
challenge: 'The association needed one place to publish knowledge and coordinate event activity for both organizers and participants.',
ownership: ['Developed event management and registration workflows.', 'Built article publishing and reading experiences.', 'Supported community participation through comments.'],
decisions: [
{ discipline: 'product', title: 'One home for activity and knowledge', description: 'Combined events and publishing so visitors can discover upcoming activity and reusable educational content.' },
{ discipline: 'engineering', title: 'API and interface separation', description: 'Used Django Ninja for application APIs, Next.js for the web experience, and Docker for deployment.' },
{ discipline: 'design', title: 'Clear participant journeys', description: 'Kept registration, reading, and commenting understandable for visitors who are not site administrators.' },
],
outcomes: ['Gave members a centralized way to organize events and publish articles.', 'Enabled visitors to register, read, and participate from the same website.'],
skills: ['Full-stack development', 'Information architecture', 'Community product design'],
tools: ['Django Ninja', 'Next.js', 'Docker'],
},
},
fa: {
title: 'انجمن علمی مهندسی کامپیوتر گیلان',
date: 'تیر ۱۴۰۴ تا امروز',
description:
'وب‌سایت انجمن علمی مهندسی کامپیوتر را برای مدیریت رویدادها و انتشار مقاله توسعه دادم. اعضا می‌توانند فعالیت‌ها را سامان دهند و کاربران هم برای رویدادها ثبت‌نام کنند، نوشته‌های وبلاگ را بخوانند و نظرشان را در بخش دیدگاه‌ها به اشتراک بگذارند.',
description: 'وب‌سایت انجمن علمی مهندسی کامپیوتر را برای مدیریت رویدادها و انتشار مقاله توسعه دادم. اعضا می‌توانند فعالیت‌ها را سامان دهند و کاربران هم برای رویدادها ثبت‌نام کنند، نوشته‌های وبلاگ را بخوانند و نظرشان را در بخش دیدگاه‌ها به اشتراک بگذارند.',
caseStudy: {
introduction: 'یک خانه دیجیتال مشترک برای رویدادها، مقاله‌ها، ثبت‌نام و گفت‌وگوی اعضای انجمن.',
role: 'توسعه‌دهنده Full-stack',
targetUsers: 'اعضای انجمن، دانشجویان، شرکت‌کنندگان رویداد و خوانندگان',
status: 'در حال توسعه',
challenge: 'انجمن به فضایی یکپارچه نیاز داشت تا هم دانش را منتشر کند و هم فعالیت‌های رویداد را برای برگزارکنندگان و شرکت‌کنندگان سامان دهد.',
ownership: ['فرایندهای مدیریت رویداد و ثبت‌نام را توسعه دادم.', 'تجربه انتشار و مطالعه مقاله‌ها را ساختم.', 'امکان مشارکت کاربران از طریق دیدگاه‌ها را فراهم کردم.'],
decisions: [
{ discipline: 'product', title: 'یک خانه برای فعالیت و دانش', description: 'رویدادها و انتشار مقاله کنار هم قرار گرفتند تا بازدیدکنندگان فعالیت‌های پیش رو و محتوای آموزشی ماندگار را پیدا کنند.' },
{ discipline: 'engineering', title: 'تفکیک API و رابط', description: 'برای APIهای برنامه از Django Ninja، برای تجربه وب از Next.js و برای استقرار از Docker استفاده شد.' },
{ discipline: 'design', title: 'مسیر روشن برای شرکت‌کنندگان', description: 'ثبت‌نام، مطالعه و ارسال دیدگاه برای کاربرانی که مدیر سایت نیستند، ساده و قابل‌فهم نگه داشته شد.' },
],
outcomes: ['اعضا به فضایی متمرکز برای مدیریت رویداد و انتشار مقاله دسترسی پیدا کردند.', 'بازدیدکنندگان توانستند در همان وب‌سایت ثبت‌نام کنند، بخوانند و مشارکت داشته باشند.'],
skills: ['توسعه Full-stack', 'معماری اطلاعات', 'طراحی محصول اجتماعی'],
tools: ['Django Ninja', 'Next.js', 'Docker'],
},
},
},
},
{
id: 'zarinpal',
link: 'https://zarinpal.amiirkhl.ir/',
liveUrl: 'https://zarinpal.amiirkhl.ir/',
tags: ['Go', 'SvelteKit', 'Docker'],
image: portfolioImage('/images/project-zarinpal.png', 359, 200),
gallery: [
{
image: portfolioImage('/images/project-zarinpal.png', 359, 200),
content: {
en: { alt: 'Zarinpal analytics dashboard in light mode', caption: 'Transaction analytics and campaign guidance in light mode.' },
fa: { alt: 'داشبورد تحلیلی زرین‌پال در حالت روشن', caption: 'نمای تحلیل تراکنش و پیشنهاد کمپین در حالت روشن.' },
},
},
{
image: portfolioImage('/images/project-zarinpal-dark.png', 358, 199),
content: {
en: { alt: 'Zarinpal analytics dashboard in dark mode', caption: 'The analytics experience in dark mode.' },
fa: { alt: 'داشبورد تحلیلی زرین‌پال در حالت تیره', caption: 'نمای تجربه تحلیلی در حالت تیره.' },
},
},
],
content: {
en: {
title: 'Zarinpal Analytic Dashboard',
date: 'AUG 2026',
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.",
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.",
caseStudy: {
introduction: 'An analytics dashboard that connects multi-terminal transaction history with timely campaign suggestions.',
role: 'Full-stack developer',
targetUsers: 'Merchants operating one or more payment terminals',
status: 'Completed',
challenge: 'Merchants need to understand activity across terminals and turn historical patterns into useful decisions around upcoming market occasions.',
ownership: ['Built analytics views for transaction activity across different terminals.', 'Connected historical transaction context to campaign suggestions.', 'Included Persian-calendar and international events that affect Iranian markets.'],
decisions: [
{ discipline: 'product', title: 'From reporting to action', description: 'Extended transaction insight into campaign suggestions so the dashboard supports decisions, not only observation.' },
{ discipline: 'engineering', title: 'Purpose-built service and interface', description: 'Used Go for application services, SvelteKit for the dashboard experience, and Docker for deployment.' },
{ discipline: 'design', title: 'Terminal-level context', description: 'Kept terminal differences visible so aggregate analytics do not hide where activity originated.' },
],
outcomes: ['Combined transaction analysis and occasion-aware campaign guidance in one dashboard.', 'Made historical activity across multiple terminals easier to review as decision context.'],
skills: ['Analytics product development', 'Backend development', 'Dashboard design'],
tools: ['Go', 'SvelteKit', 'Docker'],
},
},
fa: {
title: 'داشبورد تحلیلی زرین‌پال',
date: 'مرداد ۱۴۰۵',
description:
'داشبوردی مبتنی بر هوش مصنوعی برای تحلیل تراکنش‌های پذیرنده در پایانه‌های مختلف و پیشنهاد کمپین‌های سودآور برای مناسبت‌ها ساختم. پیشنهادها بر اساس سابقه تراکنش‌ها، رویدادهای پیش رو در تقویم فارسی و رویدادهای بین‌المللی اثرگذار بر بازار ایران تهیه می‌شوند.',
description: 'داشبوردی مبتنی بر هوش مصنوعی برای تحلیل تراکنش‌های پذیرنده در پایانه‌های مختلف و پیشنهاد کمپین‌های سودآور برای مناسبت‌ها ساختم. پیشنهادها بر اساس سابقه تراکنش‌ها، رویدادهای پیش رو در تقویم فارسی و رویدادهای بین‌المللی اثرگذار بر بازار ایران تهیه می‌شوند.',
caseStudy: {
introduction: 'داشبوردی تحلیلی که سابقه تراکنش در پایانه‌های مختلف را به پیشنهادهای به‌موقع برای کمپین‌ها متصل می‌کند.',
role: 'توسعه‌دهنده Full-stack',
targetUsers: 'پذیرندگانی که یک یا چند پایانه پرداخت را مدیریت می‌کنند',
status: 'تکمیل‌شده',
challenge: 'پذیرندگان باید فعالیت پایانه‌های مختلف را بشناسند و الگوهای تاریخی را به تصمیم‌های کاربردی برای مناسبت‌های پیش رو تبدیل کنند.',
ownership: ['نماهای تحلیلی تراکنش‌ها در پایانه‌های مختلف را توسعه دادم.', 'سابقه تراکنش‌ها را به پیشنهادهای کمپین مرتبط کردم.', 'رویدادهای تقویم فارسی و مناسبت‌های بین‌المللی اثرگذار بر بازار ایران را در نظر گرفتم.'],
decisions: [
{ discipline: 'product', title: 'حرکت از گزارش به اقدام', description: 'تحلیل تراکنش به پیشنهاد کمپین گسترش یافت تا داشبورد فقط ابزار مشاهده نباشد و به تصمیم‌گیری کمک کند.' },
{ discipline: 'engineering', title: 'سرویس و رابط متناسب با مسئله', description: 'برای سرویس‌های برنامه از Go، برای تجربه داشبورد از SvelteKit و برای استقرار از Docker استفاده شد.' },
{ discipline: 'design', title: 'حفظ زمینه هر پایانه', description: 'تفاوت پایانه‌ها در رابط باقی ماند تا تحلیل تجمیعی، منبع فعالیت‌ها را پنهان نکند.' },
],
outcomes: ['تحلیل تراکنش و پیشنهاد کمپین متناسب با مناسبت در یک داشبورد جمع شد.', 'بررسی سابقه فعالیت چند پایانه به‌عنوان زمینه تصمیم‌گیری ساده‌تر شد.'],
skills: ['توسعه محصول تحلیلی', 'توسعه Backend', 'طراحی داشبورد'],
tools: ['Go', 'SvelteKit', 'Docker'],
},
},
},
},
{
id: 'iran-implant',
link: 'http://impinternal.com/',
liveUrl: 'http://impinternal.com/',
tags: ['Django', 'Nginx', 'Docker', 'Label'],
image: portfolioImage('/images/project-iran-implant.png', 359, 200),
gallery: [
{
image: portfolioImage('/images/project-iran-implant.png', 359, 200),
content: {
en: { alt: 'Iran Implant internal clinic management system', caption: 'The centralized internal management experience.' },
fa: { alt: 'سامانه مدیریت داخلی کلینیک ایران ایمپلنت', caption: 'نمای سامانه متمرکز مدیریت داخلی.' },
},
},
],
content: {
en: {
title: 'Iran Implant',
date: 'NOV 2025 - MAR 2026',
description:
'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.',
description: '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.',
caseStudy: {
introduction: 'A centralized internal system for coordinating the operational workflows of a specialized dental clinic.',
role: 'Backend developer',
targetUsers: 'Dental clinic staff',
status: 'Completed',
challenge: 'Clinic staff needed one operational system for several connected workflows that otherwise require separate records and coordination.',
ownership: ['Developed backend capabilities for appointment and treatment-room management.', 'Supported payment and prescription workflows.', 'Implemented laboratory order management inside the centralized system.'],
decisions: [
{ discipline: 'product', title: 'Centralized clinic operations', description: 'Kept appointments, rooms, payments, prescriptions, and laboratory orders connected inside one staff workflow.' },
{ discipline: 'engineering', title: 'Production-oriented backend delivery', description: 'Used Django for backend development with Nginx and Docker in the deployment stack.' },
],
outcomes: ['Provided clinic staff with one platform for managing core internal workflows.', 'Connected clinical coordination and administrative records within the same system.'],
skills: ['Backend development', 'Workflow modeling', 'Deployment engineering'],
tools: ['Django', 'Nginx', 'Docker'],
},
},
fa: {
title: 'ایران ایمپلنت',
date: 'آبان ۱۴۰۴ تا اسفند ۱۴۰۴',
description:
'بخش سمت سرور سامانه مدیریت داخلی ایران ایمپلنت را برای یک کلینیک تخصصی دندان‌پزشکی توسعه دادم. کارکنان از طریق یک سامانه متمرکز، نوبت‌ها، اتاق‌های درمان، پرداخت‌ها، نسخه‌ها و سفارش‌های آزمایشگاه را مدیریت می‌کنند.',
description: 'بخش سمت سرور سامانه مدیریت داخلی ایران ایمپلنت را برای یک کلینیک تخصصی دندان‌پزشکی توسعه دادم. کارکنان از طریق یک سامانه متمرکز، نوبت‌ها، اتاق‌های درمان، پرداخت‌ها، نسخه‌ها و سفارش‌های آزمایشگاه را مدیریت می‌کنند.',
caseStudy: {
introduction: 'یک سامانه داخلی متمرکز برای هماهنگی فرایندهای اجرایی کلینیک تخصصی دندان‌پزشکی.',
role: 'توسعه‌دهنده Backend',
targetUsers: 'کارکنان کلینیک دندان‌پزشکی',
status: 'تکمیل‌شده',
challenge: 'کارکنان کلینیک برای چند فرایند مرتبط به یک سامانه اجرایی نیاز داشتند؛ فرایندهایی که در حالت جداگانه به ثبت و هماهنگی بیشتری نیاز دارند.',
ownership: ['قابلیت‌های سمت سرور برای مدیریت نوبت و اتاق درمان را توسعه دادم.', 'فرایندهای پرداخت و نسخه را پشتیبانی کردم.', 'مدیریت سفارش‌های آزمایشگاه را در سامانه متمرکز پیاده‌سازی کردم.'],
decisions: [
{ discipline: 'product', title: 'عملیات متمرکز کلینیک', description: 'نوبت‌ها، اتاق‌ها، پرداخت‌ها، نسخه‌ها و سفارش‌های آزمایشگاه در یک جریان کاری مشترک برای کارکنان قرار گرفتند.' },
{ discipline: 'engineering', title: 'تحویل Backend مناسب محیط واقعی', description: 'برای توسعه سمت سرور از Django و در زیرساخت استقرار از Nginx و Docker استفاده شد.' },
],
outcomes: ['کارکنان کلینیک به یک پلتفرم برای مدیریت فرایندهای داخلی اصلی دسترسی پیدا کردند.', 'هماهنگی درمان و سوابق اداری در یک سامانه به هم متصل شد.'],
skills: ['توسعه Backend', 'مدل‌سازی فرایند', 'مهندسی استقرار'],
tools: ['Django', 'Nginx', 'Docker'],
},
},
},
},
@@ -102,18 +337,64 @@ const projectDefinitions: ProjectDefinition[] = [
id: 'interanet-solutions',
tags: ['Bash', 'Caddy', 'Docker', 'DevOps'],
image: portfolioImage('/images/project-interanet-solutions.png', 1683, 935),
gallery: [
{
image: portfolioImage('/images/project-interanet-solutions.png', 1683, 935),
content: {
en: { alt: 'Interanet Solutions interface in light mode', caption: 'The self-hosted solutions collection in light mode.' },
fa: { alt: 'رابط راهکارهای اینترانت در حالت روشن', caption: 'نمای مجموعه راهکارهای خودمیزبان در حالت روشن.' },
},
},
{
image: portfolioImage('/images/project-interanet-solutions-dark.png', 1683, 935),
content: {
en: { alt: 'Interanet Solutions interface in dark mode', caption: 'The self-hosted solutions collection in dark mode.' },
fa: { alt: 'رابط راهکارهای اینترانت در حالت تیره', caption: 'نمای مجموعه راهکارهای خودمیزبان در حالت تیره.' },
},
},
],
content: {
en: {
title: 'Interanet Solutions',
date: 'MAR - JUN 2026',
description:
'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).',
description: '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).',
caseStudy: {
introduction: 'Self-hosted infrastructure and automated deployment paths for teams working with restricted international Internet access.',
role: 'DevOps developer',
targetUsers: 'Organizations and technical teams operating restricted-network environments',
status: 'Completed',
challenge: 'Teams still need dependable collaboration, design, source control, and communication tools when international services are unavailable or unreliable.',
ownership: ['Developed automated server bootstrap scripts.', 'Prepared self-hosted deployments for source-control, design, and meeting tools.', 'Packaged repeatable infrastructure using containerized services.'],
decisions: [
{ discipline: 'product', title: 'Practical service substitutes', description: 'Focused on recognizable alternatives: GitLab or Gitea for GitHub, Penpot for Figma, and Jitsi for Google Meet.' },
{ discipline: 'engineering', title: 'Automated, self-hosted delivery', description: 'Combined Bash automation, Caddy, and Docker to reduce manual server setup and keep deployments repeatable.' },
],
outcomes: ['Collected multiple restricted-network deployment solutions into a reusable toolkit.', 'Reduced repeated manual setup through automated server bootstrap flows.'],
constraints: ['Designed for environments with restricted international Internet access.'],
skills: ['Infrastructure automation', 'Self-hosting', 'Deployment engineering'],
tools: ['Bash', 'Caddy', 'Docker', 'GitLab', 'Gitea', 'Penpot', 'Jitsi'],
},
},
fa: {
title: 'راهکارهای اینترانت',
date: 'اسفند ۱۴۰۴ تا خرداد ۱۴۰۵',
description:
'مجموعه‌ای از راهکارهای زیرساخت و استقرار خودمیزبان برای محیط‌هایی با دسترسی محدود به اینترنت بین‌المللی توسعه دادم. این مجموعه شامل اسکریپت‌های خودکار راه‌اندازی اولیه سرور و استقرار ابزارهایی مانند GitLab و Gitea به‌جای GitHub،‏ Penpot به‌جای Figma و Jitsi به‌جای Google Meet است.',
description: 'مجموعه‌ای از راهکارهای زیرساخت و استقرار خودمیزبان برای محیط‌هایی با دسترسی محدود به اینترنت بین‌المللی توسعه دادم. این مجموعه شامل اسکریپت‌های خودکار راه‌اندازی اولیه سرور و استقرار ابزارهایی مانند GitLab و Gitea به‌جای GitHub،‏ Penpot به‌جای Figma و Jitsi به‌جای Google Meet است.',
caseStudy: {
introduction: 'زیرساخت خودمیزبان و مسیرهای استقرار خودکار برای تیم‌هایی که با محدودیت اینترنت بین‌المللی کار می‌کنند.',
role: 'توسعه‌دهنده DevOps',
targetUsers: 'سازمان‌ها و تیم‌های فنی فعال در شبکه‌های محدود',
status: 'تکمیل‌شده',
challenge: 'وقتی سرویس‌های بین‌المللی در دسترس یا پایدار نیستند، تیم‌ها همچنان به ابزارهای قابل‌اعتماد برای همکاری، طراحی، کنترل کد و ارتباط نیاز دارند.',
ownership: ['اسکریپت‌های خودکار راه‌اندازی اولیه سرور را توسعه دادم.', 'استقرار خودمیزبان ابزارهای کنترل کد، طراحی و جلسه را آماده کردم.', 'زیرساخت تکرارپذیر را با سرویس‌های کانتینری بسته‌بندی کردم.'],
decisions: [
{ discipline: 'product', title: 'جایگزین‌های عملی برای سرویس‌ها', description: 'تمرکز بر جایگزین‌های شناخته‌شده بود: GitLab یا Gitea به‌جای GitHub،‏ Penpot به‌جای Figma و Jitsi به‌جای Google Meet.' },
{ discipline: 'engineering', title: 'تحویل خودکار و خودمیزبان', description: 'ترکیب Bash،‏ Caddy و Docker راه‌اندازی دستی سرور را کاهش داد و استقرارها را تکرارپذیر نگه داشت.' },
],
outcomes: ['چند راهکار استقرار در شبکه محدود در یک مجموعه قابل‌استفاده مجدد جمع شد.', 'راه‌اندازی خودکار اولیه، نیاز به تنظیمات دستی تکراری را کاهش داد.'],
constraints: ['طراحی‌شده برای محیط‌هایی با دسترسی محدود به اینترنت بین‌المللی.'],
skills: ['خودکارسازی زیرساخت', 'میزبانی داخلی', 'مهندسی استقرار'],
tools: ['Bash', 'Caddy', 'Docker', 'GitLab', 'Gitea', 'Penpot', 'Jitsi'],
},
},
},
},
@@ -121,47 +402,160 @@ const projectDefinitions: ProjectDefinition[] = [
id: 'postgresql-job-queue',
tags: ['Go', 'SvelteKit', 'Docker'],
image: portfolioImage('/images/project-postgresql-job-queue.png', 359, 200),
gallery: [
{
image: portfolioImage('/images/project-postgresql-job-queue.png', 359, 200),
content: {
en: { alt: 'PostgreSQL Job Queue project interface', caption: 'The project overview for the PostgreSQL-backed job system.' },
fa: { alt: 'رابط پروژه صف کار PostgreSQL', caption: 'نمای کلی سیستم صف کار مبتنی بر PostgreSQL.' },
},
},
],
content: {
en: {
title: 'PostgreSQL Job Queue',
date: 'JUN 2026',
description:
'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.',
description: '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.',
caseStudy: {
introduction: 'A lightweight background job system built around safe concurrency, retries, and recoverability.',
role: 'Backend developer',
targetUsers: 'Application developers running slow or unreliable work outside request handling',
status: 'Completed',
challenge: 'Long-running or unreliable tasks should not block request handling, but moving them into workers introduces concurrency, failure, and recovery concerns.',
ownership: ['Built a background execution path outside the main request flow.', 'Supported multiple workers taking jobs without blocking one another.', 'Implemented safe retries and recoverable work handling.'],
decisions: [
{ discipline: 'engineering', title: 'PostgreSQL-backed coordination', description: 'Used PostgreSQL as the durable coordination layer for a lightweight job queue.' },
{ discipline: 'engineering', title: 'Failure-aware execution', description: 'Treated retries and recovery as core behavior so failed or interrupted work stays predictable.' },
{ discipline: 'product', title: 'Small operational surface', description: 'Focused the system on the essential needs of background execution rather than a broad workflow platform.' },
],
outcomes: ['Allowed slow and unreliable tasks to execute outside request handling.', 'Made concurrent worker pickup, retries, and recovery part of the system’s normal behavior.'],
skills: ['Concurrent systems', 'Failure handling', 'Backend architecture'],
tools: ['Go', 'PostgreSQL', 'SvelteKit', 'Docker'],
},
},
fa: {
title: 'صف کار PostgreSQL',
date: 'خرداد ۱۴۰۵',
description:
'یک سیستم سبک برای اجرای کارهای پس‌زمینه ساختم تا کارهای کند یا ناپایدار بیرون از مسیر اصلی درخواست اجرا شوند. چند پردازش‌گر می‌توانند هم‌زمان و بدون مسدودکردن یکدیگر کار بردارند، کارهای ناموفق با سازوکاری امن دوباره اجرا می‌شوند و در صورت وقفه قابل‌بازیابی می‌مانند.',
description: 'یک سیستم سبک برای اجرای کارهای پس‌زمینه ساختم تا کارهای کند یا ناپایدار بیرون از مسیر اصلی درخواست اجرا شوند. چند پردازش‌گر می‌توانند هم‌زمان و بدون مسدودکردن یکدیگر کار بردارند، کارهای ناموفق با سازوکاری امن دوباره اجرا می‌شوند و در صورت وقفه قابل‌بازیابی می‌مانند.',
caseStudy: {
introduction: 'یک سیستم سبک برای اجرای کارهای پس‌زمینه با تمرکز بر هم‌زمانی امن، تلاش مجدد و بازیابی.',
role: 'توسعه‌دهنده Backend',
targetUsers: 'توسعه‌دهندگانی که کارهای کند یا ناپایدار را بیرون از چرخه درخواست اجرا می‌کنند',
status: 'تکمیل‌شده',
challenge: 'کارهای طولانی یا ناپایدار نباید پاسخ درخواست را متوقف کنند؛ اما انتقال آن‌ها به پردازش‌گرها، مسئله هم‌زمانی، شکست و بازیابی را به همراه دارد.',
ownership: ['مسیر اجرای پس‌زمینه را بیرون از چرخه اصلی درخواست ساختم.', 'برداشتن هم‌زمان کار توسط چند پردازش‌گر را بدون مسدودکردن یکدیگر پشتیبانی کردم.', 'تلاش مجدد امن و بازیابی کارها را پیاده‌سازی کردم.'],
decisions: [
{ discipline: 'engineering', title: 'هماهنگی مبتنی بر PostgreSQL', description: 'از PostgreSQL به‌عنوان لایه پایدار هماهنگی برای یک صف کار سبک استفاده شد.' },
{ discipline: 'engineering', title: 'اجرای آگاه از شکست', description: 'تلاش مجدد و بازیابی به‌عنوان رفتارهای اصلی در نظر گرفته شدند تا کار ناموفق یا متوقف‌شده قابل‌پیش‌بینی بماند.' },
{ discipline: 'product', title: 'سطح عملیاتی کوچک', description: 'تمرکز سیستم بر نیازهای اصلی اجرای پس‌زمینه باقی ماند و به یک پلتفرم گسترده گردش کار تبدیل نشد.' },
],
outcomes: ['کارهای کند و ناپایدار بیرون از مسیر پاسخ‌گویی به درخواست اجرا شدند.', 'برداشت هم‌زمان کار، تلاش مجدد و بازیابی به بخشی از رفتار عادی سیستم تبدیل شد.'],
skills: ['سیستم‌های هم‌زمان', 'مدیریت شکست', 'معماری Backend'],
tools: ['Go', 'PostgreSQL', 'SvelteKit', 'Docker'],
},
},
},
},
{
id: 'subsource',
link: 'http://subsource.net/',
liveUrl: 'http://subsource.net/',
tags: ['Product Design', 'Figma', 'Prototyping'],
image: portfolioImage('/images/project-subsource.png', 2560, 1440),
gallery: [
{
image: portfolioImage('/images/project-subsource.png', 2560, 1440),
content: {
en: { alt: 'Subsource subtitle discovery product in light mode', caption: 'The subtitle discovery experience in light mode.' },
fa: { alt: 'محصول جست‌وجوی زیرنویس ساب‌سورس در حالت روشن', caption: 'نمای تجربه جست‌وجوی زیرنویس در حالت روشن.' },
},
},
{
image: portfolioImage('/images/project-subsource-dark.png', 2560, 1440),
content: {
en: { alt: 'Subsource subtitle discovery product in dark mode', caption: 'The subtitle discovery experience in dark mode.' },
fa: { alt: 'محصول جست‌وجوی زیرنویس ساب‌سورس در حالت تیره', caption: 'نمای تجربه جست‌وجوی زیرنویس در حالت تیره.' },
},
},
],
content: {
en: {
title: 'Subsource',
date: 'MAY 2024',
description:
'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.',
description: '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.',
caseStudy: {
introduction: 'An end-to-end product design for finding and downloading movie and television subtitles across languages.',
role: 'Product designer',
targetUsers: 'Movie and television audiences looking for subtitles in multiple languages',
status: 'Completed',
challenge: 'Subtitle discovery combines search, language choices, content metadata, and download decisions in an information-heavy interface.',
ownership: ['Designed the product’s end-to-end UI/UX.', 'Simplified content discovery across movies, television, and languages.', 'Organized dense information and created intuitive product flows.'],
decisions: [
{ discipline: 'product', title: 'Discovery-first structure', description: 'Centered the experience on helping people identify the right title and subtitle efficiently.' },
{ discipline: 'design', title: 'Organized information density', description: 'Structured metadata and choices so a detailed interface remains scannable.' },
{ discipline: 'design', title: 'Consistent end-to-end flows', description: 'Connected discovery and download through a coherent interaction model across the product.' },
],
outcomes: ['Produced a complete UI/UX direction for a multilingual subtitle platform.', 'Turned an information-heavy discovery task into a clearer set of user flows.'],
skills: ['Product design', 'Information architecture', 'Interaction design', 'Prototyping'],
tools: ['Figma'],
},
},
fa: {
title: 'ساب‌سورس',
date: 'اردیبهشت ۱۴۰۳',
description:
'طراحی کامل رابط و تجربه کاربری پلتفرم ساب‌سورس را انجام دادم؛ سرویسی برای پیدا کردن و دانلود زیرنویس فیلم و سریال به زبان‌های مختلف. تمرکزم بر ساده‌کردن جست‌وجوی محتوا، مرتب‌کردن رابط‌های پرجزئیات و طراحی مسیرهای کاربری روشن در سراسر محصول بود.',
description: 'طراحی کامل رابط و تجربه کاربری پلتفرم ساب‌سورس را انجام دادم؛ سرویسی برای پیدا کردن و دانلود زیرنویس فیلم و سریال به زبان‌های مختلف. تمرکزم بر ساده‌کردن جست‌وجوی محتوا، مرتب‌کردن رابط‌های پرجزئیات و طراحی مسیرهای کاربری روشن در سراسر محصول بود.',
caseStudy: {
introduction: 'طراحی کامل محصول برای پیدا کردن و دانلود زیرنویس فیلم و سریال به زبان‌های مختلف.',
role: 'طراح محصول',
targetUsers: 'مخاطبان فیلم و سریال که به‌دنبال زیرنویس در زبان‌های مختلف هستند',
status: 'تکمیل‌شده',
challenge: 'پیدا کردن زیرنویس، جست‌وجو، انتخاب زبان، اطلاعات محتوا و تصمیم دانلود را در یک رابط پرجزئیات کنار هم قرار می‌دهد.',
ownership: ['طراحی کامل UI/UX محصول را انجام دادم.', 'پیدا کردن محتوا در میان فیلم، سریال و زبان‌های مختلف را ساده کردم.', 'اطلاعات پرتراکم را سامان دادم و مسیرهای کاربری روشن ساختم.'],
decisions: [
{ discipline: 'product', title: 'ساختار مبتنی بر کشف محتوا', description: 'تجربه حول شناسایی سریع عنوان و زیرنویس مناسب شکل گرفت.' },
{ discipline: 'design', title: 'سامان‌دهی تراکم اطلاعات', description: 'فراداده و انتخاب‌ها طوری ساختاربندی شدند که رابط پرجزئیات همچنان قابل‌اسکن بماند.' },
{ discipline: 'design', title: 'جریان یکپارچه از ابتدا تا انتها', description: 'کشف محتوا و دانلود با یک مدل تعاملی منسجم در سراسر محصول به هم متصل شدند.' },
],
outcomes: ['یک مسیر کامل UI/UX برای پلتفرم چندزبانه زیرنویس طراحی شد.', 'وظیفه پرجزئیات یافتن زیرنویس به مجموعه‌ای روشن‌تر از مسیرهای کاربری تبدیل شد.'],
skills: ['طراحی محصول', 'معماری اطلاعات', 'طراحی تعامل', 'نمونه‌سازی'],
tools: ['Figma'],
},
},
},
},
];
export const projectIds = projectDefinitions.map(({ id }) => id);
export const getProjects = (locale: Locale): Project[] =>
projectDefinitions.map(({ content, ...project }) => ({
projectDefinitions.map(({ content, gallery, ...project }) => ({
...project,
...content[locale],
caseStudy: {
...content[locale].caseStudy,
gallery: gallery.map(({ image, content: galleryContent }) => ({
image,
...galleryContent[locale],
})),
},
}));
export const getProject = (locale: Locale, id: string) =>
getProjects(locale).find((project) => project.id === id);
export const getAdjacentProjects = (locale: Locale, id: string) => {
const localizedProjects = getProjects(locale);
const currentIndex = localizedProjects.findIndex((project) => project.id === id);
if (currentIndex < 0) return undefined;
return {
previous: localizedProjects[(currentIndex - 1 + localizedProjects.length) % localizedProjects.length],
next: localizedProjects[(currentIndex + 1) % localizedProjects.length],
};
};
export const getPortfolioPath = (locale: Locale) => (locale === 'fa' ? '/fa' : '/');
export const getProjectPath = (locale: Locale, id: string) =>
`${locale === 'fa' ? '/fa' : ''}/projects/${id}`;
export const projects = getProjects('en');