Added comprehensive documentation for: - Branch naming conventions with patterns and examples - Pull request guidelines including pre-submission checklist, PR template, and best practices - Legacy code explanation for components-vite, pages-vite, and locales-old directories - Migration status and what to avoid touching during Vite to Next.js migration - Updated table of contents with new sections Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
1222 lines
38 KiB
Markdown
1222 lines
38 KiB
Markdown
# 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 <repository-url>
|
|
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/<description>` | `feature/add-dark-mode` | New features or enhancements |
|
|
| **Bug Fix** | `fix/<description>` | `fix/mobile-nav-bug` | Bug fixes and error corrections |
|
|
| **Documentation** | `docs/<description>` | `docs/update-readme` | Documentation updates |
|
|
| **Refactoring** | `refactor/<description>` | `refactor/contact-form` | Code restructuring without behavior changes |
|
|
| **Testing** | `test/<description>` | `test/add-unit-tests` | Adding or updating tests |
|
|
| **Chore** | `chore/<description>` | `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:
|
|
|
|
```
|
|
<type>: <brief description>
|
|
```
|
|
|
|
**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
|
|
|
|

|
|
|
|
### Custom Components (MDX Feature)
|
|
|
|
You can embed React components:
|
|
|
|
<CustomCallout type="info">
|
|
This is a custom component in MDX!
|
|
</CustomCallout>
|
|
```
|
|
|
|
#### 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 (
|
|
<form>
|
|
<h2>{t('title')}</h2>
|
|
<input placeholder={t('name')} />
|
|
<input placeholder={t('email')} />
|
|
<textarea placeholder={t('message')} />
|
|
<button>{t('submit')}</button>
|
|
</form>
|
|
);
|
|
}
|
|
```
|
|
|
|
#### Translation Best Practices
|
|
|
|
1. **Maintain structure consistency** - Use the same JSON structure across all locale files
|
|
2. **Use semantic keys** - Keys should describe the content, not the translation (e.g., `actions.submit` not `actions.submitButton`)
|
|
3. **Organize by feature** - Group related translations under feature namespaces
|
|
4. **Test all locales** - Always verify translations in all three languages (de, en, sr)
|
|
5. **Avoid hardcoded text** - All user-facing text should come from translation files
|
|
6. **Keep translations concise** - Match the tone and length of the original language when possible
|
|
|
|
#### Locale-Specific File Naming
|
|
|
|
When creating locale-specific content files, follow these conventions:
|
|
|
|
| Content Type | File Location | Naming Pattern | Example |
|
|
|--------------|---------------|----------------|---------|
|
|
| **Portfolio Projects** | `src/i18n/locales-old/[locale]/portfolio/projects/` | `project-slug.ts` | `ai-data-reader.ts` |
|
|
| **Blog Posts** | `src/content/blog/[locale]/` | `post-slug.mdx` | `react-best-practices.mdx` |
|
|
| **Translations** | `src/messages/` | `[locale].json` | `de.json` |
|
|
| **Page Content** | `src/app/[locale]/[page]/` | Standard Next.js routing | `page.tsx` |
|
|
|
|
#### Switching Between Locales
|
|
|
|
The application automatically detects the user's locale based on:
|
|
1. URL path segment (e.g., `/de/`, `/en/`, `/sr/`)
|
|
2. Browser language settings
|
|
3. User preference stored in cookies
|
|
|
|
Users can manually switch languages using the language selector in the navigation menu.
|
|
|
|
## 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
|
|
- 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 (
|
|
<div>
|
|
<h2>{title}</h2>
|
|
<Button onClick={handleClick} disabled={isLoading}>
|
|
Submit
|
|
</Button>
|
|
</div>
|
|
);
|
|
}
|
|
```
|
|
|
|
### 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 <h1>{t('welcome')}</h1>;
|
|
}
|
|
```
|
|
|
|
## Testing
|
|
|
|
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
|
|
// src/utils/formatDate.test.ts
|
|
import { describe, it, expect } from 'vitest';
|
|
import { formatDate } from './formatDate';
|
|
|
|
describe('formatDate', () => {
|
|
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');
|
|
});
|
|
});
|
|
```
|
|
|
|
#### 3. **Component Testing**
|
|
For React components, use Vitest with React Testing Library patterns:
|
|
|
|
```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(<Button>Click me</Button>);
|
|
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.
|
|
|
|
## Legacy Code & Migration
|
|
|
|
This project is currently undergoing a **migration from Vite + React to Next.js 15**. During this transition, you'll encounter legacy directories that contain the old Vite-based code. Understanding these directories is crucial to avoid conflicts and ensure a smooth migration process.
|
|
|
|
### Legacy Directories
|
|
|
|
The following directories contain **legacy code from the Vite implementation** and should be handled with care:
|
|
|
|
| Directory | Type | Description | Status |
|
|
|-----------|------|-------------|--------|
|
|
| **`src/components-vite/`** | Components | Original Vite-based React components | ⚠️ Legacy - Being migrated |
|
|
| **`src/pages-vite/`** | Pages | Vite-based page components | ⚠️ Legacy - Being migrated |
|
|
| **`src/i18n/locales-old/`** | Translations | Old translation structure for portfolio projects | ⚠️ Legacy - Partially used |
|
|
|
|
### What Are These Directories?
|
|
|
|
#### `src/components-vite/`
|
|
- **Purpose:** Contains the original UI components built for the Vite + React architecture
|
|
- **Contents:** Buttons, forms, navigation, cards, and other reusable components
|
|
- **Status:** Being gradually replaced with Next.js-compatible components in `src/components/`
|
|
- **Why it exists:** Serves as reference during migration and fallback for unmigrated features
|
|
|
|
#### `src/pages-vite/`
|
|
- **Purpose:** Original Vite page components (before Next.js App Router)
|
|
- **Contents:** Home, About, Portfolio, Blog, Contact pages and their subpages
|
|
- **Status:** Being replaced with Next.js App Router pages in `src/app/[locale]/`
|
|
- **Why it exists:** Historical reference and for any unmigrated route logic
|
|
|
|
#### `src/i18n/locales-old/`
|
|
- **Purpose:** Legacy translation system for portfolio projects
|
|
- **Contents:** TypeScript-based portfolio project definitions (see [Adding Portfolio Projects](#adding-portfolio-projects))
|
|
- **Status:** **Still in active use** for portfolio projects until migration to MDX is complete
|
|
- **Why it exists:** Portfolio projects are currently defined as `.ts` files here, not yet moved to MDX
|
|
|
|
### Migration Status
|
|
|
|
The migration from Vite to Next.js is **actively ongoing**. Here's the current state:
|
|
|
|
✅ **Completed:**
|
|
- Next.js 15 App Router setup
|
|
- Internationalization with `next-intl`
|
|
- Blog system migrated to MDX
|
|
- Main layout and routing structure
|
|
- New component architecture in `src/components/`
|
|
|
|
🚧 **In Progress:**
|
|
- Migrating Vite components to Next.js components
|
|
- Converting portfolio project definitions from TypeScript to MDX
|
|
- Updating old translation structure
|
|
- Removing dependency on legacy directories
|
|
|
|
❌ **Not Yet Started:**
|
|
- Complete removal of `components-vite/`
|
|
- Complete removal of `pages-vite/`
|
|
- Full MDX migration for portfolio projects
|
|
|
|
### What to Avoid During Migration
|
|
|
|
To prevent conflicts, regression, and wasted effort, **DO NOT**:
|
|
|
|
#### ❌ Don't Modify Legacy Code
|
|
- **Don't edit files in `src/components-vite/`** - These components are being phased out
|
|
- **Don't edit files in `src/pages-vite/`** - Page structure has moved to `src/app/[locale]/`
|
|
- **Don't add new features to legacy directories** - Build new features in the Next.js structure
|
|
|
|
#### ❌ Don't Create Dependencies on Legacy Code
|
|
- **Don't import from `components-vite/` in new code** - Use `src/components/` instead
|
|
- **Don't reference `pages-vite/` in new routes** - Use App Router pages in `src/app/`
|
|
- **Don't expand the legacy translation structure** - Add new content to `src/messages/` or MDX files
|
|
|
|
#### ❌ Don't Delete Legacy Code Without Coordination
|
|
- **Don't delete legacy directories** - They may still be referenced by unmigrated code
|
|
- **Don't remove legacy imports** - Check if they're still in use first
|
|
- **Don't assume code is unused** - Verify before removing
|
|
|
|
### What You SHOULD Do
|
|
|
|
#### ✅ Use the New Architecture
|
|
- **Create new components in `src/components/`** - Follow Next.js patterns
|
|
- **Add new pages in `src/app/[locale]/`** - Use App Router file conventions
|
|
- **Add translations to `src/messages/`** - Use next-intl JSON format
|
|
- **Create blog posts as MDX** - Follow the [Adding Blog Posts](#adding-blog-posts) guide
|
|
|
|
#### ✅ Reference Legacy Code for Patterns
|
|
- **Study `components-vite/` for UI patterns** - Understand the design system, then recreate in new structure
|
|
- **Check `pages-vite/` for page logic** - Learn how pages work, then build with App Router
|
|
- **Learn from `locales-old/` structure** - Understand content organization before migrating
|
|
|
|
#### ✅ Help with Migration (Advanced Contributors)
|
|
If you want to help migrate legacy code:
|
|
1. **Discuss first** - Open an issue or comment on existing migration issues
|
|
2. **Check migration plan** - Ensure your work aligns with the overall strategy
|
|
3. **Migrate one component/page at a time** - Small, focused PRs
|
|
4. **Test thoroughly** - Ensure migrated code works identically
|
|
5. **Update imports** - Fix all references to migrated code
|
|
6. **Document changes** - Note what was migrated and any breaking changes
|
|
|
|
### Special Case: Portfolio Projects (`locales-old/`)
|
|
|
|
**Current State:** Portfolio projects are **still using the legacy structure** in `src/i18n/locales-old/[locale]/portfolio/projects/`.
|
|
|
|
**Why:** These TypeScript-based project definitions have not yet been migrated to MDX.
|
|
|
|
**What this means for you:**
|
|
- ✅ **You can still add portfolio projects** using the TypeScript structure (see [Adding Portfolio Projects](#adding-portfolio-projects))
|
|
- ⚠️ **This structure may change** in the future when projects are migrated to MDX
|
|
- 🔄 **Follow existing patterns** - Use the same structure as existing projects
|
|
|
|
**Future Plan:** Portfolio projects will eventually be migrated to MDX format (similar to blog posts) for consistency and easier content management.
|
|
|
|
### Questions About Legacy Code?
|
|
|
|
If you're unsure whether code is legacy or active:
|
|
1. **Check the import path** - Legacy code is in `-vite/` or `-old/` directories
|
|
2. **Look for usage** - Search the codebase to see if it's imported anywhere
|
|
3. **Ask first** - Open an issue or discussion if you're unsure
|
|
4. **When in doubt, don't touch it** - Stick to the new architecture in `src/app/`, `src/components/`, and `src/messages/`
|
|
|
|
## Commit Guidelines
|
|
|
|
### Commit Message Format
|
|
```
|
|
<type>: <subject>
|
|
|
|
<body>
|
|
```
|
|
|
|
### 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**
|