This file provides guidance to Claude Code when working in the repository. Key sections include: - Niteshift sandbox configuration status - Rapid UI prototyping guide with component library usage - Data fetching patterns with React Query - State management with Zustand - Common UI patterns and code examples - Project architecture overview Optimized for Niteshift sandbox environment where dev server and database are pre-configured and running. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
19 KiB
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
// 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 fromsrc/components/hooks/queries/ - Styles: Use
@umami/react-zencomponents (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-zencomponent 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-intlwith customuseMessageshook - 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
pnpm test # Run Jest tests
Code Quality
pnpm lint # Run ESLint (optional - runs automatically on commit)
Database (if schema changes needed)
pnpm build-db-client # Regenerate Prisma client after schema changes
pnpm update-db # Apply new migrations
If You Need to Rebuild Assets
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!
-
Make your changes - Edit any file in
src/ -
View instantly - Changes appear in browser automatically (no manual refresh needed)
-
Component library: Use
@umami/react-zenfor all UI primitives:import { Button, Form, TextField, Modal, Row, Column } from '@umami/react-zen'; -
Import path alias: Always use
@/for imports: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:
import { Button, Form, TextField } from '@umami/react-zen';
export function MyForm() {
return (
<Form>
<TextField name="email" placeholder="Email" />
<Button type="submit">Submit</Button>
</Form>
);
}
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:
import { Column } from '@umami/react-zen';
import { PageBody, PageHeader, Panel } from '@/components/common';
export function MyPage() {
return (
<PageBody>
<Column margin="2">
<PageHeader title="My Page" />
<Panel>
{/* Content */}
</Panel>
</Column>
</PageBody>
);
}
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.
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():
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 <div>{data?.name}</div>;
}
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/:
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 statewebsites.ts: Websites statecache.ts: Cache managementversion.ts: Version checking
Custom Hooks
Essential hooks in src/components/hooks/:
Data Hooks
useApi(): API wrapper with React Query integrationuseConfig(): Get app configurationuseDateRange(): Date range managementuseFilters(): Filter state managementusePagedQuery(): Pagination helper
UI Hooks
useMessages(): Internationalization (i18n) messages and formattinguseLocale(): Current localeuseFormat(): Number, date, and value formattinguseMobile(): Mobile device detectionuseNavigation(): Next.js navigation helpersuseEscapeKey(): ESC key handleruseDocumentClick(): Click outside handler
Context Hooks
These hooks provide access to React Context values for specific entities:
useUser(): Access current user context (fromUserProvider)useWebsite(): Access current website context (fromWebsiteProvider)useTeam(): Access current team context (fromTeamProvider)useLink(): Access current link context (fromLinkProvider)usePixel(): Access current pixel context (fromPixelProvider)
Usage: These hooks are typically used within components that are wrapped by their respective providers. For example:
import { useWebsite } from '@/components/hooks';
export function MyComponent() {
const website = useWebsite(); // Returns website data from WebsiteProvider
return <div>{website?.name}</div>;
}
Example Usage:
import { useMessages, useFormat, useMobile } from '@/components/hooks';
export function MyComponent() {
const { formatMessage, labels } = useMessages();
const { formatValue } = useFormat();
const isMobile = useMobile();
return (
<div>
{formatMessage(labels.welcome)}
{formatValue(1000, 'number')}
</div>
);
}
Internationalization (i18n)
All user-facing text should use useMessages:
import { useMessages } from '@/components/hooks';
export function MyComponent() {
const { formatMessage, labels } = useMessages();
return (
<div>
<h1>{formatMessage(labels.dashboard)}</h1>
<p>{formatMessage(labels.description)}</p>
</div>
);
}
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-zencomponents for styling - CSS Modules: Use when custom styles are needed (
.module.cssfiles) - Global styles:
src/styles/global.cssandsrc/styles/variables.css - CSS Variables: Primary color customization via
--primary-color
Adding Custom Styles
Only create CSS modules when absolutely necessary:
// MyComponent.module.css
import styles from './MyComponent.module.css';
export function MyComponent() {
return <div className={styles.container}>Content</div>;
}
Common UI Patterns
DataGrid Pattern
For lists with search, pagination, and actions:
import { DataGrid } from '@/components/common/DataGrid';
import { useWebsitesQuery } from '@/components/hooks/queries/useWebsitesQuery';
export function WebsiteList() {
const query = useWebsitesQuery();
return (
<DataGrid
query={query}
allowSearch
allowPaging
renderActions={() => <Button>Add Website</Button>}
>
{(row) => <div>{row.name}</div>}
</DataGrid>
);
}
Form Pattern
Standard form with validation:
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 (
<Form onSubmit={handleSubmit}>
<TextField name="name" label="Name" required />
<Button type="submit">Submit</Button>
</Form>
);
}
Modal Pattern
import { Modal, Button } from '@umami/react-zen';
import { useState } from 'react';
export function MyComponent() {
const [showModal, setShowModal] = useState(false);
return (
<>
<Button onClick={() => setShowModal(true)}>Open</Button>
{showModal && (
<Modal onClose={() => setShowModal(false)}>
<div>Modal content</div>
</Modal>
)}
</>
);
}
Page Structure
Pages in src/app/(main)/ follow this pattern:
'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 (
<div>
<h1>{formatMessage(labels.title)}</h1>
{/* Page content */}
</div>
);
}
To view your new page: Navigate to http://localhost:3001/your-route (server is already running!)
Tips for Rapid Prototyping in Niteshift
- Zero setup needed: The dev server is running, database is ready, just start coding!
- Instant feedback: Hot reload is active - save a file and see changes in <1 second
- Leverage existing components: Check
src/components/common/andsrc/components/input/before creating new components - Use query hooks: Don't write custom API calls if a query hook exists in
src/components/hooks/queries/ - Follow established patterns: Look at similar pages/components for consistent patterns
- Internationalization: Always use
useMessages()for text, never hardcode strings - TypeScript: Leverage type inference - the codebase has strong typing
- Component library first: Always check if
@umami/react-zenhas what you need before writing custom UI
Project Architecture
Directory Structure
-
src/app/: Next.js App Router pages and API routessrc/app/(main)/: Main application pages (authenticated area)src/app/(collect)/: Analytics data collection endpointssrc/app/api/: REST API routes (admin, auth, teams, websites, reports, etc.)src/app/login/,src/app/logout/,src/app/sso/: Authentication pagessrc/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)
- Database utilities (
-
src/queries/: Database query layersrc/queries/prisma/: Prisma queries for PostgreSQLsrc/queries/sql/: Raw SQL queries (organized by feature: events, pageviews, sessions, reports)
-
src/components/: React componentssrc/components/common/: Shared UI componentssrc/components/input/: Form input componentssrc/components/charts/: Chart componentssrc/components/metrics/: Analytics metric displayssrc/components/boards/: Dashboard boardssrc/components/hooks/: Custom React hookssrc/components/svg/: SVG icon components (generated bypnpm 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 scriptscheck-db.js: Database connection verificationbuild-geo.js: GeoIP database setup
API Architecture
API routes follow Next.js App Router conventions with route.ts files. Standard pattern:
- Request parsing with
parseRequest()from@/lib/request - Authentication via
checkAuth()(can be skipped withskipAuth: true) - Schema validation using Zod
- Database queries via Prisma or raw SQL
- Response formatting via helpers from
@/lib/response
Example:
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:
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 tosrc/* - 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.tsor*.spec.ts - Run with
pnpm test
Code Style
- Prettier and ESLint run automatically via pre-commit hooks
- Manually run with
pnpm lintif 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
// 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/orsrc/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)
- Access app - http://localhost:3001 (already running!)
- Login - Use admin/umami credentials
- Make changes - Edit files in
src/ - See results - Hot reload happens automatically, changes appear instantly
- Check types - TypeScript errors show in editor automatically
- Test -
pnpm test(optional) - Lint -
pnpm lint(optional, runs automatically on commit)