Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
148 changes: 96 additions & 52 deletions claude-code/smartem-frontend/REPO-GUIDELINES.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,68 +2,93 @@

## Development Environment

- **Node.js**: 20+ (strict requirement)
- **Package Manager**: npm
- **Node.js**: 22+ (strict requirement)
- **Package Manager**: npm (with workspaces)
- **Code Style**: Biome for TS/JS, Prettier for YAML/MD
- **Git Hooks**: Lefthook (auto-installed via npm install)

## Code Standards

- **No Emojis**: Consistent with backend - no emojis in code, commits, or docs
- **No Emojis**: Consistent with backend no emojis in code, commits, or docs
- **British English**: Use British spelling in documentation and UI text
- **TypeScript**: Strict mode, no `any` types without justification
- **Imports**: Let Biome handle import sorting automatically

## Project Structure (Monorepo)

The repository uses npm workspaces. See [ADR-0017](../../docs/decision-records/decisions/0017-smartem-frontend-monorepo-restructure.md) for the decision record.

```
smartem-frontend/
├── apps/
│ ├── legacy/ # Existing application (@smartem/legacy)
│ │ ├── src/
│ │ │ ├── components/ # Reusable React components
│ │ │ ├── hooks/ # Custom React hooks
│ │ │ ├── routes/ # Route components
│ │ │ └── root.tsx # Application root with providers
│ │ ├── package.json
│ │ └── vite.config.ts
│ └── smartem/ # New application (@smartem/app)
│ ├── src/
│ ├── package.json
│ └── vite.config.ts
├── packages/
│ ├── api/ # Shared API client (@smartem/api)
│ │ ├── src/
│ │ │ ├── generated/ # Orval output (DO NOT EDIT)
│ │ │ ├── mutator.ts # Axios configuration
│ │ │ ├── stubs.ts # Development stubs
│ │ │ └── index.ts # Barrel export
│ │ ├── openapi.json # OpenAPI spec (version controlled)
│ │ └── orval.config.ts
│ └── ui/ # Shared UI component library (@smartem/ui)
│ └── src/
├── package.json # Workspace root
├── biome.json # Shared lint/format config
├── tsconfig.base.json # Shared TS config (apps extend)
├── tsconfig.json # Root project references
└── lefthook.yml # Shared git hooks
```

## Common Commands

```bash
# Development
npm install # Install dependencies
npm run dev # Start dev server (port 5174)
npm run dev:mock # Dev with mock API data
npm install # Install all workspace dependencies
npm run dev # Legacy app dev server
npm run dev:mock # Legacy app with mock API data
npm run dev:smartem # New app dev server
npm run dev:smartem:mock # New app with mock API data

# API Client
npm run api:update # Fetch OpenAPI spec + regenerate client
npm run api:fetch:local # Fetch from local backend (localhost:8000)
npm run api:generate # Regenerate client from current spec
npm run api:update # Fetch OpenAPI spec + regenerate client
npm run api:fetch:local # Fetch from local backend (localhost:8000)
npm run api:generate # Regenerate client from current spec

# Code Quality
npm run check # Format + lint + imports (recommended)
npm run check:fix # Auto-fix all issues
npm run lint # Lint only
npm run format # Format only
npm run typecheck # TypeScript checking
npm run check # Biome lint + format (recommended)
npm run check:fix # Biome auto-fix all issues
npm run lint # Lint only
npm run format # Format (Biome + Prettier)
npm run typecheck # TypeScript checking across all workspaces

# Build
npm run build # Production build
npm start # Run production server
```

## Project Structure

```
app/
├── api/
│ ├── generated/ # Auto-generated API client (DO NOT EDIT)
│ ├── mutator.ts # Axios configuration
│ └── openapi.json # OpenAPI spec (version controlled)
├── components/ # Reusable React components
├── hooks/ # Custom React hooks
├── routes/ # Route components
└── root.tsx # Application root with providers
npm run build # Build legacy app
npm run build:smartem # Build new app
```

## API Client Workflow

The frontend uses Orval to generate a type-safe API client from the backend OpenAPI spec.
The frontend uses Orval to generate a type-safe API client from the backend OpenAPI spec. The client lives in `packages/api/` and is shared across both apps as `@smartem/api`.

### When Backend API Changes

```bash
# 1. Regenerate client
npm run api:update

# 2. Check for type errors
# 2. Check for type errors across all workspaces
npm run typecheck

# 3. Update components using changed endpoints
Expand All @@ -72,7 +97,7 @@ npm run typecheck
### Using Generated Hooks

```typescript
import { useGetAcquisitionsAcquisitionsGet } from '../api/generated/default/default'
import { useGetAcquisitionsAcquisitionsGet } from '@smartem/api'

