Skip to content
Open
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
4 changes: 0 additions & 4 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@ jobs:

- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 10.15.1

- name: Setup Node.js
uses: actions/setup-node@v4
Expand All @@ -45,8 +43,6 @@ jobs:

- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 10.15.1

- name: Setup Node.js
uses: actions/setup-node@v4
Expand Down
136 changes: 136 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Commands

```bash
pnpm dev # Start development server at localhost:3000
pnpm build # Build for production
pnpm test # Run tests in watch mode
pnpm test:run # Run tests once
pnpm lint # Run ESLint + Biome lint + Biome format checks
pnpm lint:fix # Auto-fix linting/formatting issues
```

## Git & GitHub

Use `gh` CLI for GitHub operations:

```bash
gh pr create --title "Title" --body "Description" # Create PR
gh pr view [number] # View PR details
gh pr edit [number] --body "New description" # Edit PR
gh pr merge [number] # Merge PR
gh pr list # List open PRs
gh issue list # List issues
gh issue view [number] # View issue details
```

## Architecture

This is a Next.js 15 App Router application displaying an interactive Mapbox map of Chattanooga bike routes and resources.

### Project Structure

```
src/
├── app/ # Next.js App Router pages
├── components/
│ ├── Map.tsx # Main map orchestrator
│ ├── MapLegend.tsx # Sidebar container with state management
│ ├── MapMarkers.tsx # Marker factory and MarkerManager class
│ └── sidebar/ # Extracted sidebar components
│ ├── BikeRoutes.tsx, MapLayers.tsx, etc.
│ ├── types.ts # Shared interfaces
│ └── index.ts # Barrel export
├── config/
│ └── map.config.ts # Centralized geo config (for multi-geography support)
├── data/
│ ├── geo_data.ts # Static routes, attractions, bike shops
│ └── gbfs.ts # Live bike share API integration
├── hooks/
│ ├── useToast.ts # Toast notification with auto-dismiss
│ ├── useMapResize.ts # Window/sidebar resize handling
│ └── useLocationTracking.ts
└── utils/
└── map.ts # Geocoding, route opacity, bounds utilities
```

### Core Data Flow

1. **Page Entry** (`src/app/page.tsx`): Dynamically imports Map component with SSR disabled (Mapbox requires browser)
2. **Map Component** (`src/components/Map.tsx`): Main orchestrator that initializes Mapbox, manages markers, and handles custom events
3. **Data Sources** (`src/data/`):
- `geo_data.ts`: Static data for bike routes, attractions, bike shops (BikeRoute, MapFeature, BikeResource interfaces)
- `gbfs.ts`: Live bike share station data from Chattanooga GBFS API

### Configuration

`src/config/map.config.ts` centralizes all geography-specific settings:
- Mapbox access token and style URL
- Default map view (center, zoom, pitch, bearing)
- GBFS API endpoints
- Region metadata

This enables future multi-geography support by swapping config files.

### Event-Driven Communication

The app uses custom DOM events (`window.dispatchEvent`) for component communication:

| Event | Dispatched By | Handled By | Purpose |
|-------|--------------|------------|---------|
| `route-select` | Sidebar, Map | Map, Sidebar | Bidirectional sync: highlights route on map AND in sidebar |
| `layer-toggle` | Sidebar | Map | Shows/hides marker layers (attractions, bikeResources, bikeRentals) |
| `center-location` | Sidebar | Map | Pans map to a specific location |
| `route-deselect` | Sidebar | Map | Resets all route opacities |
| `sidebar-toggle` | Sidebar | Map | Triggers map resize after sidebar animation |

**Important**: `route-select` is bidirectional - both Map and MapLegend listen for it. When clicking a route on the map, the map dispatches the event and MapLegend updates its selection state. When clicking in the sidebar, MapLegend dispatches the event and Map handles the visual update.

### Marker System

`MapMarkers.tsx` provides factory functions for different marker types and a `MarkerManager` class for bulk operations. Markers are pre-created at init but only added to map when their layer is toggled on.

### Map Styling

Routes are styled via Mapbox Studio (referenced by layer IDs like `riverwalk-loop-v3-public`). Route bounds are calculated from layer features at runtime to enable zoom-to-fit.

## Code Style

- Do not include "Co-Authored-By: Claude" in commit messages
- Use `function` keyword for pure functions and components
- Prefer interfaces over type aliases; avoid enums (use maps)
- Use functional components; minimize `use client`
- File order: exported component → subcomponents → helpers → static content → types
- Use existing icon libraries (Font Awesome or lucide-react) - don't add new ones
- Directories use lowercase-dash naming

## Testing

### Unit Tests

Tests are in `*.test.ts` or `*.test.tsx` files adjacent to their source files. Run with `pnpm test:run`.

Key test files:
- `src/utils/map.test.ts` - Utility function tests
- `src/config/map.config.test.ts` - Configuration tests
- `src/hooks/useToast.test.ts` - Hook tests
- `src/components/sidebar/BikeRoutes.test.tsx` - Component tests
- `src/data/gbfs.test.ts` - API integration tests

### Mapbox Testing Limitations

**Synthetic events don't trigger Mapbox layer clicks.** Mapbox's internal event system requires real user interactions to detect clicks on map layers. When testing:
- You cannot programmatically click on route lines using `MouseEvent`
- Use `window.dispatchEvent(new CustomEvent('route-select', { detail: { routeId } }))` to simulate what the map would do
- The Chrome DevTools MCP server can take screenshots but cannot trigger Mapbox layer events

### Browser Testing

Use Chrome DevTools MCP server for visual verification:
- Take screenshots to verify UI state
- Click on DOM elements (sidebar buttons work)
- Dispatch custom events to test event handlers
- Cannot test direct map layer interactions (requires manual testing)
1 change: 1 addition & 0 deletions next-env.d.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/// <reference types="next" />
/// <reference types="next/image-types/global" />
/// <reference types="next/navigation-types/compat/navigation" />
/// <reference path="./.next/types/routes.d.ts" />

// NOTE: This file should not be edited
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "bikemap",
"version": "0.1.0",
"private": true,
"packageManager": "pnpm@10.15.1",
"packageManager": "pnpm@10.27.0+sha512.72d699da16b1179c14ba9e64dc71c9a40988cbdc65c264cb0e489db7de917f20dcf4d64d8723625f2969ba52d4b7e2a1170682d9ac2a5dcaeaab732b7e16f04a",
"scripts": {
"dev": "next dev",
"build": "next build",
Expand Down Expand Up @@ -35,7 +35,7 @@
"tailwindcss-animate": "^1.0.7"
},
"devDependencies": {
"@biomejs/biome": "^2.3.10",
"@biomejs/biome": "^2.3.11",
"@eslint/eslintrc": "^3.3.3",
"@testing-library/jest-dom": "^6.9.1",
"@testing-library/react": "^16.3.1",
Expand Down
Loading