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
958 changes: 958 additions & 0 deletions frontend/.claude/api-type-map.json

Large diffs are not rendered by default.

437 changes: 437 additions & 0 deletions frontend/.claude/commands/api-types-sync.md

Large diffs are not rendered by default.

13 changes: 13 additions & 0 deletions frontend/.claude/commands/api.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
---
description: Generate a new RTK Query API service
---

IMPORTANT: Before starting, always run `/api-types-sync` to ensure frontend types are in sync with the backend.

Generate a new API service. Follow these steps:

1. Run `/api-types-sync` to sync types with the backend (compares with latest backend in main)
2. Go through the process mentioned in `.claude/context/api-integration.md`
3. If I haven't specified, attempt to find where I'd want to create this component in the frontend

Context file: `.claude/context/api-integration.md`
23 changes: 23 additions & 0 deletions frontend/.claude/commands/backend.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
---
description: Search the backend codebase for endpoint details
---

Search the `../api/` Django backend codebase for the requested endpoint.

Look for:
1. **URLs**: `../api/<app>/urls.py` - Route definitions and URL patterns
2. **Views**: `../api/<app>/views.py` - ViewSets and API logic
3. **Serializers**: `../api/<app>/serializers.py` - Request/response schemas
4. **Models**: `../api/<app>/models.py` - Data models
5. **Permissions**: Check for permission classes and authentication requirements

Common Django apps in Flagsmith:
- `organisations/` - Organization management
- `projects/` - Project management
- `environments/` - Environment configuration
- `features/` - Feature flags
- `segments/` - User segments
- `users/` - User management
- `audit/` - Audit logging

API base URL: `/api/v1/`
9 changes: 9 additions & 0 deletions frontend/.claude/commands/check-staged.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
description: Run linting on staged files
---

Run linting on all currently staged files, similar to pre-commit hooks:

1. Run `npx lint-staged --allow-empty` to lint only staged files
2. Report any linting issues found
3. If errors exist, offer to fix them
9 changes: 9 additions & 0 deletions frontend/.claude/commands/check.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
description: Run type checking and linting
---

Run the following checks on the codebase:

1. `npx lint-staged --allow-empty` - Fix linting issues on staged files only (same as git hook)

Report any errors found and offer to fix them.
16 changes: 16 additions & 0 deletions frontend/.claude/commands/context.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
---
description: Load detailed context files for specific topics
---

Available context files in `.claude/context/`:

1. **api-integration.md** - API integration workflow, RTK Query patterns, service creation
2. **api-types-sync.md** - Type synchronization between Django backend and TypeScript frontend
3. **architecture.md** - Environment config, tech stack, project structure
4. **feature-flags.md** - Flagsmith feature flag usage patterns (dogfooding)
5. **forms.md** - Custom form patterns, InputGroup components, validation
6. **git-workflow.md** - Git workflow, branching, PR process
7. **patterns.md** - Common code patterns, API services, error handling, linting
8. **ui-patterns.md** - UI patterns, confirmation dialogs, modals

Which context would you like to explore?
5 changes: 5 additions & 0 deletions frontend/.claude/commands/feature-flag.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
description: Create a feature flag
---

1. Create a feature flag using the context defined in `.claude/context/feature-flags.md`
22 changes: 22 additions & 0 deletions frontend/.claude/commands/form.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
---
description: Create a new form component
---

**Note**: This codebase does NOT use Formik or Yup.

Create a form following the standard pattern:

1. Use React class component or functional component with `useState`
2. Use `InputGroup` component from global scope with `title`, `value`, `onChange`
3. For RTK Query mutations, use `useCreateXMutation()` hooks
4. Handle loading and error states
5. Use `Utils.preventDefault(e)` in submit handler
6. Use `toast()` for success/error messages
7. Use `closeModal()` to dismiss modal forms

Examples to reference:
- `web/components/SamlForm.js` - Class component form
- `web/components/modals/CreateSegmentRulesTabForm.tsx` - Functional component form
- Search for `InputGroup` usage in `/web/components/` for more examples

Context file: `.claude/context/forms.md`
185 changes: 185 additions & 0 deletions frontend/.claude/context/api-integration.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
# API Integration Guide

## Workflow

### Preferred: Manual Service Creation

The `npx ssg` CLI requires interactive input that cannot be automated. Instead, **manually create RTK Query services** following the patterns in existing service files.

1. **Check backend code** in `../api` for endpoint details
- Search backend directly using Grep or Glob tools
- Common locations: `*/views.py`, `*/urls.py`, `*/serializers.py`
- Check API documentation or Swagger UI if available in your environment

2. **Define types** in `common/types/requests.ts` and `responses.ts`
- Add to `Req` type for request parameters
- Add to `Res` type for response data
- Match backend serializer field names and types

