Files
portfolio/.github/copilot-instructions.md
denshooter a5dba298f3
Some checks failed
Dev Deployment (Zero Downtime) / deploy-dev (push) Failing after 9m26s
feat: major UI/UX overhaul, snippets system, and performance fixes
2026-02-16 12:31:40 +01:00

8.0 KiB

Portfolio Project Instructions

This is Dennis Konkol's personal portfolio (dk0.dev) - a Next.js 15 portfolio with Directus CMS integration, n8n automation, and a "liquid" design system.

Build, Test, and Lint

Development

npm run dev          # Full dev environment (Docker + Next.js)
npm run dev:simple   # Next.js only (no Docker dependencies)
npm run dev:next     # Plain Next.js dev server

Build & Deploy

npm run build        # Production build (standalone mode)
npm run start        # Start production server

Testing

# Unit tests (Jest)
npm run test                # Run all unit tests
npm run test:watch          # Watch mode
npm run test:coverage       # With coverage report

# E2E tests (Playwright)
npm run test:e2e           # Run all E2E tests
npm run test:e2e:ui        # Interactive UI mode
npm run test:critical      # Critical paths only
npm run test:hydration     # Hydration tests only

Linting

npm run lint         # Run ESLint
npm run lint:fix     # Auto-fix issues

Database (Prisma)

npm run db:generate  # Generate Prisma client
npm run db:push      # Push schema to database
npm run db:studio    # Open Prisma Studio
npm run db:seed      # Seed database

Architecture Overview

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 (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, GraphQL, optional)
  • Automation: n8n webhooks (status, chat, hardcover, image generation)
  • i18n: next-intl (EN + DE)
  • Monitoring: Sentry
  • Deployment: Docker (standalone mode) + Nginx

Key Directories

app/
  [locale]/          # i18n routes (en, de)
    page.tsx         # Homepage sections
    projects/        # Project listing + detail pages
  api/               # API routes
    book-reviews/    # Book reviews from Directus
    hobbies/         # Hobbies from Directus
    n8n/             # n8n webhook proxies
    projects/        # Projects (PostgreSQL + Directus)
    tech-stack/      # Tech stack from Directus
  components/        # React components
lib/
  directus.ts        # Directus GraphQL client (no SDK)
  auth.ts            # Auth + rate limiting
  translations-loader.ts  # i18n loaders for server components
prisma/
  schema.prisma      # Database schema
messages/
  en.json            # English translations
  de.json            # German translations

Data Source Fallback Chain

The architecture prioritizes resilience with this fallback hierarchy:

  1. Directus CMS (if DIRECTUS_STATIC_TOKEN configured)
  2. PostgreSQL (for projects, analytics)
  3. JSON files (messages/*.json)
  4. Hardcoded defaults
  5. Display key itself (last resort)

Critical: The site never crashes if external services (Directus, PostgreSQL, n8n, Redis) are unavailable. All API routes return graceful fallbacks.

CMS Integration (Directus)

  • 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 system (M2O to languages)
  • Locale mapping: enen-US, dede-DE
  • API routes export runtime='nodejs', dynamic='force-dynamic' and include a source field in JSON responses (directus|fallback|error)

n8n Integration

  • Webhook base URL: N8N_WEBHOOK_URL env var
  • Auth via N8N_SECRET_TOKEN and/or N8N_API_KEY headers
  • All endpoints have rate limiting and 10s timeout protection
  • Hardcover reading data cached for 5 minutes

Key Conventions

i18n (Internationalization)

  • Supported locales: en (English), de (German)
  • Primary source: Static JSON files in messages/en.json and messages/de.json
  • Optional override: Directus CMS messages collection
  • Server components: Use getHeroTranslations(), getNavTranslations(), etc. from lib/translations-loader.ts
  • Client components: Use useTranslations("key.path") from next-intl
  • Locale mapping: Middleware defines ["en", "de"] which must match app/[locale]/layout.tsx

Component Patterns

  • Client components: Mark with "use client" for interactive/data-fetching parts
  • Data loading: Use useEffect for client-side fetching on mount
  • Animations: Framer Motion variants pattern with staggerContainer + fadeInUp
  • Loading states: Every async component needs a matching Skeleton component

Design System ("Liquid Editorial Bento")

  • Core palette: Cream (#fdfcf8), Stone (#0c0a09), Emerald (#10b981)
  • Custom colors: Prefixed with liquid-* (sky, mint, lavender, pink, rose, peach, coral, teal, lime)
  • Card style: Gradient backgrounds (bg-gradient-to-br from-liquid-*/15 via-liquid-*/10 to-liquid-*/15)
  • Glassmorphism: Use backdrop-blur-sm with border-2 and rounded-xl
  • Typography: Headlines uppercase, tracking-tighter, with accent point at end
  • Layout: Bento Grid for new features (no floating overlays)

File Naming

  • Components: PascalCase in app/components/ (e.g., About.tsx)
  • API routes: kebab-case directories in app/api/ (e.g., book-reviews/)
  • Lib utilities: kebab-case in lib/ (e.g., email-obfuscate.ts)

Code Style

  • Language: Code in English, user-facing text via i18n
  • TypeScript: No any types - use interfaces from lib/directus.ts or app/_ui/
  • Error handling: All API calls must catch errors with fallbacks
  • Error logging: Only in development mode (process.env.NODE_ENV === "development")
  • Commit messages: Conventional Commits (feat:, fix:, chore:)
  • No emojis: Unless explicitly requested

Testing Notes

  • Jest environment: JSDOM with mocks for window.matchMedia and IntersectionObserver
  • Playwright: Uses plain Next.js dev server (no Docker) with NODE_ENV=development to avoid Edge runtime issues
  • Transform: ESM modules (react-markdown, remark-*, etc.) are transformed via transformIgnorePatterns
  • After UI changes: Run npm run test to verify no regressions

Docker & Deployment

  • Standalone mode: next.config.ts uses output: "standalone" for optimized Docker builds
  • Branches: dev → staging, production → live
  • CI/CD: Gitea Actions (.gitea/workflows/)
  • Verify Docker builds: Always test Docker builds after changes to next.config.ts or dependencies

Common Tasks

Adding a CMS-managed section

  1. Define GraphQL query + types in lib/directus.ts
  2. Create API route in app/api/<name>/route.ts with runtime='nodejs' and dynamic='force-dynamic'
  3. Create component in app/components/<Name>.tsx
  4. Add i18n keys to messages/en.json and messages/de.json
  5. Integrate into parent component

Adding i18n strings

  1. Add keys to both messages/en.json and messages/de.json
  2. Use useTranslations("key.path") in client components
  3. Use getTranslations("key.path") in server components

Working with Directus

  • All queries go through directusRequest() in lib/directus.ts
  • Uses GraphQL endpoint (/graphql) with 2s timeout
  • Returns null on failure (graceful degradation)
  • Translations filtered by languages_code.code matching Directus locale

Environment Variables

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 & Cache

DATABASE_URL=postgresql://...
REDIS_URL=redis://...

Optional

SENTRY_DSN=...
NEXT_PUBLIC_BASE_URL=https://dk0.dev

Documentation References

  • Operations guide: docs/OPERATIONS.md
  • Locale system: docs/LOCALE_SYSTEM.md
  • CMS guide: docs/CMS_GUIDE.md
  • Testing & deployment: docs/TESTING_AND_DEPLOYMENT.md