···11+# =============================================================================
22+# Development Stage
33+# =============================================================================
44+FROM node:22-alpine AS development
55+66+# Install curl for health checks
77+RUN apk add --no-cache curl
88+99+WORKDIR /app
1010+1111+# Copy monorepo root files needed for workspace setup
1212+# Note: package-lock.json is ignored in .dockerignore for faster builds
1313+COPY package.json lerna.json ./
1414+1515+# Copy package.json files for dependency resolution (monorepo workspace)
1616+# Copy these in order of least likely to change to most likely to change
1717+COPY packages/tsconfig/package.json ./packages/tsconfig/
1818+COPY packages/biome-config/package.json ./packages/biome-config/
1919+COPY packages/utils/package.json ./packages/utils/
2020+COPY packages/ui/package.json ./packages/ui/
2121+COPY apps/docs/package.json ./apps/docs/
2222+2323+# Install dependencies first - this layer will cache well when dependencies don't change
2424+# Using npm install instead of npm ci since package-lock.json is ignored for faster builds
2525+RUN npm install --ignore-scripts
2626+2727+# Copy package source code (needed for workspace packages)
2828+COPY packages/tsconfig ./packages/tsconfig
2929+COPY packages/ui ./packages/ui
3030+COPY packages/utils ./packages/utils
3131+3232+# Copy docs config files (changes less frequently than source)
3333+COPY apps/docs/tsconfig*.json ./apps/docs/
3434+COPY apps/docs/vite.config.ts ./apps/docs/
3535+COPY apps/docs/postcss.config.cjs ./apps/docs/
3636+COPY apps/docs/index.html ./apps/docs/
3737+3838+# Source code is copied via volumes in docker-compose, but we copy content
3939+# since it's needed for MDX processing
4040+COPY apps/docs/content ./apps/docs/content
4141+4242+WORKDIR /app/apps/docs
4343+4444+EXPOSE 3001
4545+4646+CMD ["npm", "run", "dev"]
4747+4848+# =============================================================================
4949+# Build Stage
5050+# =============================================================================
5151+FROM node:22-alpine AS builder
5252+5353+WORKDIR /app
5454+5555+# Copy monorepo root files
5656+# Note: package-lock.json is ignored in .dockerignore for faster builds
5757+COPY package.json lerna.json ./
5858+5959+# Copy package.json files for dependency resolution
6060+COPY packages/tsconfig/package.json ./packages/tsconfig/
6161+COPY packages/biome-config/package.json ./packages/biome-config/
6262+COPY packages/utils/package.json ./packages/utils/
6363+COPY packages/ui/package.json ./packages/ui/
6464+COPY apps/docs/package.json ./apps/docs/
6565+6666+# Install dependencies (cached layer)
6767+# Using npm install instead of npm ci since package-lock.json is ignored for faster builds
6868+RUN npm install --ignore-scripts
6969+7070+# Copy package source code
7171+COPY packages/tsconfig ./packages/tsconfig
7272+COPY packages/ui ./packages/ui
7373+COPY packages/utils ./packages/utils
7474+7575+# Copy docs config files
7676+COPY apps/docs/tsconfig*.json ./apps/docs/
7777+COPY apps/docs/vite.config.ts ./apps/docs/
7878+COPY apps/docs/postcss.config.cjs ./apps/docs/
7979+COPY apps/docs/index.html ./apps/docs/
8080+8181+# Copy docs source code and content
8282+COPY apps/docs/src ./apps/docs/src
8383+COPY apps/docs/content ./apps/docs/content
8484+8585+# Build the docs app
8686+WORKDIR /app/apps/docs
8787+RUN npm run build
8888+8989+# =============================================================================
9090+# Production Stage
9191+# =============================================================================
9292+FROM nginx:alpine AS production
9393+9494+WORKDIR /usr/share/nginx/html
9595+9696+# Copy built assets from builder
9797+COPY --from=builder /app/apps/docs/dist .
9898+9999+# Copy nginx configuration
100100+COPY apps/docs/nginx.conf /etc/nginx/conf.d/default.conf
101101+102102+EXPOSE 80
103103+104104+CMD ["nginx", "-g", "daemon off;"]
105105+
+152
apps/docs/README.md
···11+# Documentation Site
22+33+This is the documentation site for the CV Generator project, built with React, Vite, and React Router.
44+55+## Architecture
66+77+The docs app follows a clean separation of concerns:
88+99+```
1010+apps/docs/src/
1111+├── components/ # UI components (presentation layer)
1212+│ ├── DocPage.tsx # Main documentation page component
1313+│ ├── Sidebar.tsx # Navigation sidebar
1414+│ ├── Layout.tsx # Page layout wrapper
1515+│ └── MarkdownRenderer.tsx # Markdown rendering logic
1616+│
1717+├── config/ # Configuration (content & structure)
1818+│ ├── docs.config.ts # Documentation content mapping
1919+│ ├── navigation.config.ts # Sidebar navigation structure
2020+│ ├── env.config.ts # Environment variable configuration
2121+│ └── index.ts # Barrel export
2222+│
2323+└── router/ # Application routing
2424+ └── AppRouter.tsx
2525+```
2626+2727+### Design Principles
2828+2929+1. **Separation of Concerns**: Components render, configs define content/structure
3030+2. **Single Responsibility**: Each config file has one clear purpose
3131+3. **Easy Maintenance**: Add new docs by updating config files, not components
3232+3333+## Features
3434+3535+- **Markdown Rendering**: Documentation is written in Markdown and rendered with syntax highlighting
3636+- **Environment Variable Interpolation**: Dynamic values are injected at runtime
3737+- **Catppuccin Theme**: Consistent design system with the main application
3838+- **Hot Module Replacement**: Changes to documentation update instantly
3939+- **Configuration-Driven**: Content and structure defined separately from UI components
4040+4141+## Environment Variable Interpolation
4242+4343+The documentation supports interpolating environment variables into markdown files at runtime. This allows for dynamic URLs and configuration values based on the deployment environment.
4444+4545+### Usage
4646+4747+In any markdown file, use double curly braces to reference an environment variable:
4848+4949+```markdown
5050+- **Client App**: [{{CLIENT_URL}}]({{CLIENT_URL}})
5151+- **GraphQL Playground**: [{{GRAPHQL_URL}}]({{GRAPHQL_URL}})
5252+```
5353+5454+### Available Variables
5555+5656+The following variables are available for interpolation:
5757+5858+| Variable | Default Value | Description |
5959+|----------|---------------|-------------|
6060+| `CLIENT_URL` | `http://localhost:5173` | React client application URL |
6161+| `SERVER_URL` | `http://localhost:3000` | NestJS API server URL |
6262+| `DOCS_URL` | `http://localhost:3001` | Documentation site URL |
6363+| `GRAPHQL_URL` | `http://localhost:3000/graphql` | GraphQL Playground URL |
6464+| `DB_HOST` | `localhost` | Database host |
6565+| `DB_PORT` | `5432` | Database port |
6666+6767+### Configuration
6868+6969+Environment variables are configured in two places:
7070+7171+1. **Docker Compose** (`docker-compose.yml`):
7272+ ```yaml
7373+ docs:
7474+ environment:
7575+ VITE_CLIENT_URL: ${VITE_CLIENT_URL:-http://localhost:5173}
7676+ VITE_SERVER_URL: ${VITE_SERVER_URL:-http://localhost:3000}
7777+ # ... other variables
7878+ ```
7979+8080+2. **Code** (`apps/docs/src/config/env.config.ts`):
8181+ ```typescript
8282+ export const ENV_VARS: Record<string, string> = {
8383+ CLIENT_URL: import.meta.env.VITE_CLIENT_URL || "http://localhost:5173",
8484+ SERVER_URL: import.meta.env.VITE_SERVER_URL || "http://localhost:3000",
8585+ // ... other variables
8686+ };
8787+ ```
8888+8989+### Adding New Variables
9090+9191+To add a new interpolation variable:
9292+9393+1. Add it to the `ENV_VARS` object in `apps/docs/src/config/env.config.ts`
9494+2. Add the corresponding `VITE_*` environment variable to `docker-compose.yml`
9595+3. Use it in markdown files with `{{VARIABLE_NAME}}`
9696+9797+### How It Works
9898+9999+The interpolation happens in `env.config.ts` using a simple regex replacement:
100100+101101+```typescript
102102+export const interpolateEnvVars = (content: string): string => {
103103+ return content.replace(/\{\{(\w+)\}\}/g, (match, varName) => {
104104+ return ENV_VARS[varName] ?? match;
105105+ });
106106+};
107107+```
108108+109109+This ensures that:
110110+- Variables are replaced at render time (not build time)
111111+- Missing variables fall back to the placeholder text
112112+- The same markdown files work across different environments
113113+114114+## Adding New Documentation
115115+116116+To add a new documentation page:
117117+118118+1. **Create the markdown file** in the appropriate location
119119+2. **Import it** in `apps/docs/src/config/docs.config.ts`:
120120+ ```typescript
121121+ import myNewDocMd from "../../../../MY_NEW_DOC.md?raw";
122122+ ```
123123+3. **Add to the mapping**:
124124+ ```typescript
125125+ export const DOC_CONTENT: Record<string, string> = {
126126+ // ... existing docs
127127+ "my-new-doc": myNewDocMd,
128128+ };
129129+ ```
130130+4. **Add navigation link** in `apps/docs/src/config/navigation.config.ts`:
131131+ ```typescript
132132+ export const DOC_LINKS: DocLink[] = [
133133+ // ... existing links
134134+ { title: "My New Doc", slug: "my-new-doc", icon: "📄" },
135135+ ];
136136+ ```
137137+138138+That's it! The UI components will automatically pick up the new documentation.
139139+140140+## Development
141141+142142+```bash
143143+# Install dependencies
144144+npm install
145145+146146+# Run development server
147147+npm run dev
148148+149149+# Build for production
150150+npm run build
151151+```
152152+
+23
apps/docs/content/README.md
···11+# Documentation Content
22+33+This directory contains all the markdown documentation files for the CV Generator project.
44+55+## How It Works
66+77+All `.md` and `.mdx` files in this directory are automatically discovered and made available in the documentation site. The file name (without the `.md` or `.mdx` extension) becomes the URL slug.
88+99+## Adding New Documentation
1010+1111+1. Create a new `.md` or `.mdx` file in the content directory
1212+2. The file will automatically be picked up by the docs site
1313+3. Add a navigation entry in `apps/docs/src/config/navigation.config.ts` to make it appear in the sidebar
1414+1515+## File Naming
1616+1717+- Use kebab-case for file names (e.g., `docker-strategy.md`)
1818+- The file name becomes the URL slug (e.g., `/docs/docker-strategy`)
1919+- Avoid special characters and spaces in file names
2020+2121+## Auto-Discovery
2222+2323+The docs site uses Vite's `import.meta.glob` to automatically discover all markdown files in this directory. No manual imports or configuration needed!
+109
apps/docs/content/api/README.md
···11+# API Documentation
22+33+This section contains comprehensive documentation for the CV Generator API.
44+55+## Overview
66+77+The CV Generator API is built with NestJS and GraphQL, providing a type-safe and efficient way to interact with the application's data.
88+99+## API Structure
1010+1111+### GraphQL Endpoint
1212+1313+- **URL**: `/graphql`
1414+- **Method**: POST
1515+- **Content-Type**: `application/json`
1616+1717+### Authentication
1818+1919+The API uses JWT-based authentication. Include the token in the `Authorization` header:
2020+2121+```
2222+Authorization: Bearer <your-jwt-token>
2323+```
2424+2525+## Core Modules
2626+2727+### Authentication
2828+2929+- **Login** - User authentication
3030+- **Register** - User registration
3131+- **Me** - Current user information
3232+3333+### Job Experience
3434+3535+- **Companies** - Company management
3636+- **Roles** - Job role management
3737+- **Levels** - Experience level management
3838+- **Skills** - Skill management
3939+- **User Job Experience** - User's employment history
4040+4141+### Organizations
4242+4343+- **Organization Management** - Organization CRUD operations
4444+- **User-Organization Relationships** - Membership management
4545+4646+### Vacancies
4747+4848+- **Vacancy Management** - Job posting management
4949+5050+## Pagination
5151+5252+The API supports cursor-based pagination for efficient data loading:
5353+5454+```graphql
5555+query Skills($first: Int, $after: String, $searchTerm: String) {
5656+ skills(first: $first, after: $after, searchTerm: $searchTerm) {
5757+ edges {
5858+ cursor
5959+ node {
6060+ id
6161+ name
6262+ description
6363+ }
6464+ }
6565+ pageInfo {
6666+ hasNextPage
6767+ hasPreviousPage
6868+ startCursor
6969+ endCursor
7070+ }
7171+ totalCount
7272+ }
7373+}
7474+```
7575+7676+## Error Handling
7777+7878+The API returns structured error responses:
7979+8080+```json
8181+{
8282+ "errors": [
8383+ {
8484+ "message": "Error description",
8585+ "extensions": {
8686+ "code": "ERROR_CODE",
8787+ "exception": {
8888+ "stacktrace": ["..."]
8989+ }
9090+ }
9191+ }
9292+ ]
9393+}
9494+```
9595+9696+## Health Check
9797+9898+Monitor API health with the health endpoint:
9999+100100+```graphql
101101+query Health {
102102+ health {
103103+ status
104104+ timestamp
105105+ timezone
106106+ uptime
107107+ }
108108+}
109109+```
+50
apps/docs/content/components/README.md
···11+# Components
22+33+This section contains documentation for the UI components used in the CV Generator application.
44+55+## Overview
66+77+The component library is built with React and TypeScript, using Tailwind CSS for styling and Class Variance Authority (CVA) for dynamic styling configurations.
88+99+## Components
1010+1111+All components are listed alphabetically:
1212+1313+- [**Badge**](/components/badge) - Status and category indicators
1414+- [**Button**](/components/button) - Primary, secondary, and ghost button variants
1515+- [**Calendar**](/components/calendar) - Date picker component
1616+- [**Card**](/components/card) - Card container component
1717+- [**Checkbox**](/components/checkbox) - Toggle input component
1818+- [**FormattedDate**](/components/formatteddate) - Formatted date display component
1919+- [**FormattedDateRange**](/components/formatteddaterange) - Formatted date range display component
2020+- [**IconButton**](/components/iconbutton) - Icon button component
2121+- [**PageHeader**](/components/pageheader) - Page header with title and actions
2222+- [**Placeholder**](/components/placeholder) - Empty state and loading placeholders
2323+- [**RangeSlider**](/components/rangeslider) - Range slider input component
2424+- [**SearchableSelect**](/components/searchableselect) - Searchable dropdown with infinite scroll support
2525+- [**Select**](/components/select) - Dropdown select component
2626+- [**StatusBadge**](/components/statusbadge) - Status badge component
2727+- [**Table**](/components/table) - Data table with sorting and pagination
2828+- [**Textarea**](/components/textarea) - Multi-line text input
2929+- [**TextInput**](/components/textinput) - Text input with validation support
3030+3131+## Design System
3232+3333+All components follow a consistent design system based on the Catppuccin color palette, ensuring a cohesive user experience across the application.
3434+3535+## Usage
3636+3737+Components are exported from the `@cv/ui` package and can be imported as follows:
3838+3939+```typescript
4040+import { Button, TextInput, Table } from "@cv/ui";
4141+```
4242+4343+## Styling
4444+4545+Components use Tailwind CSS utility classes with CVA for dynamic styling. The design system includes:
4646+4747+- Consistent spacing and typography
4848+- Catppuccin color palette integration
4949+- Responsive design patterns
5050+- Accessibility features
···11+# GraphQL Query Analysis: Root vs User Resource
22+33+## Current Root-Level Queries
44+55+### User-Specific Queries (Should Move to User Resource)
66+77+These queries are user-specific and should be accessible via `me { ... }` or `user(id) { ... }`:
88+99+1. **`myEducationHistory`** → Should be `me.educationHistory`
1010+1111+ - Location: `EducationResolver`
1212+ - Current: `Query.myEducationHistory`
1313+ - Should be: `User.educationHistory` (field resolver)
1414+1515+2. **`myApplications`** → Should be `me.applications`
1616+1717+ - Location: `ApplicationResolver`
1818+ - Current: `Query.myApplications`
1919+ - Should be: `User.applications` (field resolver)
2020+2121+3. **`myVacancies`** → Should be `me.vacancies`
2222+2323+ - Location: `VacancyResolver`
2424+ - Current: `Query.myVacancies`
2525+ - Should be: `User.vacancies` (field resolver)
2626+2727+4. **`myCVs`** → Should be `me.cvs`
2828+2929+ - Location: `CVTemplateResolver`
3030+ - Current: `Query.myCVs`
3131+ - Should be: `User.cvs` (field resolver)
3232+3333+5. **`myEmploymentHistory`** → Already has field resolver
3434+ - Location: `EmploymentResolver` (root query) + `UserFieldResolver` (field resolver)
3535+ - Current: Both `Query.myEmploymentHistory` AND `User.experience`
3636+ - **Recommendation**: Remove root query, keep `User.experience` field
3737+3838+### Non-User-Specific Queries (Should Stay at Root)
3939+4040+These are reference data or ID-based lookups, appropriate at root level:
4141+4242+1. **`institutions`** - Reference data query (all institutions)
4343+2. **`organization(id)`** - Fetch organization by ID
4444+3. **`cvTemplates`** - Reference data query (all templates)
4545+4. **`cvTemplate(id)`** - Fetch template by ID
4646+5. **`application(id)`** - Fetch application by ID (with ownership check)
4747+6. **`cv(id)`** - Fetch CV by ID (with ownership check)
4848+7. **`me`** - Convenience query for current user (can stay at root)
4949+5050+## Recommended Changes
5151+5252+### Pattern to Follow
5353+5454+- **User-specific data**: Access via `me { ... }` or `user(id) { ... }`
5555+- **Reference data**: Keep at root level
5656+- **ID-based lookups**: Keep at root level
5757+5858+### Migration Path
5959+6060+1. Add field resolvers to `User` type for:
6161+ - `educationHistory: [Education]`
6262+ - `applications: ApplicationConnection`
6363+ - `vacancies: [Vacancy]`
6464+ - `cvs: CVConnection`
6565+2. Keep root queries for backward compatibility during migration
6666+3. Update frontend to use `me { educationHistory }` instead of `myEducationHistory`
6767+4. Remove deprecated root queries after migration
6868+6969+### Benefits
7070+7171+- Better organization: user data under user resource
7272+- More REST-like structure
7373+- Easier to query multiple user resources in one query
7474+- Consistent with existing `User.experience` and `User.organizations` fields