https://ahmadmdabit.github.io/
This is a personal portfolio website for Ahmet Fatihoglu, a Senior Software Developer and Architect. The website is a dynamic, data-driven single-page application built with a modern React and TypeScript stack. It features an integrated AI chat assistant that answers questions about Ahmet's professional background using document-based retrieval augmented generation (RAG).
- Core Framework: React 19 with TypeScript
- Build Tool: Vite with
vite-tsconfig-pathsfor module aliasing - UI Library: Material-UI (MUI) v7 with Emotion for styling
- Routing: React Router v7 with nested layouts and SPA navigation
- State Management: Local component state managed by React Hooks (
useState,useCallback,useEffect) - Internationalization:
i18nextwithreact-i18nextandi18next-browser-languagedetector(EN/TR) - AI Chat:
@heyputer/puter.jsfor LLM inference with streaming and tool-calling support - Document Search:
minisearchfor full-text indexing and retrieval of resume/FAQ/project documents - Markdown Rendering:
markedwithdompurifyfor safe HTML rendering of AI responses - Styling: Custom scrollbar styling via
src/index.css(cross-browser thin scrollbar) - Deployment: GitHub Pages via
gh-pages
The project follows an Atomic Design methodology:
src/data— Static data files and content modules:pagesData.ts— Page metadata and navigation configuration(removed; content fully centralized in i18n JSON locale files)resumeData.ts(removed fromResume_EN_*.mdsrc/assets/; single-source-of-truth is nowpublic/data/Resume-EN.md)
src/types— TypeScript interfaces for core data structures (ResumeData,Project,Experience, etc.)src/atoms— Smallest reusable components (e.g.,ActivityButton,BoldedKeyword,StatusIndicator,CloseButton). Context-unaware, no business logic.src/molecules— Compositions of atoms forming complex components:ActivityBar— Navigation sidebar withNavLinkroutingChatPopup— AI-powered chat assistant (see Chat System below)StatusBar— Bottom status bar showing page name and title, with responsive mobile/tablet support
src/components/resume— Main UI components organized into:sections/—AboutSection,SkillsSection,ExperienceSection,EducationSection,ContactSection,ProjectsSection,CertificationsSectionitems/—ProjectCard,ContactList,ContactForm,PrivacyAccordion,SummaryContent,CVDownloadSection
src/pages— Top-level page components (HomePage,PrivacyPage,ErrorPage)src/i18n— Internationalization configuration with locale JSON files (en/translation.json,tr/translation.json), schema validation, and asset hashing for cache bustingpublic/data— Markdown documents served as data sources for the AI chat:Resume-EN.md/Resume-TR.md— Structured resume contentProjects-EN.md/Projects-TR.md— Detailed project descriptionsFAQ-EN.md/FAQ-TR.md— 110 interview questions with answers and alternative phrasings (10 per question)Resume-EN.pdf/Resume-TR.pdf— Downloadable PDF resumesResume-EN.txt/Resume-TR.txt— Plain-text resume versions
- All resume content is defined in i18n translation JSON files (EN/TR).
- Components use
useTranslationwithreturnObjects: trueto fetch structured data. - Content flows hierarchically:
HomePage→Sectioncomponents →Itemcomponents via props.
The chat assistant implements a Retrieval Augmented Generation workflow:
-
Document Loading — On chat open,
loadAndIndexDocuments()fetches markdown documents for both locales in parallel (Promise.all):- Resume (EN + TR)
- Projects (EN + TR)
- FAQ with 110 questions and alternative phrasings (EN + TR)
-
Chunking — Documents are split into searchable chunks:
- Resume/Projects: split by headings (
##,###) - FAQ: one chunk per question-answer pair, including all alternative phrasings
- Resume/Projects: split by headings (
-
Indexing — Both locales' chunks are merged into a single bilingual
MiniSearchindex with fuzzy matching (0.2), prefix matching, and title boosting (2×). The index is built once on first open and persists for the session lifetime. Each chunk is tagged with its locale ("en"or"tr"). -
Query Flow:
- User message → full resume + projects content for the current locale provided as initial context
- FAQ content is not included inline — instead, a compact numbered question list is provided, and the LLM is instructed to call
expandSearchfor any matching FAQ question - LLM receives system prompt + document context + conversation history via
puter.ai.chat() - If initial context is insufficient, LLM calls
expandSearchtool (via function calling) expandSearchperforms relaxed fuzzy search filtered by locale ("same") or across both locales ("all") as fallback- Expanded results sent back to LLM for final streaming response
-
Streaming — Responses stream in real-time via
puter.ai.chat({ stream: true })with markdown rendering viamarked+DOMPurify. Each assistant message uses aMarkdownRenderercomponent with async parsing and cancellation support. -
Session Persistence — The chat session (messages, API history, search index) persists across popup close/reopen and locale changes. Only a full page refresh clears the session. The bilingual index ensures seamless locale switching with zero re-indexing.
-
Features:
- Abort controller for stopping generation mid-stream
- Conversation history management via
apiHistoryRef - Loading/indexing state indicators (spinner during document loading)
- Bilingual support — user can switch languages mid-conversation; system prompt instructs LLM to respond in the current question's language regardless of history language
- Input focus management with 220ms delay after popup open
- Data-Driven UI: Resume content is dynamically generated from centralized i18n translation files, making updates easy and consistent across languages.
- Atomic Design: Components organized by complexity, promoting reusability and maintainability.
- Centralized Theming: All styling values managed through MUI theme in
App.tsx. - Performance Optimization:
- Memoization: Presentational components wrapped in
React.memo - Bundle Size: Direct-path MUI imports for optimal tree-shaking
- Asset Hashing: Locale and public asset hashes (
VITE_LOCALE_HASH,VITE_ASSET_HASH) for cache invalidation
- Memoization: Presentational components wrapped in
- Path Aliasing:
@/alias for absolute imports throughout the codebase. - Internationalization: Full EN/TR support with browser language auto-detected locale, hash-busted locale loading, and bilingual chat index (both locales indexed in a single
MiniSearchinstance for seamless mid-conversation language switching). - Responsive Design: StatusBar uses
useMediaQueryto hide page indicators on mobile/tablet. - Accessibility: Chat popup includes
role="dialog",aria-label, and keyboard navigation support. - Security: AI responses sanitized via
DOMPurifybefore rendering;EXTERNAL-tagged FAQ entries (placeholders for answers not presented in the resume document) are indexed but can be filtered out via the commented-out guard inchunkFAQDocument;extractFAQQuestions()safely parses only question titles without exposing answer content in the initial prompt.
| Date | Change | Description |
|---|---|---|
| 2026-06-05 | UI Fixes | Removed StatusIndicator from StatusBar; removed page name from StatusBar on mobile/tablet via useMediaQuery |
| 2026-04-24 | Resume Update | Added Distributed File Fragmentor project (.NET 9, Clean Architecture, CQRS, EF Core 9); removed E-Store project to keep portfolio focused |
| 2025-12-12 | Resume Update | Added SystemProcesses project (.NET, WPF, MVVM) to translation files |
| 2025-10-21 | Resume Update | Added Aided (SolidJS-inspired JS library) and Meeting System (.NET 9, Angular 20+, Docker) projects; refreshed skills list; removed older Sinav Oluşturma project |
| 2025-09-30 | Resume & Error Boundary | Updated resume content with new Architect title and skill reorganization; added Exchange and Market projects; added phone number to contact info; added ErrorBoundary component for graceful error handling; bumped package version to 1.0.0 |
| 2025-09-19 | Deployment Fixes | Added 404.html for GitHub Pages SPA fallback; set base: '/' and sourcemap: false in Vite config; moved ThemeProvider and CssBaseline to main.tsx; enhanced PrivacyPage layout |
| 2025-09-18 | AI Chat Integration | ChatPopup.tsx completely rewritten with Puter.js LLM streaming, MiniSearch bilingual merged index (both locales in single index), FAQ question list with expandSearch-on-demand, markdown rendering via marked + DOMPurify, abort controls, and session persistence across locale changes |
| 2025-09-18 | i18n & Routing | Migrated from state-based navigation to declarative React Router v7 routes; resume sections now fetch data directly from i18n translations via returnObjects: true; standardized env vars (VITE_ASSET_HASH, VITE_LOCALE_HASH) |
| 2025-09-18 | Component Refactoring | Extracted AboutSection into SummaryContent, AnimatedBadgeComponent, CVDownloadSection; extracted ContactSection into ContactList, ContactForm, PrivacyAccordion; reduced AboutSection from ~180 to ~60 lines and ContactSection from ~185 to ~20 lines |
| 2025-09-17 | Asset Hashing | Added scripts/hash-locale.mjs using SHA1 for combined locale + public asset hashes; dynamic ?v=hash cache busting for images, PDFs, favicons, and locale JSON files |
| 2025-09-17 | Certification Badge | Added Microsoft certification badge to AboutSection with i18n support; extended i18n schema |
| 2025-09-16 | Project Scaffold | Initial React + Vite + TypeScript setup; Atomic Design component structure; i18n with EN/TR support; resume sections (About, Skills, Experience, Education, Contact, Certifications, Languages); ActivityBar navigation; StatusBar; ChatPopup placeholder; public assets (images, favicons, PDFs) |
# Development
yarn dev
# Type checking
yarn typecheck
# Build (typecheck → hash locales → vite build)
yarn build
# Lint + build
yarn build:lint
# Deploy to GitHub Pages
yarn deploy # runs predeploy (build:lint) then gh-pages -d distThis project's all rights reserved — see the LICENSE file for details.