All checks were successful
Gitea CI / test-build (push) Successful in 11m8s
Remove @sentry/nextjs and all related files since it was never actively used. - Delete sentry.server.config.ts, sentry.edge.config.ts - Delete sentry-example-page and sentry-example-api routes - Clean up instrumentation.ts, global-error.tsx, middleware.ts - Remove Sentry env vars from env.example and docs - Update CLAUDE.md, copilot-instructions.md, PRODUCTION_READINESS.md Middleware bundle reduced from 86KB to 34.8KB (-51KB). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
156 lines
5.8 KiB
Markdown
156 lines
5.8 KiB
Markdown
# CLAUDE.md - Portfolio Project Guide
|
|
|
|
## Project Overview
|
|
|
|
Personal portfolio website for Dennis Konkol (dk0.dev). Built with Next.js 15 (App Router), TypeScript, Tailwind CSS, and Framer Motion. Uses a "liquid" design system with soft gradient colors and glassmorphism effects.
|
|
|
|
## Tech Stack
|
|
|
|
- **Framework**: Next.js 15 (App Router), TypeScript 5.9
|
|
- **Styling**: Tailwind CSS 3.4 with custom `liquid-*` color tokens
|
|
- **Theming**: `next-themes` for Dark Mode support (system/light/dark)
|
|
- **Animations**: Framer Motion 12
|
|
- **3D**: Three.js + React Three Fiber (shader gradient background)
|
|
- **Database**: PostgreSQL via Prisma ORM
|
|
- **Cache**: Redis (optional)
|
|
- **CMS**: Directus (self-hosted, REST/GraphQL, optional)
|
|
- **Automation**: n8n webhooks (status, chat, hardcover, image generation)
|
|
- **i18n**: next-intl (EN + DE), message files in `messages/`
|
|
- **Monitoring**: Console error logging (development mode only)
|
|
- **Deployment**: Docker + Nginx, CI via Gitea Actions
|
|
|
|
## Commands
|
|
|
|
```bash
|
|
npm run dev # Full dev environment (Docker + Next.js)
|
|
npm run dev:simple # Next.js only (no Docker)
|
|
npm run dev:next # Plain Next.js dev server
|
|
npm run build # Production build
|
|
npm run lint # ESLint
|
|
npm run test # Jest unit tests
|
|
npm run test:e2e # Playwright E2E tests
|
|
```
|
|
|
|
## Project Structure
|
|
|
|
```
|
|
app/
|
|
[locale]/ # i18n routes (en, de)
|
|
page.tsx # Homepage (hero, about, projects, contact)
|
|
projects/ # Project listing + detail pages
|
|
api/ # API routes
|
|
book-reviews/ # Book reviews from Directus CMS
|
|
content/ # CMS content pages
|
|
hobbies/ # Hobbies from Directus
|
|
n8n/ # n8n webhook proxies
|
|
hardcover/ # Currently reading (Hardcover API via n8n)
|
|
status/ # Activity status (coding, music, gaming)
|
|
chat/ # AI chatbot
|
|
generate-image/ # AI image generation
|
|
projects/ # Projects API (PostgreSQL + Directus fallback)
|
|
tech-stack/ # Tech stack from Directus
|
|
components/ # React components
|
|
About.tsx # About section (tech stack, hobbies, books)
|
|
CurrentlyReading.tsx # Currently reading widget (n8n/Hardcover)
|
|
ReadBooks.tsx # Read books with ratings (Directus CMS)
|
|
Projects.tsx # Featured projects section
|
|
Hero.tsx # Hero section
|
|
Contact.tsx # Contact form
|
|
lib/
|
|
directus.ts # Directus GraphQL client (no SDK)
|
|
auth.ts # Auth utilities + rate limiting
|
|
prisma/
|
|
schema.prisma # Database schema
|
|
messages/
|
|
en.json # English translations
|
|
de.json # German translations
|
|
docs/ # Documentation
|
|
```
|
|
|
|
## Architecture Patterns
|
|
|
|
### Data Source Hierarchy (Fallback Chain)
|
|
1. Directus CMS (if configured via `DIRECTUS_STATIC_TOKEN`)
|
|
2. PostgreSQL (for projects, analytics)
|
|
3. JSON files (`messages/*.json`)
|
|
4. Hardcoded defaults
|
|
5. Display key itself as last resort
|
|
|
|
All external data sources fail gracefully - the site never crashes if Directus, PostgreSQL, n8n, or Redis are unavailable.
|
|
|
|
### CMS Integration (Directus)
|
|
- REST/GraphQL calls via `lib/directus.ts` (no Directus SDK)
|
|
- Collections: `tech_stack_categories`, `tech_stack_items`, `hobbies`, `content_pages`, `projects`, `book_reviews`
|
|
- Translations use Directus native translation system (M2O to `languages`)
|
|
- Locale mapping: `en` -> `en-US`, `de` -> `de-DE`
|
|
|
|
### n8n Integration
|
|
- Webhook base URL: `N8N_WEBHOOK_URL` env var
|
|
- Auth via `N8N_SECRET_TOKEN` and/or `N8N_API_KEY` headers
|
|
- All n8n endpoints have rate limiting and timeout protection (10s)
|
|
- Hardcover data cached for 5 minutes
|
|
|
|
### Component Patterns
|
|
- Client components with `"use client"` for interactive/data-fetching parts
|
|
- `useEffect` for data loading on mount
|
|
- `useTranslations` from next-intl for i18n
|
|
- Framer Motion `variants` pattern with `staggerContainer` + `fadeInUp`
|
|
- Gradient cards with `liquid-*` color tokens and `backdrop-blur-sm`
|
|
|
|
## Design System
|
|
|
|
Custom Tailwind colors prefixed with `liquid-`:
|
|
- `liquid-sky`, `liquid-mint`, `liquid-lavender`, `liquid-pink`
|
|
- `liquid-rose`, `liquid-peach`, `liquid-coral`, `liquid-teal`, `liquid-lime`
|
|
|
|
Cards use gradient backgrounds (`bg-gradient-to-br from-liquid-*/15 via-liquid-*/10 to-liquid-*/15`) with `border-2` and `rounded-xl`.
|
|
|
|
## Key Environment Variables
|
|
|
|
```bash
|
|
# Required for CMS
|
|
DIRECTUS_URL=https://cms.dk0.dev
|
|
DIRECTUS_STATIC_TOKEN=...
|
|
|
|
# Required for n8n features
|
|
N8N_WEBHOOK_URL=https://n8n.dk0.dev
|
|
N8N_SECRET_TOKEN=...
|
|
N8N_API_KEY=...
|
|
|
|
# Database
|
|
DATABASE_URL=postgresql://...
|
|
|
|
# Optional
|
|
REDIS_URL=redis://...
|
|
```
|
|
|
|
## Conventions
|
|
|
|
- Language: Code in English, user-facing text via i18n (EN + DE)
|
|
- Commit messages: Conventional Commits (`feat:`, `fix:`, `chore:`)
|
|
- Components: PascalCase files in `app/components/`
|
|
- API routes: kebab-case directories in `app/api/`
|
|
- CMS data always has a static fallback - never rely solely on Directus
|
|
- Error logging: Only in `development` mode (`process.env.NODE_ENV === "development"`)
|
|
- No emojis in code unless explicitly requested
|
|
|
|
## Common Tasks
|
|
|
|
### Adding a new CMS-managed section
|
|
1. Define the GraphQL query + types in `lib/directus.ts`
|
|
2. Create an API route in `app/api/<name>/route.ts`
|
|
3. Create a component in `app/components/<Name>.tsx`
|
|
4. Add i18n keys to `messages/en.json` and `messages/de.json`
|
|
5. Integrate into the parent component (usually `About.tsx`)
|
|
|
|
### Adding i18n strings
|
|
1. Add keys to `messages/en.json` and `messages/de.json`
|
|
2. Access via `useTranslations("key.path")` in client components
|
|
3. Or `getTranslations("key.path")` in server components
|
|
|
|
### Working with Directus collections
|
|
- All queries go through `directusRequest()` in `lib/directus.ts`
|
|
- Uses GraphQL endpoint (`/graphql`)
|
|
- 2-second timeout, graceful null fallback
|
|
- Translations filtered by `languages_code.code` matching Directus locale
|