From 4ef3b4267d26cb5ed6ece2b49a92172a57658341 Mon Sep 17 00:00:00 2001 From: Damjan Savic Date: Sun, 25 Jan 2026 06:30:54 +0100 Subject: [PATCH 1/5] auto-claude: subtask-1-1 - Create CONTRIBUTING.md with project overview and development setup --- CONTRIBUTING.md | 374 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 374 insertions(+) create mode 100644 CONTRIBUTING.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..65e2b35 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,374 @@ +# 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) +- [Code Style & Standards](#code-style--standards) +- [Testing](#testing) +- [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 + +### 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. + +## Code Style & Standards + +### TypeScript +- Use TypeScript for all new files +- Define proper types and interfaces +- Avoid using `any` - use proper typing or `unknown` +- Export types from a central `types/` directory when shared + +### React Components +- Use functional components with hooks +- Follow the component structure: + 1. Imports + 2. Type definitions + 3. Component definition + 4. Helper functions (if needed) + 5. Exports + +Example: +```typescript +import { useState } from 'react'; +import { Button } from '@/components/ui/Button'; + +interface MyComponentProps { + title: string; + onSubmit: () => void; +} + +export function MyComponent({ title, onSubmit }: MyComponentProps) { + const [isLoading, setIsLoading] = useState(false); + + const handleClick = async () => { + setIsLoading(true); + await onSubmit(); + setIsLoading(false); + }; + + return ( +
+

{title}

+ +
+ ); +} +``` + +### Styling +- Use Tailwind CSS utility classes +- Use `cn()` utility from `tailwind-merge` to merge classes +- Follow the mobile-first responsive design approach +- Use CSS variables for theme colors (defined in `globals.css`) + +### File Naming +- Components: PascalCase (e.g., `MyComponent.tsx`) +- Utilities: camelCase (e.g., `formatDate.ts`) +- Pages: lowercase (e.g., `page.tsx`, `layout.tsx`) +- Types: PascalCase with `.types.ts` suffix (e.g., `User.types.ts`) + +### Import Organization +```typescript +// 1. External dependencies +import { useState } from 'react'; +import { useTranslations } from 'next-intl'; + +// 2. Internal components and utilities +import { Button } from '@/components/ui/Button'; +import { formatDate } from '@/utils/date'; + +// 3. Types +import type { User } from '@/types/User.types'; + +// 4. Styles (if any) +import styles from './Component.module.css'; +``` + +### Internationalization +- All user-facing text must be internationalized +- Use `useTranslations()` hook from `next-intl` +- Add translation keys to all language files in `src/messages/` + +Example: +```typescript +import { useTranslations } from 'next-intl'; + +export function WelcomeMessage() { + const t = useTranslations('home'); + + return

{t('welcome')}

