# CLAUDE.md

## Project Overview

Officer is an AI-assisted personal backend and frontend for life management.

## Monorepo Structure

```
src/
├── apps/
│   ├── dashboard/    # Main UI (React 19)
│   └── editor/       # Visual editor
├── servers/
│   ├── api/          # REST API (Hono, port 5000)
│   └── tracking/     # Event collection (port 5001)
├── databases/
│   ├── officer_db/   # Main app data
│   └── ephemeral_db/ # Cache & temporary data
└── workspaces/       # Shared: components, hooks, helpers, types, config, etc.
```

**Path aliases**: `@/` → dashboard, `@@/` → servers

## Tech Stack

- **Runtime**: Bun
- **Language**: TypeScript (strict), some raw SQL
- **Frontend**: React 19, React Router, React Query + Context
- **UI**: shadcn/ui base + custom components, Tailwind CSS
- **Backend**: Hono framework
- **Database**: PostgreSQL, Drizzle ORM + raw SQL

## Code Style

- **Paradigm**: Functional - pure functions, immutability, composition
- **TypeScript**: Strict - no `any`, proper types everywhere
- **Comments**: Minimal - code should be self-documenting
- **Async**: Always async/await
- **Errors**: Try/catch (detailed patterns in section-specific docs)

## TypeScript Patterns

### Imports
Order imports as: types → external → workspace → relative
```ts
import type { Experiment, User } from 'types';
import { useState, useEffect } from 'react';
import { useQuery } from '@tanstack/react-query';
import { useClient } from 'hooks/useClient';
import { formatDate } from 'helpers/formatters';
import { useWebsites } from '../useWebsites';
```

**Type-only imports**: With `verbatimModuleSyntax` enabled, types must use type-only imports:
```ts
// ✅ Good - inline type import
import { ActionModals, type ActionModalsTypes } from './ActionModals';

// ✅ Good - separate type import
import type { FormEvent } from 'react';
import { useState } from 'react';

// ❌ Bad - will error with verbatimModuleSyntax
import { ActionModals, ActionModalsTypes } from './ActionModals';
```

### Types
- Prefer `type` over `interface`
- Shared types go in `workspaces/types` - re-exports from database types
- Database types live in their respective `db/types.ts` files
- Component props: colocated as named type

```ts
type ExperimentCardProps = {
  experiment: Experiment;
  onSelect: (id: number) => void;
};
```

### Database Types
Use Drizzle-inferred types, never manual type definitions in schema files:

```ts
// In officerdb/types.ts
import * as Schema from './schema';

// Simple table - just Select/Insert
export type Screenshot = typeof Schema.Screenshots.$inferSelect;
export type ScreenshotInsert = typeof Schema.Screenshots.$inferInsert;

// Table with relations - Select + extended type for API responses
export type UserSelect = typeof Schema.Users.$inferSelect;
export type UserInsert = typeof Schema.Users.$inferInsert;
export type User = UserSelect & {
  company: Company;
  passkeys: Passkey[];
};
```

**In app code**, import types from `'types'` (the workspace re-exports everything):
```ts
// ✅ Good - in apps/dashboard, apps/editor, etc.
import type { User, Experiment, VariantStat } from 'types';

// ❌ Bad - don't import directly from database packages in app code
import type { User } from 'officerdb/types';
import type { VariantStat } from 'statisticsdb/types';
```

**In database/server code**, import from the specific package:
```ts
// ✅ Good - in servers/, databases/
import type { User } from 'officerdb/types';
```

### Functions
- Arrow functions for simple/one-liners
- Regular functions for complex logic
- Named exports only (no default exports)
- **No multiline parameter definitions** - extract params type when signature gets long

