diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 00000000..6b7c2c93 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,593 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Niteshift Sandbox Status + +**This repository is running in a Niteshift sandbox with the following already configured:** + +✅ **Dev server is ALREADY RUNNING** on http://localhost:3001 with hot reload enabled +✅ Database is set up and migrated (PostgreSQL) +✅ All dependencies are installed (pnpm) +✅ Environment variables are configured (.env file exists) +✅ Tracker script and geo database are built + +**Default credentials:** +- Username: `admin` +- Password: `umami` + +**You can start making changes immediately** - all changes will hot reload automatically! + +### TL;DR - Start Prototyping Now + +```typescript +// 1. Access the running app +// http://localhost:3001 (login: admin/umami) + +// 2. Make a quick UI change - edit any file in src/ +import { Button } from '@umami/react-zen'; // Use component library +import { useApi } from '@/components/hooks'; // Fetch data with React Query + +// 3. Save file - see changes instantly in browser! +``` + +**Most common tasks:** +- **New page**: Create `src/app/(main)/[feature]/page.tsx` +- **New component**: Add to `src/components/common/` +- **Fetch data**: Use `useApi()` hook or pre-built query hooks from `src/components/hooks/queries/` +- **Styles**: Use `@umami/react-zen` components (minimal custom CSS needed) + +## Overview + +Umami is a privacy-focused web analytics platform built with Next.js 15, TypeScript, and PostgreSQL. It serves as an alternative to Google Analytics with a focus on simplicity and privacy. + +## Tech Stack + +- **Framework**: Next.js 15 with App Router +- **Language**: TypeScript (ES2022 target) +- **Database**: PostgreSQL with Prisma ORM (supports optional ClickHouse for analytics) +- **Package Manager**: pnpm +- **UI Components**: React 19 with `@umami/react-zen` component library +- **Data Fetching**: `@tanstack/react-query` (React Query) +- **State Management**: Zustand stores +- **Styling**: Minimal custom CSS (CSS Modules with PostCSS), primarily using component library +- **Internationalization**: `react-intl` with custom `useMessages` hook +- **Charts**: Chart.js with react-spring animations +- **Build Tools**: Rollup (for tracker), Next.js with Turbo +- **Testing**: Jest with ts-jest + +## Useful Commands (Niteshift Sandbox) + +**Note:** The dev server is already running - you don't need to start it! + +### Testing +```bash +pnpm test # Run Jest tests +``` + +### Code Quality +```bash +pnpm lint # Run ESLint (optional - runs automatically on commit) +``` + +### Database (if schema changes needed) +```bash +pnpm build-db-client # Regenerate Prisma client after schema changes +pnpm update-db # Apply new migrations +``` + +### If You Need to Rebuild Assets +```bash +pnpm build-tracker # Rebuild tracking script (rarely needed) +pnpm build-geo # Rebuild geo database (rarely needed) +``` + +## Rapid UI Prototyping Guide + +### Quick Start for UI Changes + +**The dev server is already running on http://localhost:3001 with hot reload!** + +1. **Make your changes** - Edit any file in `src/` + +2. **View instantly** - Changes appear in browser automatically (no manual refresh needed) + +3. **Component library**: Use `@umami/react-zen` for all UI primitives: + ```typescript + import { Button, Form, TextField, Modal, Row, Column } from '@umami/react-zen'; + ``` + +4. **Import path alias**: Always use `@/` for imports: + ```typescript + import { useApi } from '@/components/hooks'; + import { formatDate } from '@/lib/date'; + ``` + +### UI Component Architecture + +#### Component Library (`@umami/react-zen`) +The codebase uses a custom component library for UI primitives. Common components include: +- **Layout**: `Row`, `Column`, `Container`, `Grid` +- **Forms**: `Form`, `TextField`, `TextArea`, `Select`, `Checkbox`, `Toggle`, `SearchField` +- **Buttons**: `Button`, `ActionButton`, `IconButton` +- **Display**: `Modal`, `Dropdown`, `Menu`, `Tabs`, `Banner`, `Tooltip` +- **Data**: `Table`, `List`, `Empty` + +**Example component usage**: +```typescript +import { Button, Form, TextField } from '@umami/react-zen'; + +export function MyForm() { + return ( +
+ + + + ); +} +``` + +#### Custom Components (`@/components/common/`) +Pages typically use a **mix** of `@umami/react-zen` components and custom components from `@/components/common/`. Common custom components include: + +- **Layout**: `PageBody`, `PageHeader`, `Panel`, `SectionHeader` +- **Data Display**: `DataGrid`, `Empty`, `EmptyPlaceholder`, `LoadingPanel` +- **Forms**: `ActionForm`, `ConfirmationForm`, `TypeConfirmationForm` +- **UI Elements**: `Avatar`, `Favicon`, `FilterLink`, `LinkButton`, `Pager` +- **Utilities**: `DateDisplay`, `DateDistance`, `ErrorMessage`, `ExternalLink` + +**Example**: Most pages combine both: +```typescript +import { Column } from '@umami/react-zen'; +import { PageBody, PageHeader, Panel } from '@/components/common'; + +export function MyPage() { + return ( + + + + + {/* Content */} + + + + ); +} +``` + +#### Other Component Locations +- **`src/components/input/`**: Input/button components (DateFilter, DownloadButton, WebsiteSelect, etc.) +- **`src/components/charts/`**: Chart components (BarChart, LineChart, etc.) +- **`src/components/metrics/`**: Analytics displays (MetricCard, StatsTable, etc.) +- **`src/components/boards/`**: Dashboard board components + +### Data Fetching Patterns + +#### Pre-built Query Hooks (Recommended) +**Most common pattern**: Use existing query hooks from `src/components/hooks/queries/`. See `src/components/hooks/index.ts` for the full list of available hooks. + +```typescript +import { useWebsitesQuery } from '@/components/hooks/queries/useWebsitesQuery'; +import { useReportsQuery } from '@/components/hooks/queries/useReportsQuery'; +import { useUsersQuery } from '@/components/hooks/queries/useUsersQuery'; + +// In component: +const { data: websites, isLoading } = useWebsitesQuery(); +``` + +Available query hooks include: `useWebsitesQuery`, `useUsersQuery`, `useReportsQuery`, `useTeamsQuery`, `useLinksQuery`, `usePixelsQuery`, `useRealtimeQuery`, `useWebsiteMetricsQuery`, `useWebsiteEventsQuery`, `useSessionDataQuery`, and many more. Check `src/components/hooks/queries/` for the complete list. + +#### Using the `useApi` Hook Directly +For custom API calls or when a pre-built hook doesn't exist, use `useApi()`: + +```typescript +import { useApi } from '@/components/hooks'; +import { useQuery, useMutation } from '@tanstack/react-query'; + +export function MyComponent() { + const { get, post } = useApi(); + + // Option 1: Use with React Query hooks directly + const { data, isLoading, error } = useQuery({ + queryKey: ['custom-data'], + queryFn: () => get('/custom-endpoint'), + }); + + // Option 2: Direct API call (for mutations, form submissions, etc.) + const handleSubmit = async (values) => { + await post('/api/resource', values); + }; + + return
{data?.name}
; +} +``` + +**Note**: Most query hooks internally use `useApi()` with `usePagedQuery()` for pagination support. Prefer pre-built hooks when available. + +### State Management + +#### Zustand Stores +Global state is managed with Zustand stores in `src/store/`: + +```typescript +import { useApp } from '@/store/app'; +import { setLocale, setTimezone, setUser } from '@/store/app'; + +// In component - read state +const user = useApp(state => state.user); +const locale = useApp(state => state.locale); + +// Update state +setUser(userData); +setLocale('en-US'); +``` + +Available stores: +- **`app.ts`**: Global app state (user, locale, theme, timezone, dateRange) +- **`dashboard.ts`**: Dashboard state +- **`websites.ts`**: Websites state +- **`cache.ts`**: Cache management +- **`version.ts`**: Version checking + +### Custom Hooks + +Essential hooks in `src/components/hooks/`: + +#### Data Hooks +- **`useApi()`**: API wrapper with React Query integration +- **`useConfig()`**: Get app configuration +- **`useDateRange()`**: Date range management +- **`useFilters()`**: Filter state management +- **`usePagedQuery()`**: Pagination helper + +#### UI Hooks +- **`useMessages()`**: Internationalization (i18n) messages and formatting +- **`useLocale()`**: Current locale +- **`useFormat()`**: Number, date, and value formatting +- **`useMobile()`**: Mobile device detection +- **`useNavigation()`**: Next.js navigation helpers +- **`useEscapeKey()`**: ESC key handler +- **`useDocumentClick()`**: Click outside handler + +#### Context Hooks +These hooks provide access to React Context values for specific entities: +- **`useUser()`**: Access current user context (from `UserProvider`) +- **`useWebsite()`**: Access current website context (from `WebsiteProvider`) +- **`useTeam()`**: Access current team context (from `TeamProvider`) +- **`useLink()`**: Access current link context (from `LinkProvider`) +- **`usePixel()`**: Access current pixel context (from `PixelProvider`) + +**Usage**: These hooks are typically used within components that are wrapped by their respective providers. For example: +```typescript +import { useWebsite } from '@/components/hooks'; + +export function MyComponent() { + const website = useWebsite(); // Returns website data from WebsiteProvider + return
{website?.name}
; +} +``` + +#### Example Usage: +```typescript +import { useMessages, useFormat, useMobile } from '@/components/hooks'; + +export function MyComponent() { + const { formatMessage, labels } = useMessages(); + const { formatValue } = useFormat(); + const isMobile = useMobile(); + + return ( +
+ {formatMessage(labels.welcome)} + {formatValue(1000, 'number')} +
+ ); +} +``` + +### Internationalization (i18n) + +All user-facing text should use `useMessages`: + +```typescript +import { useMessages } from '@/components/hooks'; + +export function MyComponent() { + const { formatMessage, labels } = useMessages(); + + return ( +
+

{formatMessage(labels.dashboard)}

+

{formatMessage(labels.description)}

+
+ ); +} +``` + +Messages are defined in `src/components/messages.ts` and compiled to JSON files in `public/intl/messages/`. + +### Styling + +#### Approach +- **Minimal custom CSS**: The codebase relies heavily on `@umami/react-zen` components for styling +- **CSS Modules**: Use when custom styles are needed (`.module.css` files) +- **Global styles**: `src/styles/global.css` and `src/styles/variables.css` +- **CSS Variables**: Primary color customization via `--primary-color` + +#### Adding Custom Styles +Only create CSS modules when absolutely necessary: + +```typescript +// MyComponent.module.css +import styles from './MyComponent.module.css'; + +export function MyComponent() { + return
Content
; +} +``` + +### Common UI Patterns + +#### DataGrid Pattern +For lists with search, pagination, and actions: + +```typescript +import { DataGrid } from '@/components/common/DataGrid'; +import { useWebsitesQuery } from '@/components/hooks/queries/useWebsitesQuery'; + +export function WebsiteList() { + const query = useWebsitesQuery(); + + return ( + } + > + {(row) =>
{row.name}
} +
+ ); +} +``` + +#### Form Pattern +Standard form with validation: + +```typescript +import { Form, TextField, Button } from '@umami/react-zen'; +import { useApi } from '@/components/hooks'; + +export function MyForm() { + const { post } = useApi(); + + const handleSubmit = async (values) => { + await post('/api/resource', values); + }; + + return ( +
+ + + + ); +} +``` + +#### Modal Pattern +```typescript +import { Modal, Button } from '@umami/react-zen'; +import { useState } from 'react'; + +export function MyComponent() { + const [showModal, setShowModal] = useState(false); + + return ( + <> + + {showModal && ( + setShowModal(false)}> +
Modal content
+
+ )} + + ); +} +``` + +### Page Structure + +Pages in `src/app/(main)/` follow this pattern: + +```typescript +'use client'; // Required for interactive components + +import { useMessages } from '@/components/hooks'; +import { Button } from '@umami/react-zen'; + +export default function MyPage() { + const { formatMessage, labels } = useMessages(); + + return ( +
+

