Design system contracts, docs search, and usage validation for @digitaltableteur components.
_____ _______
| __ \ |__ __|
| | | | | |
| | | | | |
| |__| | | |
|_____/ |_|
"Iteration beats perfectionβship today, learn tomorrow, refine forever."
Digitaltableteur is a hybrid monorepo portfolio website featuring both Next.js 16 (production) and Vite (legacy) applications. Built with React 19 and TypeScript 6.x, it showcases a comprehensive design system, multi-language support (EN/FI/SV), AI-powered chat interface, and enterprise-grade tooling including Sentry observability, Linear issue management, and MCP (Model Context Protocol) integrations.
# Clone repository
git clone https://github.com/PetriLahdelma/digitaltableteur.git
cd digitaltableteur
# Install dependencies
npm install
# Copy environment template
cp .env.example .env.local
Required for development:
# Analytics
VITE_GA_ID=G-XXXXXXXXXX # Google Analytics 4
# Email Services
VITE_EMAILJS_SERVICE_ID=service_xxx
VITE_EMAILJS_TEMPLATE_ID=template_xxx
VITE_EMAILJS_PUBLIC_KEY=xxx
# MCP Servers (optional for enhanced AI features)
FIGMA_TOKEN=figd_xxx # Figma design access
GITHUB_MCP_PAT=github_pat_xxx # GitHub operations
CONTEXT7_API_KEY=xxx # Context7 documentation access
# Linear Issue Management (optional)
LINEAR_API_KEY=lin_api_xxx
LINEAR_TEAM_ID=xxx
LINEAR_PROJECT_ID=xxx
# Akaunting Accounting (optional, self-hosted)
AKAUNTING_API_USERNAME=admin@digitaltableteur.com
AKAUNTING_API_PASSWORD=xxx
AKAUNTING_COMPANY_ID=1
Production only:
CV_PASSWORD=xxx # Secure resume download
OPENAI_API_KEY=sk-xxx # AI chat functionality
SENTRY_DSN=https://xxx@xxx.ingest.sentry.io/xxx
SENTRY_AUTH_TOKEN=xxx # Source map upload
See .env.example for complete list with descriptions.
# Next.js dev server (production app)
npm run dev # http://localhost:3001
# Storybook component development
npm run storybook # http://localhost:6010
# Type checking
npm run typecheck # TypeScript validation across project
# Linting
npm run lint # ESLint + Stylelint
npm run lint:fix # Auto-fix linting issues
# Testing
npm test # Run all tests (Vitest)
npm run test:watch # Watch mode
npm run test:coverage # Coverage report (>80% target)
npm run test:a11y # Accessibility tests (axe-core)
npm run test:visual # Visual regression (Playwright + Storybook)
# Pre-commit validation (run before PR)
npm run typecheck && npm run lint && npm test && npm run build
# GitHub MCP Server
npm run github:mcp:test # Test connectivity and authentication
# Figma MCP Server
npm run figma:mcp:test # Test connectivity and authentication
# Context7 MCP Server
npm run context7:mcp # Launch locally (respects CONTEXT7_API_KEY)
npm run context7:mcp -- --remote-check # Test remote endpoint
# TypeScript LSP Status
npm run ts:mcp:status # Validate TypeScript language server
npm run ts:mcp:status:stub # Generate stub status
# Linear Issue Management
npx tsx scripts/linear/create-issue.ts # Interactive issue creation
npx tsx scripts/linear/update-issue.ts --issue DIG-16 --state "Done"
npx tsx scripts/linear/check-issue.ts DIG-16 # Display issue details
# Sentry Observability
node scripts/sentry-mcp.js issues digitaltableteur 10 --unresolved
npm run generate-sentry-summary # Generate dashboard data
# Next.js production build
npm run build # Output: .next/
# Storybook static build
npm run build-storybook # Output: storybook-static/
# Vite to GitHub Pages
npm run deploy # Build + gh-pages deployment
# Vite + Storybook visual diffs
npm run deploy-with-storybook # Deploy with visual regression report
# Manual cache busting
npm run cache-bust # Add version metadata + .nojekyll
# Vercel (production - automatic on push to main)
vercel --prod
Hybrid Deployment Strategy:
https://digitaltableteur.com)https://nextjs-app.vercel.app)/api/* routes)vercel.json)Vite Build:
Next.js Build:
The project uses a hierarchical CLAUDE.md/AGENTS.md system optimized for AI assistants:
Root Documentation (Universal Rules)
βββ CLAUDE.md (380 lines) # Comprehensive authority for Claude Code
βββ AGENTS.md (150 lines) # Quick reference for generic agents
βββ .github/copilot-instructions.md # GitHub Copilot specific
Subdirectory Documentation (Specific Context)
βββ app/CLAUDE.md + AGENTS.md # Next.js App Router patterns
βββ shared/components/CLAUDE.md + AGENTS.md # Component library rules
βββ api-legacy-vercel-functions/AGENTS.md # Serverless patterns
βββ docs/AGENTS.md # Documentation navigation
βββ scripts/AGENTS.md # Automation patterns
Claude Code Configuration
βββ .claude/settings.json # Hooks (auto-format, safety checks)
βββ .claude/commands/ # Custom slash commands
βββ review.md # Comprehensive code review
βββ fix-issue.md # GitHub issue workflow
βββ create-component.md # Component generation
βββ create-linear-issue.md # Issue creation
CLAUDE.md (Claude Code Authority)
AGENTS.md (Generic AI Quick Reference)
Claude Code Enhancements
/review, /fix-issue, /create-component, /create-linear-issueClaude Code (automatic):
/review # Comprehensive code review
/fix-issue 123 # Analyze and fix GitHub issue
/create-component Button # Generate component (5 files)
/create-linear-issue Implement X # Create Linear issue
Generic AI Agents (manual reference):
cat AGENTS.md # Root rules
cat app/AGENTS.md # Next.js patterns
cat shared/components/AGENTS.md # Component rules
Documentation:
If you need the raw design data, you can download the Figma file as JSON. Set the
FIGMA_TOKEN environment variable with your personal access token, then run:
npm run fetch-figma
Synchronizes design tokens and assets from Figma using the API.
The file is saved as figma.json in the project root.
npm run generate:sitemap # Generate XML sitemap
npm run generate:llms # Create LLM-friendly content index
npm run generate:alt-text # Generate accessibility descriptions (requires OPENAI_API_KEY)
generate:alt-text streams local image bytes to the OpenAI Vision API so it can describe the actual artwork; add OPENAI_API_KEY (and optionally OPENAI_ALT_MODEL) to .env.local before running, or append --force to regenerate every <img> alt attribute.
Quick Publish Workflow:
# Publish single article from Sanity
npm run sanity:publish-single <article-slug>
# Publish all articles
npm run sanity:publish
docs/SANITY_PUBLISHING_AUTOMATION.md for the complete automated publishing workflowdocs/SANITY_MIGRATION.md for the full migration workflow (React β Sanity via sanity:parse-posts / sanity:convert / sanity:upload, Sanity β MDX via sanity:sync-from-remote, redirects generation, cleanup helpers)digitaltableteur/
βββ app/ # Next.js 16 App Router (production)
β βββ layout.tsx # Root layout with providers
β βββ page.tsx # Home page (server component)
β βββ about/page.tsx # Route pages
β βββ blog/[slug]/page.tsx # Dynamic routes
β βββ api/*/route.ts # API routes (Vercel functions)
β
βββ src/ # Vite app (legacy, being phased out)
β βββ App.tsx # React Router configuration
β βββ pages/ # Route components (to be migrated)
β βββ components/ # Component library
β
βββ shared/ # Symlinked shared code
β βββ components/ # Design system (from src/components)
β βββ hooks/ # Custom React hooks
β βββ styles/ # Design tokens & global styles
β βββ locales/ # i18n translation files
β
βββ api-legacy-vercel-functions/ # Serverless functions
β βββ cors.js # CORS middleware
β βββ openai-chat.js # AI chat endpoint
β βββ save-contact.js # Contact form handler
β βββ download-cv.js # Secure CV download
β
βββ scripts/ # Automation & tooling
β βββ linear/ # Issue management
β βββ sentry-mcp.js # Observability queries
β βββ generate-*.js # Code generation
β
βββ docs/ # Documentation
β βββ LLM_COMPONENT_GENERATION_RULES.md (12,000+ words)
β βββ NEXTJS_MIGRATION_PLAN.md
β βββ LINEAR_AUTOMATION.md
β βββ *_MCP_SETUP.md # MCP integration guides
β
βββ .claude/ # Claude Code configuration
βββ settings.json # Hooks (auto-format, safety)
βββ commands/ # Custom slash commands
Frontend
Styling & Design
src/styles/variables.css@supports queries for modern featuresmargin-inline, padding-block (RTL-ready)State & Data
Backend Services
Developer Tools
AI & Automation
The SocialShare component implements progressive enhancement with the Web Share API:
Native Share Support
Responsive Design
Progressive Enhancement
navigator.share availabilityAccessibility
Browser Support
The implementation follows Web Share API best practices with proper error handling and provides a consistent user experience across all device types.
npm test # Run all tests
npm run test:watch # Watch mode
npm run test:coverage # Generate coverage reports
npm run test:a11y # Accessibility testing
npm run test:visual # Visual regression tests
npm run test:visual:update # Update visual baselines
Test Suite Coverage:
Testing Libraries:
npm run lint # ESLint + Stylelint
npm run lint:fix # Auto-fix linting issues
npm run format # Prettier formatting
npm run typecheck # TypeScript validation
Quality Standards:
Sentry Integration:
npm run sentry:issues # Query unresolved issues
npm run sentry:releases # List recent releases
npm run generate:sentry-summary # Generate JSON summary
MCP Testing:
npm run github:mcp:test # Test GitHub MCP connectivity
npm run figma:mcp:test # Test Figma MCP connectivity
npm run ts:mcp:status # Validate TypeScript LSP
Storybook:
npm run storybook # Launch Storybook dev server
npm run build-storybook # Build static Storybook
npm run storybook:deploy # Deploy to GitHub Pages
WIP Badge System:
parameters: { wip: { disabled: true } } after passing:
The site supports three languages with complete translation coverage:
Structure:
nextjs-app/shared/locales/{en,fi,sv}/translation.jsonUsage:
useTranslation() hook"navigation.home" for organizationSecurity Measures:
.env.local (gitignored).gitignore)Performance Optimizations:
Next.js App:
npm run build # Build Next.js app (production)
npm run start # Start Next.js production server
Vite App (legacy):
npm run build # Vite production build
npm run preview # Preview Vite production build
npm run cache-bust # Manual cache busting
Hybrid Deployment:
Vercel: Hosts Next.js app + serverless API functions
main branchGitHub Pages: Hosts legacy Vite app + Storybook
npm run deploydigitaltableteur.comDeployment Commands:
npm run deploy # Deploy Vite app to GitHub Pages
npm run deploy-with-storybook # Deploy with visual diff report
npm run storybook:deploy # Deploy Storybook standalone
npm run generate:sitemap # Generate sitemap.xml
Vite Build:
.nojekyll file prevents GitHub Pages Jekyll processingNext.js Build:
Cache-Control headers configured via next.config.tsProduction (Vercel):
Production (GitHub Pages):
GitHub Actions:
Branch Protection:
main branchdocs/LLM_COMPONENT_GENERATION_RULES.md first.stories.tsx for every componentdocs/BRANCH_NAMING.md
git checkout -b DT-XXX-feat-description
npm run typecheck && npm run lint && npm test && npm run build
git commit -m "feat: add amazing feature"
git push origin DT-XXX-feat-description
Before creating a PR:
npm test)npm run typecheck)npm run lint)npm run build)npm run test:visual:update)npm run build)AI Documentation System:
Critical References:
Setup & Guides:
Frontend:
Styling & Design:
Internationalization:
Testing & Quality:
Backend & Deployment:
Automation & AI:
The Chat interface includes a guided, multi-step email composition workflow triggered by natural phrasing. Two trigger paths exist:
chatEmailSendPhrase and invites composition.chatEmailSimplePhrase, reveals mail@digitaltableteur.com, then asks if you want to start composing.Both converge to the same reducer-driven flow; only initial phrasing differs. The simple path uses an anchored regex so incidental mentions ("I like email workflows") are ignored.
messageProcessor.ts sets one of two pending flags (pendingEmailWorkflowGeneral or pendingEmailWorkflowSimple) based on multilingual regex matches. ChatWidget consumes exactly one flag on the next assistant turn, injects the phrase key (chatEmailSendPhrase or chatEmailSimplePhrase), mounts workflow UI inline, then resets the flag.
Reducer file: src/components/ChatWidget/emailWorkflow/reducer.ts
Types: src/components/ChatWidget/emailWorkflow/types.ts
States (simplified):
idle β Workflow not activecompose β Initial free-form intent capture (subject / purpose)fields β Sequential structured field collection (name, email, phone (optional), message body)review β User reviews aggregated draft, can edit any fieldsending β Async submission in progress (aria-busy applied)success β Confirmation + summary displayederror β Error state with retry and edit optionsTransitions are deterministic and validated; editing returns to fields with preserved data. Cancellation cleanly resets to idle.
ComposePrompt β Captures initial intent/subjectFieldPrompt β Renders current required field input with validation hintsReviewSummary β Summarizes all collected fields before sendSendStatus β Displays sending, success, or error feedbackAll components are in src/components/ChatWidget/emailWorkflow/ and follow the standard pattern with .stories.tsx and .test.tsx coverage. Styling leverages existing design tokens and CSS Modules; accessible labels and descriptions use i18n keys.
contactValidation.ts β Shared field validators (name, email format, message length, optional phone)contactEmailService.ts β EmailJS send wrapper that throws typed errors (EmailServiceError) enabling granular retry messagingAdd the following to your development .env (prefixed for Vite):
VITE_EMAILJS_SERVICE_ID=<your_service_id>
VITE_EMAILJS_TEMPLATE_ID=<your_template_id>
VITE_EMAILJS_PUBLIC_KEY=<***REMOVED***>
These are used by the workflow and by the traditional contact form. Missing variables gracefully prevent send actions (error state surfaced to user).
All user-visible workflow text uses the emailWorkflow.* key prefix (e.g. emailWorkflow.compose.heading, emailWorkflow.fields.name.label, emailWorkflow.status.success.title). Ensure additions update all three locale files (en, fi, sv) before mergingβtranslation coverage tests will fail otherwise.
htmlForrole="status" + aria-busy="true" with localized progress textemailWorkflow.integration.test.tsx) and simple keyword path (emailWorkflow.simpleTrigger.test.tsx)emailWorkflow.* keys present across localesTo extend with additional fields or optional attachments:
EmailDraft type and validatorsemailWorkflow.fields.<fieldName>.*ReviewSummary rendering & testsPrefer additive changes over altering existing field semantics to avoid breaking previously localized content.
Any architectural or trigger behavior changes MUST update this README, .github/copilot-instructions.md, CLAUDE.md, and docs/donny-chat.md together.
Automated scripts produce lightweight JSON artifacts consumed by UI components (e.g., summary cards) and Storybook dashboards without live API calls at render time:
public/observability/sentry-summary.json (generated via scripts/generate-sentry-summary.mjs or scripts/sentry-mcp.js commands)public/observability/ts-mcp-status.json (generated via scripts/ts-mcp-automation.mjs)npm run ts:mcp:status # Perform LSP handshake and write status JSON
npm run ts:mcp:status:stub # Force stub status JSON (no handshake)
The Sentry summary script runs with project + filter options (unresolved production issues). A stub mode is available when credentials are absent; UI distinguishes stub data via a badge.
SentrySummaryCard reads and renders Sentry JSON with localized loading/error/empty statesobservability.sentry.*, future observability.ts.*) synchronized across localesWhenever observability schemas evolve (new fields, renamed properties) update README, .github/copilot-instructions.md, and CLAUDE.md concurrently. Tests must be added or adjusted to cover new states (e.g., stub detection, additional metadata rendering).
Donnyβs serverless chat handler automatically loads every MCP server declared in mcp.json. In addition to the local TypeScript language server and Sentry helper, the repository now includes the hosted Context7 MCP server so assistant prompts can pull the latest framework/library docs without leaving the conversation.
mcp.json β "context7" entry points at https://mcp.context7.com/mcp and sends the Context7-API-Key header (value resolved from CONTEXT7_API_KEY). Leave the env unset for anonymous/low-rate usage.npm run context7:mcp -- [optional flags]
--remote-check to ping the hosted Context7 MCP endpoint. The helper automatically injects the Context7-API-Key header using CONTEXT7_API_KEY.CONTEXT7_API_KEY in .env.local or your shell profile (the secret lives in Vercelβs project envs) to benefit from higher rate limits and private library access. The script automatically injects the key unless you pass --api-key manually.https://context7.com/api/v1; use that base URL whenever you need to inspect account status or manage keys outside the dashboard.api/donny-tools.ts names each tool as <server>.<toolName>, so Context7 capabilities appear under the context7.* namespace when connected. This keeps downstream prompts explicit and makes it easy to disable the server by removing the config block if needed.
The official GitHub MCP Server provides AI tools with direct access to GitHub's platform capabilities.
mcp.json β "github" entry points at https://api.githubcopilot.com/mcp/ and sends the Authorization: Bearer header (value resolved from GITHUB_MCP_PAT)GITHUB_MCP_PAT environment variablenpm run github:mcp:testCapabilities include:
GitHub capabilities appear under the github.* namespace when connected, maintaining the same explicit tool naming pattern.
The figma-developer-mcp provides AI tools with direct access to Figma's design platform for design-to-code workflows.
mcp.json β "figma-developer-mcp" entry runs as SSE server at http://localhost:3333/sseFIGMA_TOKEN environment variablenpx figma-developer-mcp before using MCP featuresnpm run figma:mcp:testCapabilities include:
Figma capabilities appear under the figma.* namespace when connected, enabling AI assistants to interact with your design files and generate implementation code directly from Figma designs.
This listing does not have a supported local package template. Use the maintainerβs documentation for its hosted endpoint, authentication, and client-specific setup. No install command has been inferred.