3. **Create service file** in `common/services/use{Entity}.ts`
- Follow pattern from existing services (e.g., `usePartner.ts`, `useCompany.ts`)
- Use `service.enhanceEndpoints()` and `.injectEndpoints()`
- Define queries with `builder.query<Res['entity'], Req['getEntity']>()`
- Define mutations with `builder.mutation<Res['entity'], Req['createEntity']>()`
- Set appropriate `providesTags` and `invalidatesTags` for cache management
- Export hooks: `useGetEntityQuery`, `useCreateEntityMutation`, etc.

4. **CRITICAL: Update `.claude/api-type-map.json`** to register the new endpoint
- Add entry in `request_types` section with:
- `type`: TypeScript type signature (e.g., `{id: string, name: string}`)
- `serializer`: Backend serializer path (e.g., `entities/serializers.py:EntitySerializer`)
- `endpoint`: API endpoint pattern (e.g., `/api/v1/entities/{id}/`)
- `method`: HTTP method (GET, POST, PUT, DELETE)
- `service`: Frontend service file path (e.g., `common/services/useEntity.ts`)
- Add entry in `response_types` section (if needed)
- This enables the `/api-types-sync` command to track type changes

5. **Verify implementation**
- Check URL matches backend endpoint exactly
- Verify HTTP method (GET, POST, PUT, DELETE)
- Ensure request body structure matches backend serializer
- Test with actual API calls

### Example Service Structure

```typescript
import { service } from 'common/service'
import { Req } from 'common/types/requests'
import { Res } from 'common/types/responses'

export const entityService = service
.enhanceEndpoints({ addTagTypes: ['Entity'] })
.injectEndpoints({
endpoints: (builder) => ({
getEntity: builder.query<Res['entity'], Req['getEntity']>({
providesTags: (res) => [{ id: res?.id, type: 'Entity' }],
query: (query) => ({
url: `entities/${query.id}`,
}),
}),
updateEntity: builder.mutation<Res['entity'], Req['updateEntity']>({
invalidatesTags: (res) => [
{ id: 'LIST', type: 'Entity' },
{ id: res?.id, type: 'Entity' },
],
query: (query) => ({
body: query,
method: 'PUT',
url: `entities/${query.id}`,
}),
}),
// END OF ENDPOINTS
}),
})

export const {
useGetEntityQuery,
useUpdateEntityMutation,
// END OF EXPORTS
} = entityService
```

## Finding Backend Endpoints

### Search Strategy

1. **Search backend directly**: Use Grep/Glob tools to search the `../api` directory
2. **Check URL patterns**: Look in `../api/*/urls.py`
3. **Check ViewSets**: Look in `../api/*/views.py`
4. **Common file download pattern**:
- Backend returns PDF/file with `Content-Disposition: attachment; filename=...`
- Use `responseHandler` in RTK Query to handle blob downloads
- Check existing service files for examples

### File Download Pattern

**Use the reusable utility function:**

```typescript
import { handleFileDownload } from 'common/utils/fileDownload'

query: (query) => ({
url: `resource/${query.id}/pdf`,
responseHandler: (response) => handleFileDownload(response, 'invoice.pdf'),
})
```

The utility automatically:
- Extracts filename from `Content-Disposition` header
- Creates and triggers download
- Cleans up blob URLs
- Returns `{ data: { url } }` format

## State Management

- **Redux Toolkit + RTK Query** for all API calls
- Store: `common/store.ts` with redux-persist
- Base service: `common/service.ts`
- **Use `npx ssg` CLI to generate new services** (optional but helpful)
- **IMPORTANT**: When implementing API logic, prefer implementing it in the RTK Query service layer (using `transformResponse`, `transformErrorResponse`, etc.) rather than in components. This makes the logic reusable across the application.

## Error Handling Patterns

### RTK Query Mutations

```typescript
const [createMail, { isLoading, error }] = useCreateMailMutation()

const handleSubmit = async () => {
try {
const result = await createMail(data).unwrap()
toast.success('Success!')
} catch (err) {
if ('status' in err) {
// FetchBaseQueryError - has status, data, error
const errMsg = 'error' in err ? err.error : JSON.stringify(err.data)
toast.error(errMsg)
} else {
// SerializedError - has message, code, name
toast.error(err.message || 'An error occurred')
}
}
}
```

### RTK Query Queries

```typescript
const { data, error, isLoading, refetch } = useGetMailQuery({ id: '123' })

// Display error in UI
if (error) {
return <ErrorMessage error={error} />
}

// Retry on error
const handleRetry = () => refetch()
```

### 401 Unauthorized Handling

**Automatic logout on 401** is handled in `common/service.ts`:
- Check the service.ts file for specific 401 handling logic
- Typically debounced to prevent multiple logout calls

### Backend Error Response Format

Backend typically returns:
```json
{
"detail": "Error message here",
"code": "ERROR_CODE"
}
```

Access in error handling:
```typescript
if ('data' in err && err.data?.detail) {
toast.error(err.data.detail)
}
```

## Platform Patterns

- Web and common code are separated in the directory structure
- Check existing patterns in the codebase for platform-specific implementations
Loading