{formatMessage(labels.title)}

+ {/* Page content */} +
+ ); +} +``` + +**To view your new page:** Navigate to http://localhost:3001/your-route (server is already running!) + +### Tips for Rapid Prototyping in Niteshift + +1. **Zero setup needed**: The dev server is running, database is ready, just start coding! +2. **Instant feedback**: Hot reload is active - save a file and see changes in <1 second +3. **Leverage existing components**: Check `src/components/common/` and `src/components/input/` before creating new components +4. **Use query hooks**: Don't write custom API calls if a query hook exists in `src/components/hooks/queries/` +5. **Follow established patterns**: Look at similar pages/components for consistent patterns +6. **Internationalization**: Always use `useMessages()` for text, never hardcode strings +7. **TypeScript**: Leverage type inference - the codebase has strong typing +8. **Component library first**: Always check if `@umami/react-zen` has what you need before writing custom UI + +## Project Architecture + +### Directory Structure + +- **`src/app/`**: Next.js App Router pages and API routes + - `src/app/(main)/`: Main application pages (authenticated area) + - `src/app/(collect)/`: Analytics data collection endpoints + - `src/app/api/`: REST API routes (admin, auth, teams, websites, reports, etc.) + - `src/app/login/`, `src/app/logout/`, `src/app/sso/`: Authentication pages + - `src/app/share/`: Public sharing pages + +- **`src/lib/`**: Core utility functions and shared logic + - Database utilities (`db.ts`) + - Authentication (`auth.ts`) + - Request/response helpers (`request.ts`, `response.ts`) + - Date utilities (`date.ts`) + - Constants (`constants.ts`) + - IP detection (`ip.ts`) + - Device detection (`detect.ts`) + - Formatting utilities (`format.ts`) + +- **`src/queries/`**: Database query layer + - `src/queries/prisma/`: Prisma queries for PostgreSQL + - `src/queries/sql/`: Raw SQL queries (organized by feature: events, pageviews, sessions, reports) + +- **`src/components/`**: React components + - `src/components/common/`: Shared UI components + - `src/components/input/`: Form input components + - `src/components/charts/`: Chart components + - `src/components/metrics/`: Analytics metric displays + - `src/components/boards/`: Dashboard boards + - `src/components/hooks/`: Custom React hooks + - `src/components/svg/`: SVG icon components (generated by `pnpm build-icons`) + +- **`src/tracker/`**: Client-side tracking script source code + +- **`src/permissions/`**: Authorization and permission checks + +- **`src/store/`**: Zustand state management stores + +- **`src/generated/prisma/`**: Auto-generated Prisma client (DO NOT edit manually) + +- **`prisma/schema.prisma`**: Prisma schema definition + +- **`scripts/`**: Build and utility scripts + - `check-db.js`: Database connection verification + - `build-geo.js`: GeoIP database setup + +### API Architecture + +API routes follow Next.js App Router conventions with `route.ts` files. Standard pattern: + +1. Request parsing with `parseRequest()` from `@/lib/request` +2. Authentication via `checkAuth()` (can be skipped with `skipAuth: true`) +3. Schema validation using Zod +4. Database queries via Prisma or raw SQL +5. Response formatting via helpers from `@/lib/response` + +Example: +```typescript +export async function GET(request: Request) { + const { query, auth, error } = await parseRequest(request, schema); + if (error) return error(); + // ... business logic + return json(data); +} +``` + +### Database Query Pattern + +The codebase supports multiple database backends through `runQuery()` in `src/lib/db.ts`: + +- **Prisma queries** (in `src/queries/prisma/`): Used with PostgreSQL +- **Raw SQL queries** (in `src/queries/sql/`): Direct SQL for complex analytics +- **ClickHouse queries**: Optional high-performance analytics backend + +Use `runQuery()` with object containing query implementations for each backend: +```typescript +return runQuery({ + [PRISMA]: () => prismaQuery(), + [CLICKHOUSE]: () => clickhouseQuery(), +}); +``` + +### Environment Variables + +**In Niteshift:** The `.env` file is already configured with `DATABASE_URL` and any required variables. + +See `next.config.ts` for full list of available environment variables. + +### Key Data Models + +Core entities in `prisma/schema.prisma`: +- **User**: User accounts with authentication +- **Team**: Organizations/teams +- **Website**: Tracked websites +- **Session**: User sessions with device/location data +- **WebsiteEvent**: Individual events/pageviews +- **Report**: Custom analytics reports +- **Link**: Short links (link tracking feature) +- **Pixel**: Tracking pixels + +## Important Notes for Niteshift Development + +### TypeScript Configuration +- Module resolution: `bundler` +- Path alias: `@/*` maps to `src/*` +- Strict mode enabled (except `strictNullChecks`) +- Target: ES2022 +- Types are checked automatically - errors show in editor + +### Testing +- Unit tests in `src/lib/__tests__/` using Jest +- Test pattern: `*.test.ts` or `*.spec.ts` +- Run with `pnpm test` + +### Code Style +- Prettier and ESLint run automatically via pre-commit hooks +- Manually run with `pnpm lint` if needed +- Stylelint for CSS validation + +### Database & Prisma +- Prisma client is in `src/generated/prisma/` (not default location) +- Never edit generated files manually +- If you modify `prisma/schema.prisma`, regenerate with: `pnpm build-db-client` +- Database is already migrated and ready to use + +## Quick Reference for UI Development + +### Most Common Imports +```typescript +// UI Components (from component library) +import { Button, Form, TextField, Modal, Row, Column, Grid } from '@umami/react-zen'; + +// Custom Components (from common) +import { PageBody, PageHeader, Panel, DataGrid, Empty } from '@/components/common'; + +// Hooks +import { useApi, useMessages, useFormat, useWebsite, useUser } from '@/components/hooks'; + +// State +import { useApp } from '@/store/app'; +import { setUser, setLocale, setTimezone } from '@/store/app'; +``` + +### Most Common Patterns +- **New page**: Create in `src/app/(main)/[feature]/page.tsx` +- **New API route**: Create in `src/app/api/[feature]/route.ts` +- **New component**: Add to `src/components/common/` or `src/components/input/` +- **New hook**: Add to `src/components/hooks/` +- **New query hook**: Add to `src/components/hooks/queries/` +- **Global state**: Add to or use existing `src/store/*.ts` + +### Development Workflow (Niteshift Sandbox) +1. **Access app** - http://localhost:3001 (already running!) +2. **Login** - Use admin/umami credentials +3. **Make changes** - Edit files in `src/` +4. **See results** - Hot reload happens automatically, changes appear instantly +5. **Check types** - TypeScript errors show in editor automatically +6. **Test** - `pnpm test` (optional) +7. **Lint** - `pnpm lint` (optional, runs automatically on commit)