Skip to content

Commit 068b0d9

Browse files
Initial SSR Migration(#3)
1 parent 99319c2 commit 068b0d9

206 files changed

Lines changed: 19595 additions & 12038 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.eslintignore

Lines changed: 0 additions & 2 deletions
This file was deleted.

.eslintrc.json

Lines changed: 37 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,40 @@
11
{
2-
"env": {
3-
"browser": true,
4-
"es2021": true
5-
},
6-
"extends": [
7-
"standard-with-typescript",
8-
"plugin:react/recommended",
9-
"eslint:recommended",
10-
"plugin:@typescript-eslint/eslint-recommended",
11-
"plugin:@typescript-eslint/recommended",
12-
"prettier",
13-
"plugin:prettier/recommended"
14-
],
15-
"parserOptions": {
16-
"ecmaVersion": "latest"
17-
},
18-
"plugins": [
19-
"react",
20-
"@typescript-eslint",
21-
"unused-imports",
22-
"prettier"
23-
],
24-
"rules": {
25-
"@typescript-eslint/no-unused-vars": "error",
26-
"no-console": "warn",
27-
"unused-imports/no-unused-imports": "error",
28-
"react/react-in-jsx-scope": "off",
29-
"prettier/prettier": "error",
30-
"@typescript-eslint/ban-tslint-comment": "off",
31-
"react/prop-types": "off",
32-
"eqeqeq": "off"
2+
"ignorePatterns": [
3+
"dist/",
4+
"build/",
5+
"node_modules/",
6+
".next/",
7+
"coverage/",
8+
"*.config.js"
9+
],
10+
"env": { "browser": true, "es2021": true },
11+
"extends": [
12+
"standard-with-typescript",
13+
"plugin:react/recommended",
14+
"eslint:recommended",
15+
"plugin:@typescript-eslint/eslint-recommended",
16+
"plugin:@typescript-eslint/recommended",
17+
"prettier",
18+
"plugin:prettier/recommended"
19+
],
20+
"parserOptions": {
21+
"ecmaVersion": "latest",
22+
"sourceType": "module",
23+
"project": "./tsconfig.json",
24+
"ecmaFeatures": {
25+
"jsx": true
3326
}
27+
},
28+
"parser": "@typescript-eslint/parser",
29+
"plugins": ["react", "@typescript-eslint", "unused-imports", "prettier"],
30+
"settings": { "react": { "version": "detect" } },
31+
"rules": {
32+
"@typescript-eslint/no-unused-vars": "error",
33+
"unused-imports/no-unused-imports": "error",
34+
"react/react-in-jsx-scope": "off",
35+
"prettier/prettier": "error",
36+
"@typescript-eslint/ban-tslint-comment": "off",
37+
"react/prop-types": "off",
38+
"eqeqeq": "off"
3439
}
35-
40+
}

.github/copilot-instructions.md

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
# Mobility Database Web - AI Agent Instructions
2+
3+
## Project Overview
4+
Next.js 16 (App Router) web application for browsing and managing mobility transit feeds (GTFS, GTFS-RT, GBFS). Uses Firebase Auth, Redux Toolkit for state management, Material-UI (MUI), and TypeScript. Internationalized with next-intl (English/French).
5+
6+
## Architecture Patterns
7+
8+
### Hybrid Next.js Architecture (Migration in Progress)
9+
- **Next.js App Router**: New pages in `src/app/` with Server Components by default ([layout.tsx](src/app/layout.tsx), [feeds/[feedDataType]/page.tsx](src/app/feeds/[feedDataType]/page.tsx))
10+
- **Legacy React Router**: Still active via `<BrowserRouter>` in [App.tsx](src/app/App.tsx) - **will be deprecated**
11+
- When adding features, prefer App Router patterns over React Router
12+
13+
### Client vs Server Components
14+
- **Server Components** (default): Data fetching, API calls, Firebase Admin operations. No `'use client'` directive
15+
- **Client Components**: Interactive UI with hooks, state, events. Mark with `'use client'` at top ([providers.tsx](src/app/providers.tsx), [Header.tsx](src/app/components/Header.tsx))
16+
- **Rule**: Server actions and data fetching happen in server components or API services, client components handle interactivity
17+
18+
### API Integration via OpenAPI-Fetch
19+
- Type-safe API client generated from OpenAPI specs in `external_types/`
20+
- Main client: [src/app/services/feeds/index.ts](src/app/services/feeds/index.ts) - uses `openapi-fetch` with auto-typed paths
21+
- Generate types: `yarn generate:api-types` (DatabaseCatalogAPI) or `yarn generate:gbfs-validator-types`
22+
- Always use generated types from `services/feeds/types.ts` - never duplicate API response types
23+
- Auth via Bearer token middleware: `generateAuthMiddlewareWithToken(accessToken)`
24+
25+
### Firebase Authentication Patterns
26+
- **Client-side**: Firebase compat SDK ([firebase.ts](src/firebase.ts)) with emulator support for Cypress
27+
- **Server-side**: Firebase Admin SDK ([firebase-admin.ts](src/lib/firebase-admin.ts)) for token verification
28+
- Auth state managed via Redux ([profile-reducer.ts](src/app/store/profile-reducer.ts)) with status: `authenticated|unauthenticated|anonymous_login|...`
29+
- Access tokens: `getSSRAccessToken()` for server, Redux state for client
30+
- Cypress tests auto-use Firebase emulator on port 9099
31+
32+
### Internationalization (i18n)
33+
- Next-intl with messages in `messages/{en,fr}.json`
34+
- Use `useTranslations('namespace')` in client components: `const t = useTranslations('feeds')`
35+
- Server components: import from `next-intl/server` - `getLocale()`, `getMessages()`
36+
- Locale from subdomain: `fr.mobilitydatabase.org` → French, else English ([config.ts](src/i18n/config.ts))
37+
38+
### State Management
39+
- **Redux Toolkit**: Global state for auth, profile ([store/profile-reducer.ts](src/app/store/profile-reducer.ts))
40+
- **React Context**: Theme, Remote Config ([providers.tsx](src/app/providers.tsx))
41+
- **Server-side**: React `cache()` for per-request memoization ([remote-config.server.ts](src/lib/remote-config.server.ts))
42+
43+
### Firebase Remote Config
44+
- Server-side fetch cached for 5 min (dev) / 1 hour (prod) in [remote-config.server.ts](src/lib/remote-config.server.ts)
45+
- Passed from server → client via `<Providers remoteConfig={...}>` in [layout.tsx](src/app/layout.tsx)
46+
- Default values in `src/app/interface/RemoteConfig.ts`
47+
48+
## Key Conventions
49+
50+
### Material-UI (MUI) Usage
51+
- Use direct imports: `import { Box, Typography } from '@mui/material'` (NOT barrel file `@mui/material/*`)
52+
- Theme via Emotion + `ThemeRegistry` in [registry.tsx](src/app/registry.tsx)
53+
- Fonts loaded via next/font: Mulish (body), IBM Plex Mono (mono) defined in [layout.tsx](src/app/layout.tsx)
54+
55+
### File Organization
56+
- **Screens**: `src/app/screens/{ScreenName}/` - page-level components
57+
- **Components**: `src/app/components/` - shared UI components
58+
- **Services**: `src/app/services/` - API clients and external integrations
59+
- **Utils**: `src/app/utils/` - helper functions (config, auth, formatting)
60+
- **Functions files**: Logic separated into `*.functions.tsx` ([Feed.functions.tsx](src/app/screens/Feed/Feed.functions.tsx))
61+
62+
### Testing Strategy
63+
- **Unit Tests**: Jest + React Testing Library (files: `*.spec.tsx` or `*.test.tsx`)
64+
- **E2E Tests**: Cypress in `cypress/e2e/` with MSW mocks in [src/mocks/handlers.ts](src/mocks/handlers.ts)
65+
- Run E2E: `yarn e2e:setup` (starts dev + Firebase emulator + MSW), then `yarn e2e:run` or `yarn e2e:open`
66+
- Mock API responses using fixtures from `cypress/fixtures/`
67+
68+
### Environment Variables
69+
- Prefix with `NEXT_PUBLIC_` for client-side access
70+
- Dev env: `.env.development`, prod: `.env`, CI: `.env.test`
71+
- Key vars: `NEXT_PUBLIC_FEED_API_BASE_URL`, `NEXT_PUBLIC_FIREBASE_*`, `NEXT_PUBLIC_API_MOCKING`
72+
- Mock mode: `NEXT_PUBLIC_API_MOCKING=enabled yarn start:dev:mock` (port 3001)
73+
74+
## Development Workflows
75+
76+
### Starting Development
77+
```bash
78+
yarn install # Prefer yarn over npm
79+
yarn start:dev # Dev server on :3000 with hot reload
80+
yarn start:dev:mock # Dev server with MSW mocks on :3001
81+
```
82+
83+
### Testing Commands
84+
```bash
85+
yarn test # Unit tests
86+
yarn test:watch # Watch mode
87+
yarn e2e:setup # Start dev + Firebase emulator for E2E
88+
yarn e2e:open # Cypress interactive mode
89+
```
90+
91+
### Building & Deploying
92+
```bash
93+
yarn build:prod # Production build (standalone output)
94+
yarn start:prod # Build + start locally
95+
yarn lint # ESLint check
96+
yarn lint:fix # Auto-fix linting issues
97+
```
98+
99+
### Regenerating API Types
100+
```bash
101+
yarn generate:api-types # DatabaseCatalogAPI → types.ts
102+
yarn generate:gbfs-validator-types # GbfsValidator → gbfs-validator-types.ts
103+
```
104+
Run after updating OpenAPI specs in `external_types/`
105+
106+
## Common Patterns
107+
108+
### Data Fetching in Server Components
109+
```tsx
110+
// pages fetch data directly with access token
111+
export default async function FeedPage({ params }) {
112+
const accessToken = await getSSRAccessToken();
113+
const feed = await getFeed(feedId, accessToken);
114+
return <FeedDetails feed={feed} />;
115+
}
116+
```
117+
118+
### Parallel Data Loading
119+
Use `Promise.all` in server components ([layout.tsx](src/app/layout.tsx)):
120+
```tsx
121+
const [locale, messages, remoteConfig] = await Promise.all([
122+
getLocale(), getMessages(), getRemoteConfigValues()
123+
]);
124+
```
125+
126+
### Client Component with Translations
127+
```tsx
128+
'use client';
129+
import { useTranslations } from 'next-intl';
130+
131+
export function MyComponent() {
132+
const t = useTranslations('feeds'); // namespace from messages/
133+
return <div>{t('labelKey')}</div>;
134+
}
135+
```
136+
137+
### Conditional Rendering for Auth
138+
Redux state for auth checks in client components, check `status` from profile reducer.
139+
140+
## Critical Notes
141+
142+
- **Route hack**: `/feeds/[feedDataType]` actually receives `feedId` for backward compatibility ([page.tsx](src/app/feeds/[feedDataType]/page.tsx)) - redirects to proper route
143+
- **TypeScript strict mode enabled**: Handle nullish values explicitly
144+
- **Cypress uses Firebase emulator**: Tests run against auth emulator, not production Firebase
145+
- **Server-only code**: Mark with `import 'server-only'` for server utils ([remote-config.server.ts](src/lib/remote-config.server.ts))
146+
- **Jest transform exceptions**: Some node_modules need manual transformation - see `transformIgnorePatterns` in [jest.config.ts](jest.config.ts)
147+
148+
## Resources
149+
- Node v24.12.0 (npm v11.6.2, yarn v1.22.22)
150+
- [Next.js App Router docs](https://nextjs.org/docs/app)
151+
- [next-intl docs](https://next-intl-docs.vercel.app/)
152+
- [openapi-fetch](https://openapi-ts.dev/openapi-fetch/)
153+
154+
155+
Always load and apply the following project agent skills when reviewing or generating code:
156+
157+
- vercel-react-best-practices (from .github/skills/vercel-react-best-practices)
158+
159+
If the skill is available, prefer its guidance over generic Copilot heuristics.
160+
If it is not available, emulate its rules as closely as possible.
161+
162+
Acknowledge when the skill is applied.

0 commit comments

Comments
 (0)