```ts
// Simple utility - arrow
export const formatDate = (value: number) => new Date(value).toLocaleDateString();

// Complex logic - regular function
export function calculateStatistics(data: DataPoint[]) {
  // multi-line logic
}

// ❌ Bad - multiline params
export function processData(
  body: Record<string, unknown> | undefined,
  query: Record<string, string | undefined>,
): Result { ... }

// ✅ Good - extract params type
type ProcessDataParams = {
  body: Record<string, unknown> | undefined;
  query: Record<string, string | undefined>;
};

export function processData({ body, query }: ProcessDataParams): Result { ... }
```

### Null Handling
- Early returns for guards
- Optional chaining for property access
- Use `?? undefined` to convert `null` to `undefined` for props expecting `string | undefined`
- Use `?? 0` or `?? ''` for fallback values with state setters

```ts
function processExperiment(exp: Experiment | null) {
  if (!exp) return null;  // guard

  const websiteName = exp.website?.name ?? 'Unknown';  // access
  // ...
}

// Converting null to undefined for props
<AvatarImage src={user.avatar ?? undefined} />  // avatar is string | null, src expects string | undefined

// Fallback for state setters expecting non-null
setWebsite(website?.id ?? 0);  // id might be undefined, setter expects number

// Array access with known valid index - use non-null assertion
const items = ['a', 'b', 'c'];
if (items.length > 0) {
  const first = items[0]!;  // We know index 0 exists after length check
}
```

## React Patterns

See `CONVENTIONS.md` for detailed patterns (feature folders, manager pattern, state management, React 19 rules).

### Components
Arrow function with named props type:
```tsx
type ExperimentCardProps = {
  experiment: Experiment;
  onSelect: (id: number) => void;
};

export const ExperimentCard = ({ experiment, onSelect }: ExperimentCardProps) => {
  return (/* ... */);
};
```

### Hooks
Return objects for complex hooks, tuples for simple state:
```tsx
// Complex - return object
export const useExperiment = (id: number) => {
  return { experiment, isLoading, update, delete: deleteExp };
};

// Simple state - return tuple (like useState)
export const useGlobal = <T>(key: string, initial: T) => {
  return [value, setValue, refresh, reset] as const;
};
```

## Directory Structure

### Naming Conventions
- **Components**: `PascalCase.tsx`
- **Everything else**: `kebab-case.ts`
- **Hooks**: `use-*.ts` or `useFeatureName.ts`

## Formatting

Prettier config in `.prettierrc`:
- Semicolons: always
- Quotes: single (JS), double (JSX)
- Trailing commas: all
- Indent: 2 spaces
- Line width: 120

```bash
bun format       # Format all files
bun format:check # Check without writing
```

## Development

```bash
bun dev          # Runs dashboard + API server
bun dev:tracking # Runs tracking server separately if needed
bunx tsgo        # TypeScript check (not npx tsc)
```

**New features**: Usually database-first (schema → API → UI)

**Environment variables**:
- Backend: `process.env.X` directly
- Frontend: Use existing config patterns

**Logging**: `console.log` for debugging (Signoz integration coming)

## Git

Simple lowercase commit messages, no prefixes.

## Working With Me

- **Ask first**: Confirm approach before significant changes
- **Explore thoroughly**: Read related files for full context
- **Keep it simple**: No over-engineering or premature abstractions
- **Be explicit**: No magic or implicit behavior
- **Stay focused**: Note unrelated issues but don't fix them
- **Detailed output**: Provide full breakdown of changes when completing tasks

## Section-Specific Docs

Detailed patterns for each area live in their respective directories:

**Frontend:**
- `src/apps/CLAUDE.md` - Shared frontend patterns (components, hooks, state)
- `src/apps/dashboard/CLAUDE.md` - Dashboard specifics
- `src/apps/editor/CLAUDE.md` - Editor specifics
**Backend:**
- `src/servers/CLAUDE.md` - API and tracking servers
- `src/databases/CLAUDE.md` - Database schemas and patterns

**Project-wide:**
- `CONVENTIONS.md` - Detailed code patterns with rationale (component organization, state management, React patterns)
