# Contributing to Portfolio Thank you for your interest in contributing to this project! This guide will help you get started with development. ## Table of Contents - [Getting Started](#getting-started) - [Project Overview](#project-overview) - [Tech Stack](#tech-stack) - [Project Structure](#project-structure) - [Development Workflow](#development-workflow) - [Pull Request Guidelines](#pull-request-guidelines) - [Content Workflows](#content-workflows) - [Code Style & Standards](#code-style--standards) - [Testing](#testing) - [Legacy Code & Migration](#legacy-code--migration) - [Commit Guidelines](#commit-guidelines) ## Getting Started ### Prerequisites - **Node.js** 18.x or higher - **npm** 9.x or higher - **Git** for version control ### Installation 1. **Clone the repository** ```bash git clone cd portfolio ``` 2. **Install dependencies** ```bash npm install ``` 3. **Set up environment variables** Create a `.env.local` file in the root directory with the required environment variables: ```bash # Supabase Configuration NEXT_PUBLIC_SUPABASE_URL=your_supabase_url NEXT_PUBLIC_SUPABASE_ANON_KEY=your_supabase_anon_key # Optional: OpenAI (for image generation scripts) OPENAI_API_KEY=your_openai_api_key ``` 4. **Start the development server** ```bash npm run dev ``` The application will be available at `http://localhost:3000` ### Available Scripts | Command | Description | |---------|-------------| | `npm run dev` | Start development server with hot reload | | `npm run build` | Build production bundle | | `npm start` | Start production server (after build) | | `npm run lint` | Run ESLint with strict mode | | `npm test` | Run unit tests with Vitest | | `npm run test:coverage` | Run tests with coverage report | | `npm run build:images` | Optimize images using Sharp | | `npm run generate:images` | Generate blog images using OpenAI | | `npm run generate:images:dry` | Preview image generation without creating files | ## Project Overview This is a personal portfolio website for Damjan Savić, showcasing work as an AI & Automation Specialist. The site features: - **Multi-language support** - German, English, and Serbian with automatic language detection - **Progressive Web App** - Installable and offline-capable - **SEO optimized** - Structured data (Schema.org), sitemap generation - **Responsive Design** - Mobile-first approach with Tailwind CSS - **Blog System** - MDX-based content with frontmatter support - **Portfolio Gallery** - Dynamic project showcase with filtering - **Contact Form** - Integrated with Supabase backend ## Tech Stack ### Core Framework - **Next.js 15** - React framework with App Router - **React 19** - UI library with latest features - **TypeScript 5.5** - Type-safe development ### Styling & UI - **Tailwind CSS 3.4** - Utility-first CSS framework - **Framer Motion 11** - Animation library - **Lucide React** - Icon library - **Tailwind Merge** - Utility for merging Tailwind classes - **CVA (Class Variance Authority)** - Component variants management ### Content & Internationalization - **MDX** - Markdown + JSX for content pages - `@mdx-js/loader`, `@mdx-js/mdx`, `@mdx-js/react` - `@next/mdx`, `next-mdx-remote` - **next-intl 3.26** - Internationalization (i18n) - **gray-matter** - Frontmatter parsing ### Backend & Services - **Supabase** - Database, authentication, and backend services - `@supabase/supabase-js` - `@supabase/ssr` - **OpenAI 4.77** - AI integration for content generation ### Development Tools - **Vitest** - Unit testing framework - **ESLint 9** - Code linting with Next.js config - **PostCSS** - CSS processing - **Autoprefixer** - CSS vendor prefixes - **Sharp** - Image optimization ### Performance & Analytics - **web-vitals** - Performance metrics - **react-intersection-observer** - Lazy loading and scroll animations ## Project Structure ``` portfolio/ ├── public/ # Static assets (images, fonts, etc.) ├── scripts/ # Build and utility scripts │ ├── optimize-images.js # Image optimization │ └── generate-blog-images.mjs # AI-powered image generation ├── src/ │ ├── app/ # Next.js App Router │ │ ├── [locale]/ # Internationalized routes │ │ │ ├── about/ # About page │ │ │ ├── blog/ # Blog listing & posts │ │ │ ├── contact/ # Contact form │ │ │ ├── dashboard/ # Admin dashboard │ │ │ ├── imprint/ # Legal imprint │ │ │ ├── leistungen/ # Services page │ │ │ ├── login/ # Authentication │ │ │ ├── portfolio/ # Portfolio gallery & projects │ │ │ ├── privacy/ # Privacy policy │ │ │ ├── terms/ # Terms of service │ │ │ ├── layout.tsx # Locale-specific layout │ │ │ └── page.tsx # Homepage │ │ ├── globals.css # Global styles │ │ ├── layout.tsx # Root layout │ │ ├── not-found.tsx # 404 page │ │ ├── robots.ts # Robots.txt generation │ │ └── sitemap.ts # Sitemap generation │ ├── components/ # Reusable React components │ ├── components-vite/ # Legacy Vite components │ ├── data/ # Static data and content │ ├── hooks/ # Custom React hooks │ ├── i18n/ # Internationalization config │ ├── lib/ # Utility libraries and helpers │ ├── messages/ # Translation files (de, en, sr) │ ├── middleware.ts # Next.js middleware (i18n routing) │ ├── pages-vite/ # Legacy Vite pages │ ├── services/ # API and service integrations │ ├── styles/ # Additional styles │ ├── types/ # TypeScript type definitions │ └── utils/ # Helper functions ├── .env.local # Environment variables (not in git) ├── .eslintrc.json # ESLint configuration ├── docker-compose.yml # Docker Compose setup ├── Dockerfile # Docker container config ├── next.config.ts # Next.js configuration ├── package.json # Dependencies and scripts ├── postcss.config.js # PostCSS configuration ├── tailwind.config.js # Tailwind CSS configuration └── tsconfig.json # TypeScript configuration ``` ### Key Directories Explained - **`src/app/`** - Next.js App Router pages and layouts using the new file-based routing system - **`src/app/[locale]/`** - All pages are nested under locale for multi-language support (de, en, sr) - **`src/components/`** - Reusable UI components (buttons, cards, forms, navigation, etc.) - **`src/hooks/`** - Custom React hooks for shared logic - **`src/lib/`** - Utility libraries (Supabase client, helpers, etc.) - **`src/messages/`** - Translation JSON files for each language - **`src/services/`** - API integrations and external service wrappers - **`src/types/`** - TypeScript interfaces and type definitions - **`src/utils/`** - Helper functions and utilities - **`public/`** - Static files served directly (images, fonts, favicons) - **`scripts/`** - Build automation and utility scripts ## Development Workflow ### Branch Naming Conventions Use descriptive branch names that follow these patterns: | Branch Type | Pattern | Example | Description | |-------------|---------|---------|-------------| | **Feature** | `feature/` | `feature/add-dark-mode` | New features or enhancements | | **Bug Fix** | `fix/` | `fix/mobile-nav-bug` | Bug fixes and error corrections | | **Documentation** | `docs/` | `docs/update-readme` | Documentation updates | | **Refactoring** | `refactor/` | `refactor/contact-form` | Code restructuring without behavior changes | | **Testing** | `test/` | `test/add-unit-tests` | Adding or updating tests | | **Chore** | `chore/` | `chore/update-dependencies` | Maintenance tasks, dependency updates | **Guidelines:** - Use lowercase with hyphens (kebab-case) - Keep branch names concise but descriptive (3-5 words max) - Avoid special characters except hyphens - Delete branches after they've been merged **Examples:** ```bash # ✅ Good git checkout -b feature/cookie-consent-banner git checkout -b fix/portfolio-filter-crash git checkout -b docs/api-integration-guide # ❌ Bad git checkout -b myFeature git checkout -b fix_bug git checkout -b john-working-branch ``` ### 1. Create a New Feature Branch ```bash git checkout -b feature/your-feature-name ``` ### 2. Make Your Changes - Follow the existing code patterns and structure - Use TypeScript for type safety - Write clean, readable code with appropriate comments - Test your changes locally ### 3. Test Your Changes ```bash # Run development server npm run dev # Run linter npm run lint # Run tests npm test ``` ### 4. Build for Production ```bash npm run build ``` Ensure the build completes without errors before submitting. ### 5. Submit a Pull Request See the [Pull Request Guidelines](#pull-request-guidelines) section below for detailed instructions. ## Pull Request Guidelines When you're ready to submit your changes, create a pull request (PR) following these guidelines to ensure a smooth review process. ### Before Creating a PR **Pre-submission Checklist:** - [ ] All tests pass (`npm test`) - [ ] Linter runs without errors (`npm run lint`) - [ ] Production build succeeds (`npm run build`) - [ ] Changes have been tested locally - [ ] Code follows project style guidelines - [ ] No debugging statements left in code (console.log, debugger, etc.) - [ ] Translation files updated for all locales (de, en, sr) if UI text changed - [ ] Documentation updated if necessary ### PR Title Format Use the same format as commit messages: ``` : ``` **Examples:** ``` feat: add cookie consent banner fix: resolve mobile navigation menu bug docs: update API integration guide refactor: simplify contact form validation ``` ### PR Description Template Provide a clear description of your changes: ```markdown ## Summary Brief overview of what this PR does and why. ## Changes Made - Added new component for cookie consent - Updated privacy policy page - Added tests for consent logic ## Testing - [ ] Tested on desktop browsers (Chrome, Firefox, Safari) - [ ] Tested on mobile devices - [ ] Verified all three languages (de, en, sr) - [ ] Checked accessibility with screen reader ## Screenshots (if applicable) [Add screenshots for UI changes] ## Related Issues Closes #123 Related to #456 ``` ### PR Best Practices 1. **Keep PRs Focused** - One feature or fix per PR - Avoid mixing unrelated changes - Break large features into smaller, reviewable PRs 2. **Write Descriptive Commits** - Follow the [Commit Guidelines](#commit-guidelines) - Each commit should be a logical unit of work - Avoid "WIP" or "Fix" commit messages 3. **Update Documentation** - Update README.md if adding new features - Update CONTRIBUTING.md if changing development workflow - Add JSDoc comments for new public functions 4. **Request Review** - Tag relevant reviewers - Respond to review comments promptly - Make requested changes in new commits (don't force push) 5. **CI/CD Checks** - Ensure all automated checks pass - Fix any linting or build errors - Address test failures before requesting review ### PR Review Process 1. **Automated Checks** - All CI/CD checks must pass 2. **Code Review** - At least one approval required 3. **Testing** - Reviewer may test changes locally 4. **Merge** - Squash and merge into main branch 5. **Cleanup** - Delete branch after merge ### Handling Review Feedback - **Be responsive** - Address feedback within 1-2 days - **Ask questions** - If feedback is unclear, ask for clarification - **Make changes** - Commit review suggestions as new commits - **Re-request review** - After addressing all comments - **Be respectful** - Code reviews are about the code, not the person ### What Gets Rejected PRs will be rejected or require changes if: - ❌ Tests are failing - ❌ Linter errors exist - ❌ Build fails - ❌ Breaking changes without discussion - ❌ Missing translations for new UI text - ❌ No description or context provided - ❌ Touches legacy code without coordination (see [Legacy Code](#legacy-code--migration)) ## Content Workflows This section covers how to add and manage content for portfolio projects, blog posts, and translations. The project supports multi-language content in German (de), English (en), and Serbian (sr). ### Adding Portfolio Projects Portfolio projects are defined as TypeScript files with structured metadata and content. Each project must be created for all supported locales. #### File Location Conventions Portfolio project files follow this structure: ``` src/i18n/locales-old/ ├── de/portfolio/projects/ # German portfolio projects │ └── my-project.ts ├── en/portfolio/projects/ # English portfolio projects │ └── my-project.ts └── sr/portfolio/projects/ # Serbian portfolio projects └── my-project.ts ``` **Important:** Use the same filename (slug) across all locales to maintain consistency. #### TypeScript Data Structure Each portfolio project file must export an object with the following structure: ```typescript // src/i18n/locales-old/de/portfolio/projects/my-project.ts export const myProject = { meta: { slug: 'my-project', // Unique identifier (same across locales) title: 'Project Title | Damjan Savić', // SEO-optimized title description: 'Brief project description for meta tags', excerpt: 'Short excerpt for project cards', date: '2025-01', // Publication date (YYYY-MM format) category: 'Category Name', // e.g., "Web Development", "Data Processing" client: 'Client Name', // Client or organization duration: '2 Monate', // Project duration url: 'https://project-url.com', // Live project URL (optional) repository: '', // GitHub repository URL (optional) documentation: '', // Documentation URL (optional) published: true, // Visibility flag featured: true, // Featured on homepage technologies: [ // Technology stack 'TypeScript', 'React', 'Next.js' ], tags: [ // Project tags for filtering 'Web Development', 'Full Stack', 'SEO' ] }, content: { intro: 'Introduction paragraph describing the project overview.', challenge: { title: 'The Challenge', description: 'Brief description of the problem to solve.', points: [ 'Challenge point 1', 'Challenge point 2', 'Challenge point 3' ] }, solution: { title: 'The Solution', description: 'How the problem was approached.', content: 'Detailed explanation of the solution.', points: [ 'Solution feature 1', 'Solution feature 2', 'Solution feature 3' ] }, technical: { title: 'Technical Implementation', description: 'Technical approach and architecture.', points: [ 'Technical detail 1', 'Technical detail 2', 'Technical detail 3' ], code: `// Optional code snippet to showcase implementation export function exampleFunction() { return 'Example code'; }` }, implementation: { title: 'Implementation Process', description: 'How the project was built.', points: [ 'Implementation step 1', 'Implementation step 2', 'Implementation step 3' ] }, results: { title: 'Results and Impact', description: 'Measurable outcomes and business impact.', points: [ 'Result metric 1', 'Result metric 2', 'Result metric 3' ] }, conclusion: 'Final thoughts and project summary.' } }; // Re-export for compatibility with the project import system export default myProject; ``` #### Step-by-Step: Adding a New Portfolio Project 1. **Create the TypeScript file** for each locale: ```bash # German version touch src/i18n/locales-old/de/portfolio/projects/my-project.ts # English version touch src/i18n/locales-old/en/portfolio/projects/my-project.ts # Serbian version touch src/i18n/locales-old/sr/portfolio/projects/my-project.ts ``` 2. **Copy the template structure** above into each file 3. **Translate the content** for each locale while keeping the same structure 4. **Add project images** (if any) to `public/images/portfolio/my-project/` 5. **Test the project** by navigating to: - German: `http://localhost:3000/de/portfolio/my-project` - English: `http://localhost:3000/en/portfolio/my-project` - Serbian: `http://localhost:3000/sr/portfolio/my-project` 6. **Verify SEO metadata** using browser DevTools to check meta tags ### Adding Blog Posts Blog posts use MDX (Markdown + JSX) format with frontmatter metadata. MDX allows you to embed React components directly in your content. #### File Location Conventions Blog posts are organized by locale: ``` src/content/blog/ ├── de/ # German blog posts │ └── my-blog-post.mdx ├── en/ # English blog posts │ └── my-blog-post.mdx └── sr/ # Serbian blog posts └── my-blog-post.mdx ``` #### MDX File Structure Each blog post must include frontmatter metadata at the top: ```mdx --- title: "Blog Post Title" description: "Brief description for SEO and post previews" date: "2025-01-20" author: "Damjan Savić" category: "Technology" tags: ["React", "Next.js", "TypeScript"] image: "/images/blog/my-blog-post/hero.jpg" published: true featured: false excerpt: "A short excerpt that appears in post listings" --- # Blog Post Title Your content starts here. You can use standard Markdown syntax: ## Headings Regular **bold** and *italic* text. ### Code Blocks ```typescript // Code examples with syntax highlighting const greeting = "Hello, World!"; console.log(greeting); ``` ### Lists - Bullet point 1 - Bullet point 2 - Bullet point 3 ### Images ![Alt text](/images/blog/my-blog-post/diagram.png) ### Custom Components (MDX Feature) You can embed React components: This is a custom component in MDX! ``` #### Step-by-Step: Adding a New Blog Post 1. **Create the MDX file** for each locale: ```bash # German version touch src/content/blog/de/my-blog-post.mdx # English version touch src/content/blog/en/my-blog-post.mdx # Serbian version touch src/content/blog/sr/my-blog-post.mdx ``` 2. **Add frontmatter metadata** with all required fields 3. **Write the content** using Markdown/MDX syntax 4. **Add images** to `public/images/blog/my-blog-post/` 5. **Generate blog images** (optional) using the AI-powered script: ```bash # Preview image generation npm run generate:images:dry # Generate actual images npm run generate:images ``` 6. **Test the blog post** by navigating to: - German: `http://localhost:3000/de/blog/my-blog-post` - English: `http://localhost:3000/en/blog/my-blog-post` - Serbian: `http://localhost:3000/sr/blog/my-blog-post` ### Internationalization (i18n) Workflow The project uses **next-intl** for internationalization with JSON-based translation files. #### Translation File Structure Translation files are located in `src/messages/` and organized by locale: ``` src/messages/ ├── de.json # German translations ├── en.json # English translations └── sr.json # Serbian translations ``` #### Translation File Format Each translation file is a nested JSON object organized by page or section: ```json { "meta": { "site": { "name": "Damjan Savić", "title": "Damjan Savić | Fullstack Developer", "description": "Portfolio website description" } }, "common": { "nav": { "home": "Home", "about": "About", "portfolio": "Portfolio", "blog": "Blog", "contact": "Contact" }, "actions": { "readMore": "Read More", "viewMore": "View More", "back": "Back", "close": "Close" } }, "pages": { "home": { "hero": { "title": "FULLSTACK DEVELOPER", "subtitle": "AI Agents | Voice AI | Process Automation" } } } } ``` #### Step-by-Step: Adding Translations 1. **Add the translation key** to `src/messages/de.json` (German - primary language): ```json { "pages": { "contact": { "form": { "title": "Kontakt aufnehmen", "name": "Name", "email": "E-Mail", "message": "Nachricht", "submit": "Absenden" } } } } ``` 2. **Add the same structure** to `src/messages/en.json` (English): ```json { "pages": { "contact": { "form": { "title": "Get in Touch", "name": "Name", "email": "Email", "message": "Message", "submit": "Submit" } } } } ``` 3. **Add the same structure** to `src/messages/sr.json` (Serbian): ```json { "pages": { "contact": { "form": { "title": "Kontaktirajte nas", "name": "Ime", "email": "Email", "message": "Poruka", "submit": "Pošalji" } } } } ``` 4. **Use the translation** in your component: ```typescript import { useTranslations } from 'next-intl'; export function ContactForm() { const t = useTranslations('pages.contact.form'); return (

{t('title')}