; +} +``` + +## Testing + +### Unit Tests +- Write tests for utility functions and complex logic +- Use Vitest as the testing framework +- Place test files next to the code they test with `.test.ts` or `.test.tsx` extension + +Example: +```typescript +// formatDate.test.ts +import { describe, it, expect } from 'vitest'; +import { formatDate } from './formatDate'; + +describe('formatDate', () => { + it('formats date correctly', () => { + const date = new Date('2024-01-15'); + expect(formatDate(date)).toBe('15.01.2024'); + }); +}); +``` + +### Running Tests +```bash +# Run tests in watch mode +npm test + +# Run tests once with coverage +npm run test:coverage +``` + +## Commit Guidelines + +### Commit Message Format +``` +: + + +``` + +### Types +- `feat` - New feature +- `fix` - Bug fix +- `docs` - Documentation changes +- `style` - Code style changes (formatting, etc.) +- `refactor` - Code refactoring +- `test` - Adding or updating tests +- `chore` - Maintenance tasks + +### Examples +```bash +git commit -m "feat: add contact form validation" +git commit -m "fix: resolve mobile navigation menu bug" +git commit -m "docs: update README with deployment instructions" +``` + +## Questions? + +If you have any questions or need help, please: +- Check existing documentation +- Review similar code in the codebase +- Open an issue for discussion + +--- + +**Built with Next.js + TypeScript + Tailwind CSS** From 52fcf579ad08f26a3c3542a2e60278d99aae41d5 Mon Sep 17 00:00:00 2001 From: Damjan Savic Date: Sun, 25 Jan 2026 06:32:24 +0100 Subject: [PATCH 2/5] auto-claude: subtask-1-2 - Document code style conventions and linting --- CONTRIBUTING.md | 63 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 65e2b35..48b62e4 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -217,6 +217,69 @@ Ensure the build completes without errors before submitting. ## Code Style & Standards +### ESLint & Code Linting + +This project uses ESLint to enforce code quality and consistency. The configuration extends Next.js recommended presets: + +**ESLint Configuration** (`.eslintrc.json`): +```json +{ + "extends": [ + "next/core-web-vitals", + "next/typescript" + ] +} +``` + +**Key ESLint Rules:** +- `next/core-web-vitals` - Enforces Next.js best practices and Core Web Vitals optimizations +- `next/typescript` - TypeScript-specific rules for Next.js applications + +**Running the Linter:** +```bash +# Run ESLint in strict mode (recommended before committing) +npm run lint + +# The linter will check for: +# - Code quality issues +# - Next.js best practices violations +# - TypeScript type errors +# - Unused variables and imports +# - Accessibility issues +``` + +**Important:** +- Always run `npm run lint` before committing changes +- Fix all linting errors - the project uses `--strict` mode +- The linter runs automatically during the build process +- ESLint errors will prevent successful production builds + +### Code Formatting + +**General Formatting Guidelines:** +- **Indentation:** 2 spaces (no tabs) +- **Line Length:** Aim for 80-100 characters, hard limit at 120 +- **Semicolons:** Required at the end of statements +- **Quotes:** Single quotes for strings (except in JSX/TSX where double quotes are preferred) +- **Trailing Commas:** Use trailing commas in multi-line objects and arrays + +**Example:** +```typescript +// ✅ Good +const user = { + name: 'John Doe', + email: 'john@example.com', + role: 'admin', +}; + +// ❌ Bad +const user = { + name: "John Doe", + email: "john@example.com", + role: "admin" +} +``` + ### TypeScript - Use TypeScript for all new files - Define proper types and interfaces From 5822ceb7c4325c2db17a5d3728904de04a1903bf Mon Sep 17 00:00:00 2001 From: Damjan Savic Date: Sun, 25 Jan 2026 06:34:13 +0100 Subject: [PATCH 3/5] auto-claude: subtask-1-3 - Document testing requirements and setup --- CONTRIBUTING.md | 131 +++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 119 insertions(+), 12 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 48b62e4..a733bb0 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -372,34 +372,141 @@ export function WelcomeMessage() { ## Testing -### Unit Tests -- Write tests for utility functions and complex logic -- Use Vitest as the testing framework -- Place test files next to the code they test with `.test.ts` or `.test.tsx` extension +This project uses **Vitest** as its testing framework. Vitest is a fast, modern test runner built for Vite-based projects, offering: +- Lightning-fast execution with native ESM support +- Jest-compatible API for easy migration and familiarity +- First-class TypeScript support +- Built-in coverage reporting with c8 + +**Current State:** The project is fully configured with Vitest, but **no tests currently exist**. We encourage contributors to add tests for new features and gradually improve test coverage. + +### Test Setup + +Vitest is already configured in `package.json`: +```json +{ + "scripts": { + "test": "vitest", + "test:coverage": "vitest run --coverage" + }, + "devDependencies": { + "vitest": "^1.3.1" + } +} +``` + +### Running Tests + +```bash +# Run tests in watch mode (recommended during development) +npm test + +# Run tests once (useful for CI/CD) +npm run test:coverage + +# The test runner will: +# - Automatically detect .test.ts, .test.tsx, .spec.ts, .spec.tsx files +# - Re-run tests when files change (in watch mode) +# - Display coverage reports (with --coverage flag) +``` + +### Writing Tests + +When adding tests to this project, follow these conventions: + +#### 1. **File Naming & Location** +- Place test files **next to the code they test** +- Use `.test.ts` or `.test.tsx` extension for test files +- Match the filename of the file being tested + +Example: +``` +src/utils/ +├── formatDate.ts # Source file +└── formatDate.test.ts # Test file +``` + +#### 2. **Test Structure** +- Use `describe` blocks to group related tests +- Use `it` or `test` for individual test cases +- Write descriptive test names that explain the expected behavior Example: ```typescript -// formatDate.test.ts +// src/utils/formatDate.test.ts import { describe, it, expect } from 'vitest'; import { formatDate } from './formatDate'; describe('formatDate', () => { - it('formats date correctly', () => { + it('formats date in DD.MM.YYYY format', () => { const date = new Date('2024-01-15'); expect(formatDate(date)).toBe('15.01.2024'); }); + + it('handles invalid dates gracefully', () => { + const invalidDate = new Date('invalid'); + expect(formatDate(invalidDate)).toBe('Invalid Date'); + }); }); ``` -### Running Tests -```bash -# Run tests in watch mode -npm test +#### 3. **Component Testing** +For React components, use Vitest with React Testing Library patterns: -# Run tests once with coverage -npm run test:coverage +```typescript +// src/components/Button.test.tsx +import { describe, it, expect } from 'vitest'; +import { render, screen } from '@testing-library/react'; +import { Button } from './Button'; + +describe('Button', () => { + it('renders with correct text', () => { + render(); + expect(screen.getByText('Click me')).toBeInTheDocument(); + }); +}); ``` +#### 4. **What to Test** +- **Utility functions** - Pure functions and helpers +- **Business logic** - Complex calculations and transformations +- **Hooks** - Custom React hooks with multiple states +- **Components** - User interactions and rendering logic +- **API integrations** - Service layer functions (use mocks) + +#### 5. **Testing Conventions** +- **Arrange-Act-Assert** pattern for test structure +- **One assertion per test** when possible (for clarity) +- **Mock external dependencies** (API calls, database, etc.) +- **Test edge cases** and error conditions +- **Use meaningful test data** that reflects real usage + +Example: +```typescript +describe('calculateDiscount', () => { + it('applies 10% discount for orders over $100', () => { + // Arrange + const orderTotal = 150; + const discountThreshold = 100; + + // Act + const result = calculateDiscount(orderTotal, discountThreshold); + + // Assert + expect(result).toBe(135); // 150 - 15 (10%) + }); +}); +``` + +### Test Coverage Goals + +While we don't enforce strict coverage requirements, aim for: +- **80%+ coverage** for utility functions and business logic +- **60%+ coverage** for components +- **100% coverage** for critical paths (authentication, payments, data validation) + +Run `npm run test:coverage` to see current coverage reports. + ## Commit Guidelines ### Commit Message Format From 65f3a28435f1ad19a6045611a4ce776366d617f8 Mon Sep 17 00:00:00 2001 From: Damjan Savic Date: Sun, 25 Jan 2026 06:36:41 +0100 Subject: [PATCH 4/5] auto-claude: subtask-1-4 - Document content workflows (portfolio projects and blog posts) - Added comprehensive Content Workflows section to CONTRIBUTING.md - Documented how to add portfolio projects with TypeScript data structure example - Documented how to add blog posts using MDX format - Documented i18n workflow for translations with JSON structure - Added file location conventions for all locales (de, en, sr) - Included step-by-step instructions and best practices Co-Authored-By: Claude Sonnet 4.5 --- CONTRIBUTING.md | 408 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 408 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a733bb0..78c5aa3 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -9,6 +9,7 @@ Thank you for your interest in contributing to this project! This guide will hel - [Tech Stack](#tech-stack) - [Project Structure](#project-structure) - [Development Workflow](#development-workflow) +- [Content Workflows](#content-workflows) - [Code Style & Standards](#code-style--standards) - [Testing](#testing) - [Commit Guidelines](#commit-guidelines) @@ -215,6 +216,413 @@ npm run build Ensure the build completes without errors before submitting. +## 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')}

+ + +