# Template Frontend

This folder contains the frontend application for the PostXL template project. It is a full-stack React application built with Vite + React + TypeScript and uses TanStack Router for file-based routing, TanStack Query for data fetching, tRPC for type-safe APIs, Tailwind CSS for styling, and shadcn/ui-based components (Radix primitives).

> **Note**: This is a code-generated project from PostXL. Most files are automatically generated from `postxl-schema.json` using the PostXL generator framework. See the main project [CLAUDE.md](/Users/henk/Work/Projects/PostXL/PXL/CLAUDE.md) for details on the generation workflow.


## Table of Contents
- [Overview](#overview)
- [Prerequisites](#prerequisites)
- [Getting Started](#getting-started)
  - [Initial Setup](#1-initial-setup)
  - [Environment Variables](#2-environment-variables)
  - [Run Development Server](#3-run-development-server)
  - [Build & Preview Production](#4-build--preview-production)
- [Available Scripts](#available-scripts)
- [Architecture & Libraries](#architecture--libraries)
  - [Routing (TanStack Router)](#routing-tanstack-router-v1)
  - [Data Fetching (tRPC + TanStack Query)](#data-fetching-trpc--tanstack-query)
  - [Authentication (Keycloak)](#authentication--keycloak)
  - [UI Components (shadcn/ui + Radix)](#ui-components-shadcnui--radix)
  - [Styling (Tailwind CSS)](#styling-tailwind-css)
  - [Testing (Vitest + Storybook)](#testing-vitest--storybook)
- [Route Guards](#route-guards)
- [Development Conventions](#development-conventions)
- [Troubleshooting](#troubleshooting)


## Overview

**Key Technologies:**
- **React 19** with React 19 DOM for modern React features
- **Vite 7** for fast dev builds, HMR, and optimized production builds
- **TypeScript 5.8** with strict type checking (`pnpm run test:types`)
- **TanStack Router v1** with file-based routing and automatic code-splitting
- **TanStack Query** for server state management and caching
- **tRPC 11** integrated with TanStack Query for end-to-end type-safe API calls
- **Tailwind CSS v4** with utility-first styling and the new Vite plugin
- **shadcn/ui** components built on Radix UI primitives, class-variance-authority, and tailwind-merge
- **Keycloak** for enterprise-grade authentication and authorization
- **Vitest** for unit and component testing with browser mode
- **Storybook 9** for component development and documentation


## Prerequisites

**Required:**
- **Node.js 22+** (see `.nvmrc` - use `nvm use` to switch to the correct version)
- **pnpm 10.8.0+** (package manager - this project does not support npm or yarn)

**Optional:**
- Backend API running (usually at `http://localhost:3001`)
- Keycloak instance for authentication (usually at `http://localhost:8080`)
- Docker and Docker Compose for running backend services


## Getting Started

### 1. Initial Setup

**IMPORTANT:** Always use the correct Node.js version before running any commands:

```bash
# Switch to the correct Node.js version (22+)
nvm use
```

If you see warnings like `WARN Unsupported engine: wanted: {"node":">=22"}`, you MUST run `nvm use` immediately.

**Installation:**

```bash
# From the monorepo root, install all dependencies
pnpm install

# Navigate to the frontend directory (if not already there)
cd projects/template/frontend
```

### 2. Environment Variables

The app is configured via Vite environment variables (see `src/lib/config.ts`). Create a `.env.local` file in the frontend directory with the following variables:

**Core Configuration:**
- `VITE_PUBLIC_API_URL` - Base API URL (default: `http://localhost:3001`)
- `VITE_PUBLIC_BASE_URL` - Public base URL of the frontend (default: `http://localhost:3000`)

**Authentication (Keycloak):**
- `VITE_PUBLIC_KEYCLOAK_URL` - Keycloak server URL (e.g., `http://localhost:8080`)
- `VITE_PUBLIC_KEYCLOAK_REALM` - Keycloak realm name
- `VITE_PUBLIC_KEYCLOAK_CLIENT_ID` - Keycloak client ID
- `VITE_PUBLIC_CHECK_LOGIN_IFRAME` - Enable/disable iframe-based session checks (boolean)
- `VITE_AUTH` - Enable/disable authentication entirely (boolean, useful for local development)

**Development:**
- `VITE_ENABLE_DEV_TOOLS` - Enable TanStack Router and Query DevTools (boolean, auto-enabled with `pnpm dev`)

**Testing & E2E:**
- `VITE_PUBLIC_MAPBOX_DISABLE_TRANSITIONS` - Disable map transitions for E2E tests (boolean)
- `VITE_PUBLIC_USE_TEST_ASSETS` - Use test assets instead of production assets (boolean)

**Analytics (Optional):**
- `VITE_PUBLIC_ENABLE_ANALYTICS` - Enable analytics tracking (boolean)
- `VITE_PUBLIC_ANALYTICS_SCRIPT_URL` - Analytics script URL (string)

**Example `.env.local`** (do not commit secrets):

```bash
# API Configuration
VITE_PUBLIC_API_URL=http://localhost:3001
VITE_PUBLIC_BASE_URL=http://localhost:3000

# Keycloak Authentication
VITE_PUBLIC_KEYCLOAK_URL=http://localhost:8080
VITE_PUBLIC_KEYCLOAK_REALM=template
VITE_PUBLIC_KEYCLOAK_CLIENT_ID=template-frontend
VITE_PUBLIC_CHECK_LOGIN_IFRAME=false

# Development
VITE_AUTH=true
VITE_ENABLE_DEV_TOOLS=true

# Analytics (optional)
VITE_PUBLIC_ENABLE_ANALYTICS=false
```

> **Note:** Boolean environment variables are parsed in `src/lib/config.ts` via JSON parsing and support special Docker placeholders. See the config file for implementation details.

### 3. Run Development Server

```bash
# Start dev server with DevTools enabled
pnpm dev

# Start dev server without authentication (useful for local development)
pnpm dev:noAuth
```

The application will be available at `http://localhost:3000` (or the port specified in `VITE_PUBLIC_BASE_URL`).

### 4. Build & Preview Production

```bash
# Type-check and build for production
pnpm build

# Preview the production build locally
pnpm preview
```


## Available Scripts

**Development:**
- `pnpm dev` - Start development server with DevTools enabled
- `pnpm dev:noAuth` - Start development server with authentication disabled

**Building:**
- `pnpm build` - Type-check and build for production
- `pnpm build:analyze` - Build with bundle analysis (skips type-check)
- `pnpm debug` - Build with Vite debug logging enabled
- `pnpm preview` - Preview production build locally

**Testing:**
- `pnpm test:types` - Run TypeScript type checking
- `pnpm test:storybook` - Run Storybook component tests with Vitest

**Linting:**
- `pnpm lint` - Run ESLint on the codebase

**Storybook:**
- `pnpm storybook:start` - Start Storybook dev server on port 6006
- `pnpm storybook:build` - Build Storybook for production

**E2E Support:**
- `pnpm e2e:build` - Build for E2E tests with test-friendly configuration
- `pnpm e2e:ci` - Build for CI E2E tests


## Architecture & Libraries

### Routing (TanStack Router v1)

This project uses **file-based routing** powered by `@tanstack/router-plugin`. Route modules live in `src/routes/` and the plugin automatically generates `routeTree.gen.ts` used by the router configuration.

**Key Concepts:**
- **File-based routing**: Routes are defined by the file structure in `src/routes/`
- **Lazy loading**: Use `.lazy.tsx` files for route components that should be code-split and loaded on-demand
- **Type-safe navigation**: Full TypeScript support for route paths, params, and search params
- **Nested layouts**: Support for nested route layouts with `_layout` files

**Important Files:**
- `src/lib/router.tsx` - Router configuration, context setup (auth), error/not-found components
- `src/routes/__root.tsx` - Root route with typed context and `<RootDocument>` shell component
- `src/routes/routeTree.gen.ts` - Auto-generated route tree (do not edit manually)

**Resources:**
- [File-based routing docs](https://tanstack.com/router/v1/docs/framework/react/routing/file-based-routing)
- [File naming conventions](https://tanstack.com/router/v1/docs/framework/react/routing/file-naming-conventions)
- [Code-splitting guide](https://tanstack.com/router/v1/docs/framework/react/guide/code-splitting)

### Data Fetching (tRPC + TanStack Query)

This project uses **tRPC** for end-to-end type-safe API calls, integrated with **TanStack Query** for powerful data fetching, caching, and state management.

**Key Features:**
- **Type safety**: Full TypeScript inference from backend to frontend - no manual type definitions needed
- **Automatic serialization**: Uses `superjson` for handling complex types (Date, Map, Set, etc.)
- **Caching & optimistic updates**: Built on TanStack Query for intelligent caching and UI updates
- **Queries and mutations**: Colocated with components for easy data management

**Important Files:**
- `src/lib/trpc.ts` - tRPC client configuration and React hooks setup

**Usage Example:**
```typescript
import { trpc } from '@lib/trpc'

function MyComponent() {
  // Type-safe query
  const { data, isLoading } = trpc.users.list.useQuery()

  // Type-safe mutation
  const createUser = trpc.users.create.useMutation()

  return (
    <button onClick={() => createUser.mutate({ name: 'John' })}>
      Create User
    </button>
  )
}
```

**Resources:**
- [tRPC documentation](https://trpc.io/docs)
- [tRPC + TanStack Query integration](https://trpc.io/docs/client/react/integration/tanstack-query)
- [TanStack Query documentation](https://tanstack.com/query/latest)

### Authentication (Keycloak)

Authentication is handled by **Keycloak** using the `keycloak-js` library, wrapped in a custom React context for easy access throughout the application.

**Key Features:**
- **SSO support**: Single sign-on across multiple applications
- **Role-based access control**: User roles and permissions from Keycloak
- **Token management**: Automatic token refresh and session management
- **Optional auth**: Can be disabled via `VITE_AUTH=false` for local development

**Important Files:**
- `src/contextProviders/authContextProvider.tsx` - Authentication context and hooks
- `src/contextProviders/themeContextProvider.tsx` - Theme management (light/dark mode)
- `src/pages/login/login.page.tsx` - Login page and redirect handling

### UI Components (shadcn/ui + Radix)

This project follows the **shadcn/ui** approach, using **Radix UI** primitives combined with **Tailwind CSS** and **class-variance-authority** for composable, accessible components.

**Key Characteristics:**
- **Copy-paste components**: Components are part of your codebase, not a package dependency
- **Radix primitives**: Built on Radix UI for accessibility and headless UI patterns
- **Tailwind styling**: Utility-first CSS with Tailwind v4
- **Variants**: Uses `class-variance-authority` (CVA) for component variants

**Component Location:**
- `src/components/ui/` - Base UI components (button, dialog, dropdown, etc.)

**Example Components:**
- `src/components/ui/button/Button.tsx` - Button component with variants
- `src/components/ui/dialog/Dialog.tsx` - Modal dialog
- `src/components/ui/checkbox/ShadcnCheckbox.tsx` - Checkbox with Radix
- `src/components/ui/calendar/Calendar.tsx` - Date picker calendar

**Resources:**
- [shadcn/ui documentation](https://ui.shadcn.com/docs)
- [Radix UI primitives](https://www.radix-ui.com/primitives)
- [class-variance-authority](https://cva.style/docs)

### Styling (Tailwind CSS)

This project uses **Tailwind CSS v4** with the new **@tailwindcss/vite** plugin for utility-first styling.

**Key Features:**
- **Utility-first**: Compose designs using utility classes
- **Custom theme**: Extended theme in `tailwind.config.js` or CSS variables
- **Dark mode**: Built-in dark mode support with `class` strategy
- **Responsive design**: Mobile-first responsive utilities

**Utility Functions:**
- `src/lib/utils.ts` - Contains the `cn()` utility function that combines `clsx` and `tailwind-merge` for conditional class names

**Usage Example:**
```typescript
import { cn } from '@lib/utils'

<div className={cn(
  "px-4 py-2 rounded-lg",
  isActive && "bg-blue-500",
  isDisabled && "opacity-50"
)}>
  Content
</div>
```

**Resources:**
- [Tailwind CSS documentation](https://tailwindcss.com/docs)
- [Utility classes reference](https://tailwindcss.com/docs/styling-with-utility-classes)
- [Responsive design guide](https://tailwindcss.com/docs/responsive-design)

### Testing (Vitest + Storybook)

This project uses **Vitest** for unit and component testing, and **Storybook** for component development and documentation.

**Vitest:**
- **Browser mode**: Tests run in a real browser environment using Playwright
- **Component testing**: Test React components with `vitest-browser-react`
- **Coverage**: V8 coverage reports with `@vitest/coverage-v8`
- **Fast**: Powered by Vite for instant feedback

**Storybook:**
- **Component development**: Develop components in isolation
- **Documentation**: Auto-generated docs with `@storybook/addon-docs`
- **Testing**: Integration with Vitest via `@storybook/addon-vitest`
- **Visual testing**: Ready for Chromatic integration

**Configuration Files:**
- `vite.config.ts` - Vitest configuration
- `.storybook/main.ts` - Storybook configuration
- `.storybook/preview.ts` - Storybook preview settings

**Resources:**
- [Vitest documentation](https://vitest.dev/)
- [Storybook documentation](https://storybook.js.org/docs)


## Route Guards

Route guards are implemented via the `beforeLoad` hook in route files. Guards can redirect users, throw errors, or prevent navigation based on authentication state or user permissions.

**Authentication Guards:**

The main authentication guard is implemented in `src/routes/_authRoutes.tsx` and applies to all routes nested under it:
- If the user is not authenticated, they are redirected to `/login` with a `redirectUrl` search parameter to restore their location after login
- After successful login, users are automatically redirected back to their original destination

**Route Structure:**

```
/                          → Redirects to /dashboard (src/routes/index.tsx)
/login                     → Login page (src/pages/login/login.page.tsx)
/unauthorized              → Unauthorized access page
/_authRoutes/*             → Protected routes requiring authentication
  /dashboard               → Main dashboard (default landing page)
  /[other-routes]          → Other authenticated routes
```

**Implementing Custom Guards:**

To add custom guards to a route, use the `beforeLoad` hook:

```typescript
// src/routes/admin.tsx
export const Route = createFileRoute('/admin')({
  beforeLoad: async ({ context }) => {
    if (!context.auth.isAuthenticated) {
      throw redirect({ to: '/login' })
    }

    if (context.auth.user?.role !== 'admin') {
      throw redirect({ to: '/unauthorized' })
    }
  },
})
```


## Development Conventions

### File Organization

- **Routes**: Place route files in `src/routes/` following TanStack Router file naming conventions
- **Components**:
  - UI components: `src/components/ui/`
  - Feature components: `src/components/[feature-name]/`
- **Pages**: Page components in `src/pages/[route-name]/` (lazy-loaded by routes)
- **Lib/Utils**: Shared utilities and configurations in `src/lib/`
- **Types**: Type definitions in `src/types/`

### Import Aliases

This project uses path aliases configured via `vite-tsconfig-paths`:

```typescript
import { Button } from '@components/ui/button/Button'
import { trpc } from '@lib/trpc'
import { useAuth } from '@contextProviders/authContextProvider'
```

Available aliases:
- `@` - Points to `src/`

### Code Splitting

- Use `.lazy.tsx` files for route components that should be lazy-loaded to keep initial bundle size small
- Heavy components or libraries should be dynamically imported where possible
- The build automatically code-splits routes for optimal loading performance

### State Management

- **Server state**: Use tRPC queries and mutations with TanStack Query
- **Client state**: Use React hooks (`useState`, `useReducer`) and context providers
- **Form state**: Use `react-hook-form` with Zod validation
- **URL state**: Use TanStack Router's search params for shareable state

### Styling Conventions

- **Prefer utility classes**: Use Tailwind utilities over custom CSS
- **Use the `cn()` utility**: For conditional and merged class names (from `src/lib/utils.ts`)
- **Component variants**: Use `class-variance-authority` for component variant systems
- **Consistency**: Follow existing component patterns from `src/components/ui/`

### TypeScript

- **Strict mode**: TypeScript strict mode is enabled - address all type errors
- **Type inference**: Let TypeScript infer types where possible, explicit types for public APIs
- **tRPC types**: Never manually define backend types - they're automatically inferred from tRPC
- **Zod schemas**: Use Zod for runtime validation and type generation

### Generated Code

**IMPORTANT:** This project uses PostXL code generation:

- Most application files are auto-generated from `postxl-schema.json`
- To regenerate code after schema changes: `pnpm run generate` (from project root)
- To force regeneration: `pnpm run generate -f`
- Generated files should not be manually edited unless you "eject" them (modify them to take ownership)
- See the main [CLAUDE.md](../../../CLAUDE.md) for generator development workflow


## Troubleshooting

### Common Issues

**Authentication redirect loop:**
- Ensure `VITE_PUBLIC_KEYCLOAK_URL`, `VITE_PUBLIC_KEYCLOAK_REALM`, and `VITE_PUBLIC_KEYCLOAK_CLIENT_ID` are correct
- Check that Keycloak is running and reachable at the specified URL
- For local development without auth, set `VITE_AUTH=false`
- Try clearing browser cookies and localStorage

**Type errors:**
- Run `pnpm test:types` to see all TypeScript errors
- Ensure all dependencies are installed: `pnpm install`
- If using tRPC, ensure the backend is built and types are generated
- Check that `routeTree.gen.ts` is up to date (generated automatically on dev server start)

**Build failures:**
- Ensure you're using Node.js 22+: Run `nvm use`
- Clear build cache: `rm -rf node_modules/.vite`
- Rebuild from monorepo root: `pnpm run build`
- Check for TypeScript errors: `pnpm test:types`

**HMR (Hot Module Replacement) not working:**
- Check that no errors are logged in the browser console
- Try restarting the Vite dev server
- Clear browser cache and reload
- On macOS, you may need to increase file watch limits (see main project README)

**Storybook issues:**
- Ensure Storybook dependencies are installed: `pnpm install`
- Clear Storybook cache: `rm -rf node_modules/.cache/storybook`
- Restart Storybook: `pnpm storybook:start`

**Port already in use:**
- Frontend default port is 3000
- Change port with: `pnpm dev -- --port 3001`
- Or kill the process using the port: `lsof -ti:3000 | xargs kill -9` (macOS/Linux)

**Environment variables not loading:**
- Ensure `.env.local` is in the frontend directory (not the project root)
- Restart the dev server after changing environment variables
- Remember: Only `VITE_*` prefixed variables are exposed to the client
- Check `src/lib/config.ts` for how environment variables are parsed

### Getting Help

- Check the [PostXL CLAUDE.md](../../../CLAUDE.md) for monorepo-level documentation
- Review TanStack Router docs: https://tanstack.com/router/latest
- Review tRPC docs: https://trpc.io/docs
- Check Vite documentation: https://vite.dev/