function MyComponent() {
const { data, isLoading, error } = useGetAcquisitionsAcquisitionsGet()
Expand All @@ -96,7 +121,7 @@ npm run api:update
### Biome (TS/JS/JSON)

- Linting + formatting + import sorting
- Config: `biome.json`
- Config: `biome.json` (workspace root, shared)
- Faster than ESLint + Prettier combined

### Prettier (YAML/MD)
Expand Down Expand Up @@ -125,37 +150,56 @@ git commit --no-verify -m "message"

## Technology Stack

| Category | Technology |
|----------|------------|
| Framework | React 19 |
| Routing | React Router 7 (SSR) |
| State | TanStack Query |
| UI Components | Material-UI |
| Styling | Tailwind CSS |
| API Client | Orval (generated) |
| Build | Vite |
| Linting | Biome |
| Git Hooks | Lefthook |
| Category | Technology | Notes |
|----------|------------|-------|
| Framework | React 19 | |
| Routing (legacy) | React Router 7 | Legacy app only |
| Routing (new) | TanStack Router | New app — type-safe, SPA |
| Server State | TanStack Query | Auto-generated hooks via Orval |
| Tables | TanStack Table | New app — headless, full styling control |
| UI Components | Material UI | |
| Component Library | sci-react-ui + @smartem/ui | sci-react-ui for legacy; @smartem/ui for new |
| API Client | Orval (generated) | Shared via @smartem/api package |
| Build | Vite | Per-app config |
| Linting | Biome | Workspace root config |
| Git Hooks | Lefthook | |

## Environment Configuration

| Environment | API Endpoint |
|-------------|--------------|
| Development | Vite proxy `/api` -> `localhost:8000` |
| Development | Vite proxy `/api` → `localhost:8000` |
| Mock | MSW (Mock Service Worker) with client-side stubs |
| Production | `VITE_API_ENDPOINT` env var or `localhost:8000` |

## Route Structure

### Legacy app (`apps/legacy`)

| Route | Page |
|-------|------|
| `/` | Session List (Home) |
| `/acquisitions/:acqId` | Session Dashboard |
| `/acquisitions/:acqId/grids/:gridId` | Grid Explorer |
| `/acquisitions/:acqId/grids/:gridId/atlas` | Atlas View |
| `/acquisitions/:acqId/grids/:gridId/gridsquares` | Grid Square Gallery |
| `/acquisitions/:acqId/grids/:gridId/workspace` | Workspace |
| `/acquisitions/:acqId/grids/:gridId/squares/:squareId` | Grid Square Detail |
| `/acquisitions/:acqId/grids/:gridId/square/:squareId/predictions` | Quality Analytics |
| `/models` | Models Overview |
| `/models/:modelName/grids/:gridId/weights` | Model Weights |

### New app (`apps/smartem`)

Uses session-anchored nesting. Route structure is under active design — see the [design specification](../../docs/decision-records/smartem-frontend-design.md) for the current plan.

## Claude Workflow

1. Make changes to components/code
2. Run `npm run check:fix` to auto-fix formatting/lint issues
3. Run `npm run typecheck` to verify types
3. Run `npm run typecheck` to verify types across all workspaces
4. If API-related changes, ensure `npm run api:update` was run first

## Routes

- `/` - Main application (acquisition view)
- `/admin` - System administration (separate concerns, may be split later)

## Contributing Checklist

- [ ] No TypeScript errors (`npm run typecheck`)
Expand Down
1 change: 1 addition & 0 deletions docs/decision-records/decisions.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,6 @@ Architectural decisions are made throughout a project's lifetime. As a way of ke
- [ADR-0014: JSON Config with TypeScript Types](/docs/explanations/decisions/0014-json-config-with-typescript-types)
- [ADR-0015: SmartEM Release Architecture](/docs/explanations/decisions/0015-smartem-release-architecture)
- [ADR-0016: Facility Connector Fork Synchronization](/docs/explanations/decisions/0016-facility-connector-fork-sync) (Proposed)
- [ADR-0017: SmartEM Frontend Monorepo Restructure](/docs/explanations/decisions/0017-smartem-frontend-monorepo-restructure)

For more information on ADRs see this [blog by Michael Nygard](http://thinkrelevance.com/blog/2011/11/15/documenting-architecture-decisions).
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
# 17. SmartEM Frontend Monorepo Restructure

Date: 2026-02-13

## Status

Accepted

## Context

The smartem-frontend repository was a single-app React project with all source code under `src/`. This layout created several problems:

1. **Parallel development blocked**: Building a redesigned frontend alongside the existing application was impractical within a single `src/` tree. Shared dependencies (API client, UI components) had no clean separation from application-specific code.
2. **API client coupling**: The auto-generated API client (Orval + React Query hooks) lived inside the application source, making it impossible to share between applications without circular imports or copy-paste.
3. **UI component isolation**: The planned `smartem-ui` component library (a local complement to sci-react-ui) needed its own package boundary to enforce clean interfaces and enable future extraction.

A frontend design specification identified the monorepo restructure as a prerequisite for all further development. The existing application needed to remain fully functional during and after the transition, with the new application developed in parallel.

## Decision

Restructure smartem-frontend into an npm workspaces monorepo with the following layout:

```
smartem-frontend/
├── apps/
│ ├── legacy/ # Existing application (moved from src/)
│ │ ├── src/
│ │ ├── package.json # @smartem/legacy
│ │ └── vite.config.ts
│ └── smartem/ # New application (fresh scaffold)
│ ├── src/
│ ├── package.json # @smartem/app
│ └── vite.config.ts
├── packages/
│ ├── api/ # Shared API client
│ │ ├── src/
│ │ │ ├── generated/ # Orval output (DO NOT EDIT)
│ │ │ ├── mutator.ts # Axios configuration
│ │ │ ├── stubs.ts # Development stubs
│ │ │ └── index.ts # Barrel export
│ │ ├── orval.config.ts
│ │ └── package.json # @smartem/api
│ └── ui/ # Shared UI component library
│ ├── src/
│ └── package.json # @smartem/ui
├── package.json # Workspace root (npm workspaces config)
├── biome.json # Shared lint/format config
├── tsconfig.base.json # Shared TypeScript config (apps extend)
├── tsconfig.json # Root project references
└── lefthook.yml # Shared git hooks
```

### Workspace packages

| Package | Name | Purpose |
|---------|------|---------|
| `apps/legacy` | `@smartem/legacy` | Existing application, moved from `src/` |
| `apps/smartem` | `@smartem/app` | New application with redesigned shell and routes |
| `packages/api` | `@smartem/api` | Auto-generated API client (Orval config, hooks, mutator, types, stubs) |
| `packages/ui` | `@smartem/ui` | SmartEM UI component library (local, eventually syncs with sci-react-ui) |

### Key patterns

- **One app runs at a time**: `npm run dev` for legacy, `npm run dev:smartem` for new
- **Barrel exports**: `@smartem/api` re-exports all generated hooks via `index.ts`, so consumers import from `'@smartem/api'` rather than reaching into generated internals
- **Shared tooling at root**: Biome, Lefthook, Prettier, and TypeScript base config live at the workspace root. Each app extends `tsconfig.base.json`.
- **SPA-only**: Both applications are pure client-side SPAs (no SSR, no server components)
- **sci-react-ui**: Remains a dependency of the legacy app. `packages/ui` is the new local library that will eventually sync with sci-react-ui (take from and contribute back).

### Root-level scripts

```bash
npm run dev # Legacy app dev server
npm run dev:smartem # New app dev server
npm run typecheck # TypeScript checking across all workspaces
npm run api:update # Fetch OpenAPI spec + regenerate client
npm run check # Biome lint + format
npm run check:fix # Biome auto-fix
```

## Consequences

### Positive

- **Parallel development**: Legacy and redesigned applications coexist cleanly, sharing the API client and UI components without interference
- **Shared packages**: `@smartem/api` is imported identically from both apps, eliminating duplication and ensuring API client changes propagate automatically
- **Clean separation**: Application-specific code, shared libraries, and tooling configuration each have clear boundaries
- **Incremental migration**: Users can continue using the legacy app while the new application is developed; switching requires only changing which app is built/deployed
- **Future extraction**: `@smartem/ui` has a clean package boundary from day one, simplifying eventual contribution back to sci-react-ui

### Negative

- **CI complexity**: Build, lint, and typecheck workflows must be updated to handle workspaces (workspace-aware scripts, potentially per-app build matrix)
- **Workspace learning curve**: Contributors unfamiliar with npm workspaces may need guidance on hoisting, cross-package imports, and workspace-specific commands
- **Dockerfile updates**: The container build needs updating to handle the monorepo layout (separate concern, tracked separately)

### Neutral

- Node.js minimum version raised from 20 to 22 (aligns with current LTS)
- React Router 7 (legacy) and TanStack Router (new app) coexist in separate workspaces without conflict
Loading