mirror of
https://github.com/Significant-Gravitas/AutoGPT.git
synced 2026-01-08 22:58:01 -05:00
docs(frontend): contributing guidelines (#11210)
## Changes 🏗️ Document how to contribute on the Front-end so it is easier for non-regular contributors. ## Checklist 📋 ### For code changes: - [x] I have clearly listed my changes in the PR description - [x] I have made a test plan - [x] I have tested my changes according to the test plan: - [x] Contribution guidelines make sense and look good considering the AutoGPT stack ### For configuration changes: None
This commit is contained in:
94
.github/copilot-instructions.md
vendored
94
.github/copilot-instructions.md
vendored
@@ -12,6 +12,7 @@ This file provides comprehensive onboarding information for GitHub Copilot codin
|
||||
- **Infrastructure** - Docker configurations, CI/CD, and development tools
|
||||
|
||||
**Primary Languages & Frameworks:**
|
||||
|
||||
- **Backend**: Python 3.10-3.13, FastAPI, Prisma ORM, PostgreSQL, RabbitMQ
|
||||
- **Frontend**: TypeScript, Next.js 15, React, Tailwind CSS, Radix UI
|
||||
- **Development**: Docker, Poetry, pnpm, Playwright, Storybook
|
||||
@@ -23,15 +24,17 @@ This file provides comprehensive onboarding information for GitHub Copilot codin
|
||||
**Always run these commands in the correct directory and in this order:**
|
||||
|
||||
1. **Initial Setup** (required once):
|
||||
|
||||
```bash
|
||||
# Clone and enter repository
|
||||
git clone <repo> && cd AutoGPT
|
||||
|
||||
|
||||
# Start all services (database, redis, rabbitmq, clamav)
|
||||
cd autogpt_platform && docker compose --profile local up deps --build --detach
|
||||
```
|
||||
|
||||
2. **Backend Setup** (always run before backend development):
|
||||
|
||||
```bash
|
||||
cd autogpt_platform/backend
|
||||
poetry install # Install dependencies
|
||||
@@ -48,6 +51,7 @@ This file provides comprehensive onboarding information for GitHub Copilot codin
|
||||
### Runtime Requirements
|
||||
|
||||
**Critical:** Always ensure Docker services are running before starting development:
|
||||
|
||||
```bash
|
||||
cd autogpt_platform && docker compose --profile local up deps --build --detach
|
||||
```
|
||||
@@ -58,6 +62,7 @@ cd autogpt_platform && docker compose --profile local up deps --build --detach
|
||||
### Development Commands
|
||||
|
||||
**Backend Development:**
|
||||
|
||||
```bash
|
||||
cd autogpt_platform/backend
|
||||
poetry run serve # Start development server (port 8000)
|
||||
@@ -68,6 +73,7 @@ poetry run lint # Lint code (ruff) - run after format
|
||||
```
|
||||
|
||||
**Frontend Development:**
|
||||
|
||||
```bash
|
||||
cd autogpt_platform/frontend
|
||||
pnpm dev # Start development server (port 3000) - use for active development
|
||||
@@ -81,23 +87,27 @@ pnpm storybook # Start component development server
|
||||
### Testing Strategy
|
||||
|
||||
**Backend Tests:**
|
||||
|
||||
- **Block Tests**: `poetry run pytest backend/blocks/test/test_block.py -xvs` (validates all blocks)
|
||||
- **Specific Block**: `poetry run pytest 'backend/blocks/test/test_block.py::test_available_blocks[BlockName]' -xvs`
|
||||
- **Snapshot Tests**: Use `--snapshot-update` when output changes, always review with `git diff`
|
||||
|
||||
**Frontend Tests:**
|
||||
|
||||
- **E2E Tests**: Always run `pnpm dev` before `pnpm test` (Playwright requires running instance)
|
||||
- **Component Tests**: Use Storybook for isolated component development
|
||||
|
||||
### Critical Validation Steps
|
||||
|
||||
**Before committing changes:**
|
||||
|
||||
1. Run `poetry run format` (backend) and `pnpm format` (frontend)
|
||||
2. Ensure all tests pass in modified areas
|
||||
3. Verify Docker services are still running
|
||||
4. Check that database migrations apply cleanly
|
||||
|
||||
**Common Issues & Workarounds:**
|
||||
|
||||
- **Prisma issues**: Run `poetry run prisma generate` after schema changes
|
||||
- **Permission errors**: Ensure Docker has proper permissions
|
||||
- **Port conflicts**: Check the `docker-compose.yml` file for the current list of exposed ports. You can list all mapped ports with:
|
||||
@@ -108,6 +118,7 @@ pnpm storybook # Start component development server
|
||||
### Core Architecture
|
||||
|
||||
**AutoGPT Platform** (`autogpt_platform/`):
|
||||
|
||||
- `backend/` - FastAPI server with async support
|
||||
- `backend/backend/` - Core API logic
|
||||
- `backend/blocks/` - Agent execution blocks
|
||||
@@ -121,6 +132,7 @@ pnpm storybook # Start component development server
|
||||
- `docker-compose.yml` - Development stack orchestration
|
||||
|
||||
**Key Configuration Files:**
|
||||
|
||||
- `pyproject.toml` - Python dependencies and tooling
|
||||
- `package.json` - Node.js dependencies and scripts
|
||||
- `schema.prisma` - Database schema and migrations
|
||||
@@ -136,6 +148,7 @@ pnpm storybook # Start component development server
|
||||
### Development Workflow
|
||||
|
||||
**GitHub Actions**: Multiple CI/CD workflows in `.github/workflows/`
|
||||
|
||||
- `platform-backend-ci.yml` - Backend testing and validation
|
||||
- `platform-frontend-ci.yml` - Frontend testing and validation
|
||||
- `platform-fullstack-ci.yml` - End-to-end integration tests
|
||||
@@ -146,11 +159,13 @@ pnpm storybook # Start component development server
|
||||
### Key Source Files
|
||||
|
||||
**Backend Entry Points:**
|
||||
|
||||
- `backend/backend/server/server.py` - FastAPI application setup
|
||||
- `backend/backend/data/` - Database models and user management
|
||||
- `backend/blocks/` - Agent execution blocks and logic
|
||||
|
||||
**Frontend Entry Points:**
|
||||
|
||||
- `frontend/src/app/layout.tsx` - Root application layout
|
||||
- `frontend/src/app/page.tsx` - Home page
|
||||
- `frontend/src/lib/supabase/` - Authentication and database client
|
||||
@@ -160,6 +175,7 @@ pnpm storybook # Start component development server
|
||||
### Agent Block System
|
||||
|
||||
Agents are built using a visual block-based system where each block performs a single action. Blocks are defined in `backend/blocks/` and must include:
|
||||
|
||||
- Block definition with input/output schemas
|
||||
- Execution logic with proper error handling
|
||||
- Tests validating functionality
|
||||
@@ -167,6 +183,7 @@ Agents are built using a visual block-based system where each block performs a s
|
||||
### Database & ORM
|
||||
|
||||
**Prisma ORM** with PostgreSQL backend including pgvector for embeddings:
|
||||
|
||||
- Schema in `schema.prisma`
|
||||
- Migrations in `backend/migrations/`
|
||||
- Always run `prisma migrate dev` and `prisma generate` after schema changes
|
||||
@@ -174,13 +191,15 @@ Agents are built using a visual block-based system where each block performs a s
|
||||
## Environment Configuration
|
||||
|
||||
### Configuration Files Priority Order
|
||||
|
||||
1. **Backend**: `/backend/.env.default` → `/backend/.env` (user overrides)
|
||||
2. **Frontend**: `/frontend/.env.default` → `/frontend/.env` (user overrides)
|
||||
2. **Frontend**: `/frontend/.env.default` → `/frontend/.env` (user overrides)
|
||||
3. **Platform**: `/.env.default` (Supabase/shared) → `/.env` (user overrides)
|
||||
4. Docker Compose `environment:` sections override file-based config
|
||||
5. Shell environment variables have highest precedence
|
||||
|
||||
### Docker Environment Setup
|
||||
|
||||
- All services use hardcoded defaults (no `${VARIABLE}` substitutions)
|
||||
- The `env_file` directive loads variables INTO containers at runtime
|
||||
- Backend/Frontend services use YAML anchors for consistent configuration
|
||||
@@ -189,6 +208,7 @@ Agents are built using a visual block-based system where each block performs a s
|
||||
## Advanced Development Patterns
|
||||
|
||||
### Adding New Blocks
|
||||
|
||||
1. Create file in `/backend/backend/blocks/`
|
||||
2. Inherit from `Block` base class with input/output schemas
|
||||
3. Implement `run` method with proper error handling
|
||||
@@ -198,6 +218,7 @@ Agents are built using a visual block-based system where each block performs a s
|
||||
7. Consider how inputs/outputs connect with other blocks in graph editor
|
||||
|
||||
### API Development
|
||||
|
||||
1. Update routes in `/backend/backend/server/routers/`
|
||||
2. Add/update Pydantic models in same directory
|
||||
3. Write tests alongside route files
|
||||
@@ -205,21 +226,76 @@ Agents are built using a visual block-based system where each block performs a s
|
||||
5. Run `poetry run test` to verify changes
|
||||
|
||||
### Frontend Development
|
||||
1. Components in `/frontend/src/components/`
|
||||
2. Use existing UI components from `/frontend/src/components/ui/`
|
||||
3. Add Storybook stories for component development
|
||||
4. Test user-facing features with Playwright E2E tests
|
||||
5. Update protected routes in middleware when needed
|
||||
|
||||
**📖 Complete Frontend Guide**: See `autogpt_platform/frontend/CONTRIBUTING.md` and `autogpt_platform/frontend/.cursorrules` for comprehensive patterns and conventions.
|
||||
|
||||
**Quick Reference:**
|
||||
|
||||
**Component Structure:**
|
||||
|
||||
- Separate render logic from data/behavior
|
||||
- Structure: `ComponentName/ComponentName.tsx` + `useComponentName.ts` + `helpers.ts`
|
||||
- Exception: Small components (3-4 lines of logic) can be inline
|
||||
- Render-only components can be direct files without folders
|
||||
|
||||
**Data Fetching:**
|
||||
|
||||
- Use generated API hooks from `@/app/api/__generated__/endpoints/`
|
||||
- Generated via Orval from backend OpenAPI spec
|
||||
- Pattern: `use{Method}{Version}{OperationName}`
|
||||
- Example: `useGetV2ListLibraryAgents`
|
||||
- Regenerate with: `pnpm generate:api`
|
||||
- **Never** use deprecated `BackendAPI` or `src/lib/autogpt-server-api/*`
|
||||
|
||||
**Code Conventions:**
|
||||
|
||||
- Use function declarations for components and handlers (not arrow functions)
|
||||
- Only arrow functions for small inline lambdas (map, filter, etc.)
|
||||
- Components: `PascalCase`, Hooks: `camelCase` with `use` prefix
|
||||
- No barrel files or `index.ts` re-exports
|
||||
- Minimal comments (code should be self-documenting)
|
||||
|
||||
**Styling:**
|
||||
|
||||
- Use Tailwind CSS utilities only
|
||||
- Use design system components from `src/components/` (atoms, molecules, organisms)
|
||||
- Never use `src/components/__legacy__/*`
|
||||
- Only use Phosphor Icons (`@phosphor-icons/react`)
|
||||
- Prefer design tokens over hardcoded values
|
||||
|
||||
**Error Handling:**
|
||||
|
||||
- Render errors: Use `<ErrorCard />` component
|
||||
- Mutation errors: Display with toast notifications
|
||||
- Manual exceptions: Use `Sentry.captureException()`
|
||||
- Global error boundaries already configured
|
||||
|
||||
**Testing:**
|
||||
|
||||
- Add/update Storybook stories for UI components (`pnpm storybook`)
|
||||
- Run Playwright E2E tests with `pnpm test`
|
||||
- Verify in Chromatic after PR
|
||||
|
||||
**Architecture:**
|
||||
|
||||
- Default to client components ("use client")
|
||||
- Server components only for SEO or extreme TTFB needs
|
||||
- Use React Query for server state (via generated hooks)
|
||||
- Co-locate UI state in components/hooks
|
||||
|
||||
### Security Guidelines
|
||||
|
||||
**Cache Protection Middleware** (`/backend/backend/server/middleware/security.py`):
|
||||
|
||||
- Default: Disables caching for ALL endpoints with `Cache-Control: no-store, no-cache, must-revalidate, private`
|
||||
- Uses allow list approach for cacheable paths (static assets, health checks, public pages)
|
||||
- Prevents sensitive data caching in browsers/proxies
|
||||
- Add new cacheable endpoints to `CACHEABLE_PATHS`
|
||||
|
||||
### CI/CD Alignment
|
||||
|
||||
The repository has comprehensive CI workflows that test:
|
||||
|
||||
- **Backend**: Python 3.11-3.13, services (Redis/RabbitMQ/ClamAV), Prisma migrations, Poetry lock validation
|
||||
- **Frontend**: Node.js 21, pnpm, Playwright with Docker Compose stack, API schema validation
|
||||
- **Integration**: Full-stack type checking and E2E testing
|
||||
@@ -229,6 +305,7 @@ Match these patterns when developing locally - the copilot setup environment mir
|
||||
## Collaboration with Other AI Assistants
|
||||
|
||||
This repository is actively developed with assistance from Claude (via CLAUDE.md files). When working on this codebase:
|
||||
|
||||
- Check for existing CLAUDE.md files that provide additional context
|
||||
- Follow established patterns and conventions already in the codebase
|
||||
- Maintain consistency with existing code style and architecture
|
||||
@@ -237,8 +314,9 @@ This repository is actively developed with assistance from Claude (via CLAUDE.md
|
||||
## Trust These Instructions
|
||||
|
||||
These instructions are comprehensive and tested. Only perform additional searches if:
|
||||
|
||||
1. Information here is incomplete for your specific task
|
||||
2. You encounter errors not covered by the workarounds
|
||||
3. You need to understand implementation details not covered above
|
||||
|
||||
For detailed platform development patterns, refer to `autogpt_platform/CLAUDE.md` and `AGENTS.md` in the repository root.
|
||||
For detailed platform development patterns, refer to `autogpt_platform/CLAUDE.md` and `AGENTS.md` in the repository root.
|
||||
|
||||
@@ -63,6 +63,9 @@ poetry run pytest path/to/test.py --snapshot-update
|
||||
# Install dependencies
|
||||
cd frontend && pnpm i
|
||||
|
||||
# Generate API client from OpenAPI spec
|
||||
pnpm generate:api
|
||||
|
||||
# Start development server
|
||||
pnpm dev
|
||||
|
||||
@@ -75,12 +78,23 @@ pnpm storybook
|
||||
# Build production
|
||||
pnpm build
|
||||
|
||||
# Format and lint
|
||||
pnpm format
|
||||
|
||||
# Type checking
|
||||
pnpm types
|
||||
```
|
||||
|
||||
We have a components library in autogpt_platform/frontend/src/components/atoms that should be used when adding new pages and components.
|
||||
**📖 Complete Guide**: See `/frontend/CONTRIBUTING.md` and `/frontend/.cursorrules` for comprehensive frontend patterns.
|
||||
|
||||
**Key Frontend Conventions:**
|
||||
|
||||
- Separate render logic from data/behavior in components
|
||||
- Use generated API hooks from `@/app/api/__generated__/endpoints/`
|
||||
- Use function declarations (not arrow functions) for components/handlers
|
||||
- Use design system components from `src/components/` (atoms, molecules, organisms)
|
||||
- Only use Phosphor Icons
|
||||
- Never use `src/components/__legacy__/*` or deprecated `BackendAPI`
|
||||
|
||||
## Architecture Overview
|
||||
|
||||
@@ -95,11 +109,16 @@ We have a components library in autogpt_platform/frontend/src/components/atoms t
|
||||
|
||||
### Frontend Architecture
|
||||
|
||||
- **Framework**: Next.js App Router with React Server Components
|
||||
- **State Management**: React hooks + Supabase client for real-time updates
|
||||
- **Framework**: Next.js 15 App Router (client-first approach)
|
||||
- **Data Fetching**: Type-safe generated API hooks via Orval + React Query
|
||||
- **State Management**: React Query for server state, co-located UI state in components/hooks
|
||||
- **Component Structure**: Separate render logic (`.tsx`) from business logic (`use*.ts` hooks)
|
||||
- **Workflow Builder**: Visual graph editor using @xyflow/react
|
||||
- **UI Components**: Radix UI primitives with Tailwind CSS styling
|
||||
- **UI Components**: shadcn/ui (Radix UI primitives) with Tailwind CSS styling
|
||||
- **Icons**: Phosphor Icons only
|
||||
- **Feature Flags**: LaunchDarkly integration
|
||||
- **Error Handling**: ErrorCard for render errors, toast for mutations, Sentry for exceptions
|
||||
- **Testing**: Playwright for E2E, Storybook for component development
|
||||
|
||||
### Key Concepts
|
||||
|
||||
@@ -153,6 +172,7 @@ Key models (defined in `/backend/schema.prisma`):
|
||||
**Adding a new block:**
|
||||
|
||||
Follow the comprehensive [Block SDK Guide](../../../docs/content/platform/block-sdk-guide.md) which covers:
|
||||
|
||||
- Provider configuration with `ProviderBuilder`
|
||||
- Block schema definition
|
||||
- Authentication (API keys, OAuth, webhooks)
|
||||
@@ -160,6 +180,7 @@ Follow the comprehensive [Block SDK Guide](../../../docs/content/platform/block-
|
||||
- File organization
|
||||
|
||||
Quick steps:
|
||||
|
||||
1. Create new file in `/backend/backend/blocks/`
|
||||
2. Configure provider using `ProviderBuilder` in `_config.py`
|
||||
3. Inherit from `Block` base class
|
||||
@@ -180,10 +201,20 @@ ex: do the inputs and outputs tie well together?
|
||||
|
||||
**Frontend feature development:**
|
||||
|
||||
1. Components go in `/frontend/src/components/`
|
||||
2. Use existing UI components from `/frontend/src/components/ui/`
|
||||
3. Add Storybook stories for new components
|
||||
4. Test with Playwright if user-facing
|
||||
See `/frontend/CONTRIBUTING.md` for complete patterns. Quick reference:
|
||||
|
||||
1. **Pages**: Create in `src/app/(platform)/feature-name/page.tsx`
|
||||
- Add `usePageName.ts` hook for logic
|
||||
- Put sub-components in local `components/` folder
|
||||
2. **Components**: Structure as `ComponentName/ComponentName.tsx` + `useComponentName.ts` + `helpers.ts`
|
||||
- Use design system components from `src/components/` (atoms, molecules, organisms)
|
||||
- Never use `src/components/__legacy__/*`
|
||||
3. **Data fetching**: Use generated API hooks from `@/app/api/__generated__/endpoints/`
|
||||
- Regenerate with `pnpm generate:api`
|
||||
- Pattern: `use{Method}{Version}{OperationName}`
|
||||
4. **Styling**: Tailwind CSS only, use design tokens, Phosphor Icons only
|
||||
5. **Testing**: Add Storybook stories for new components, Playwright for E2E
|
||||
6. **Code conventions**: Function declarations (not arrow functions) for components/handlers
|
||||
|
||||
### Security Implementation
|
||||
|
||||
|
||||
765
autogpt_platform/frontend/CONTRIBUTING.md
Normal file
765
autogpt_platform/frontend/CONTRIBUTING.md
Normal file
@@ -0,0 +1,765 @@
|
||||
<div align="center">
|
||||
<h1>AutoGPT Frontend • Contributing ⌨️</h1>
|
||||
<p>Next.js App Router • Client-first • Type-safe generated API hooks • Tailwind + shadcn/ui</p>
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
## ☕️ Summary
|
||||
|
||||
This document is your reference for contributing to the AutoGPT Frontend. It adapts legacy guidelines to our current stack and practices.
|
||||
|
||||
- Architecture and stack
|
||||
- Component structure and design system
|
||||
- Data fetching (generated API hooks)
|
||||
- Feature flags
|
||||
- Naming and code conventions
|
||||
- Tooling, scripts, and testing
|
||||
- PR process and checklist
|
||||
|
||||
This is a living document. Open a pull request any time to improve it.
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Quick Start FAQ
|
||||
|
||||
New to the codebase? Here are shortcuts to common tasks:
|
||||
|
||||
### I need to make a new page
|
||||
|
||||
1. Create page in `src/app/(platform)/your-feature/page.tsx`
|
||||
2. If it has logic, create `usePage.ts` hook next to it
|
||||
3. Create sub-components in `components/` folder
|
||||
4. Use generated API hooks for data fetching
|
||||
5. If page needs auth, ensure it's in the `(platform)` route group
|
||||
|
||||
**Example structure:**
|
||||
|
||||
```
|
||||
app/(platform)/dashboard/
|
||||
page.tsx
|
||||
useDashboardPage.ts
|
||||
components/
|
||||
StatsPanel/
|
||||
StatsPanel.tsx
|
||||
useStatsPanel.ts
|
||||
```
|
||||
|
||||
See [Component structure](#-component-structure) and [Styling](#-styling) and [Data fetching patterns](#-data-fetching-patterns) sections.
|
||||
|
||||
### I need to update an existing component in a page
|
||||
|
||||
1. Find the page `src/app/(platform)/your-feature/page.tsx`
|
||||
2. Check its `components/` folder
|
||||
3. If needing to update its logic, check the `use[Component].ts` hook
|
||||
4. If the update is related to rendering, check `[Component].tsx` file
|
||||
|
||||
See [Component structure](#-component-structure) and [Styling](#-styling) sections.
|
||||
|
||||
### I need to make a new API call and show it on the UI
|
||||
|
||||
1. Ensure the backend endpoint exists in the OpenAPI spec
|
||||
2. Regenerate API client: `pnpm generate:api`
|
||||
3. Import the generated hook by typing the operation name (auto-import)
|
||||
4. Use the hook in your component/custom hook
|
||||
5. Handle loading, error, and success states
|
||||
|
||||
**Example:**
|
||||
|
||||
```tsx
|
||||
import { useGetV2ListLibraryAgents } from "@/app/api/__generated__/endpoints/library/library";
|
||||
|
||||
export function useAgentList() {
|
||||
const { data, isLoading, isError, error } = useGetV2ListLibraryAgents();
|
||||
|
||||
return {
|
||||
agents: data?.data || [],
|
||||
isLoading,
|
||||
isError,
|
||||
error,
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
See [Data fetching patterns](#-data-fetching-patterns) for more examples.
|
||||
|
||||
### I need to create a new component in the Design System
|
||||
|
||||
1. Determine the atomic level: atom, molecule, or organism
|
||||
2. Create folder: `src/components/[level]/ComponentName/`
|
||||
3. Create `ComponentName.tsx` (render logic)
|
||||
4. If logic exists, create `useComponentName.ts`
|
||||
5. Create `ComponentName.stories.tsx` for Storybook
|
||||
6. Use Tailwind + design tokens (avoid hardcoded values)
|
||||
7. Only use Phosphor icons
|
||||
8. Test in Storybook: `pnpm storybook`
|
||||
9. Verify in Chromatic after PR
|
||||
|
||||
**Example structure:**
|
||||
|
||||
```
|
||||
src/components/molecules/DataCard/
|
||||
DataCard.tsx
|
||||
DataCard.stories.tsx
|
||||
useDataCard.ts
|
||||
```
|
||||
|
||||
See [Component structure](#-component-structure) and [Styling](#-styling) sections.
|
||||
|
||||
---
|
||||
|
||||
## 📟 Contribution process
|
||||
|
||||
### 1) Branch off `dev`
|
||||
|
||||
- Branch from `dev` for features and fixes
|
||||
- Keep PRs focused (aim for one ticket per PR)
|
||||
- Use conventional commit messages with a scope (e.g., `feat(frontend): add X`)
|
||||
|
||||
### 2) Feature flags
|
||||
|
||||
If a feature will ship across multiple PRs, guard it with a flag so we can merge iteratively.
|
||||
|
||||
- Use [LaunchDarkly](https://www.launchdarkly.com) based flags (see Feature Flags below)
|
||||
- Avoid long-lived feature branches
|
||||
|
||||
### 3) Open PR and get reviews ✅
|
||||
|
||||
Before requesting review:
|
||||
|
||||
- [x] Code follows architecture and conventions here
|
||||
- [x] `pnpm format && pnpm lint && pnpm types` pass
|
||||
- [x] Relevant tests pass locally: `pnpm test` (and/or Storybook tests)
|
||||
- [x] If touching UI, validate against our design system and stories
|
||||
|
||||
### 4) Merge to `dev`
|
||||
|
||||
- Use squash merges
|
||||
- Follow conventional commit message format for the squash title
|
||||
|
||||
---
|
||||
|
||||
## 📂 Architecture & Stack
|
||||
|
||||
### Next.js App Router
|
||||
|
||||
- We use the [Next.js App Router](https://nextjs.org/docs/app) in `src/app`
|
||||
- Use [route segments](https://nextjs.org/docs/app/building-your-application/routing) with semantic URLs; no `pages/`
|
||||
|
||||
### Component good practices
|
||||
|
||||
- Default to client components
|
||||
- Use server components only when:
|
||||
- SEO requires server-rendered HTML, or
|
||||
- Extreme first-byte performance justifies it
|
||||
- If you render server-side data, prefer server-side prefetch + client hydration (see examples below and [React Query SSR & Hydration](https://tanstack.com/query/latest/docs/framework/react/guides/ssr))
|
||||
- Prefer using [Next.js API routes](https://nextjs.org/docs/pages/building-your-application/routing/api-routes) when possible over [server actions](https://nextjs.org/docs/14/app/building-your-application/data-fetching/server-actions-and-mutations)
|
||||
- Keep components small and simple
|
||||
- favour composition and splitting large components into smaller bits of UI
|
||||
- [colocate state](https://kentcdodds.com/blog/state-colocation-will-make-your-react-app-faster) when possible
|
||||
- keep render/side-effects split for [separation of concerns](https://en.wikipedia.org/wiki/Separation_of_concerns)
|
||||
- do not over-complicate or re-invent the wheel
|
||||
|
||||
**❓ Why a client-side first design vs server components/actions?**
|
||||
|
||||
While server components and actions are cool and cutting-edge, they introduce a layer of complexity which not always justified by the benefits they deliver. Defaulting to client-first keeps things simple in the mental model of the developer, specially for those developers less familiar with Next.js or heavy Front-end development.
|
||||
|
||||
### Data fetching: prefer generated API hooks
|
||||
|
||||
- We generate a type-safe client and React Query hooks from the backend OpenAPI spec via [Orval](https://orval.dev/)
|
||||
- Prefer the generated hooks under `src/app/api/__generated__/endpoints/...`
|
||||
- Treat `BackendAPI` and code under `src/lib/autogpt-server-api/*` as deprecated; do not introduce new usages
|
||||
- Use [Zod](https://zod.dev/) schemas from the generated client where applicable
|
||||
|
||||
### State management
|
||||
|
||||
- Prefer [React Query](https://tanstack.com/query/latest/docs/framework/react/overview) for server state, colocated near consumers (see [state colocation](https://kentcdodds.com/blog/state-colocation-will-make-your-react-app-faster))
|
||||
- Co-locate UI state inside components/hooks; keep global state minimal
|
||||
|
||||
### Styling and components
|
||||
|
||||
- [Tailwind CSS](https://tailwindcss.com/docs) + [shadcn/ui](https://ui.shadcn.com/) ([Radix Primitives](https://www.radix-ui.com/docs/primitives/overview/introduction) under the hood)
|
||||
- Use the design system under `src/components` for primitives and building blocks
|
||||
- Do not use anything under `src/components/_legacy__`; migrate away from it when touching old code
|
||||
- Reference the design system catalog on Chromatic: [`https://dev--670f94474adee5e32c896b98.chromatic.com/`](https://dev--670f94474adee5e32c896b98.chromatic.com/)
|
||||
- Use the [`tailwind-scrollbar`](https://www.npmjs.com/package/tailwind-scrollbar) plugin utilities for scrollbar styling
|
||||
|
||||
---
|
||||
|
||||
## 🧱 Component structure
|
||||
|
||||
For components, separate render logic from data/behavior, and keep implementation details local.
|
||||
|
||||
**Most components should follow this structure.** Pages are just bigger components made of smaller ones, and sub-components can have their own nested sub-components when dealing with complex features.
|
||||
|
||||
### Basic structure
|
||||
|
||||
When a component has non-trivial logic:
|
||||
|
||||
```
|
||||
FeatureX/
|
||||
FeatureX.tsx (render logic only)
|
||||
useFeatureX.ts (hook; data fetching, behavior, state)
|
||||
helpers.ts (pure helpers used by the hook)
|
||||
components/ (optional, subcomponents local to FeatureX)
|
||||
```
|
||||
|
||||
### Example: Page with nested components
|
||||
|
||||
```tsx
|
||||
// Page composition
|
||||
app/(platform)/dashboard/
|
||||
page.tsx
|
||||
useDashboardPage.ts
|
||||
components/ # (Sub-components the dashboard page is made of)
|
||||
StatsPanel/
|
||||
StatsPanel.tsx
|
||||
useStatsPanel.ts
|
||||
helpers.ts
|
||||
components/ # (Sub-components belonging to StatsPanel)
|
||||
StatCard/
|
||||
StatCard.tsx
|
||||
ActivityFeed/
|
||||
ActivityFeed.tsx
|
||||
useActivityFeed.ts
|
||||
```
|
||||
|
||||
### Guidelines
|
||||
|
||||
- Prefer function declarations for components and handlers
|
||||
- Only use arrow functions for small inline lambdas (e.g., in `map`)
|
||||
- Avoid barrel files and `index.ts` re-exports
|
||||
- Keep component files focused and readable; push complex logic to `helpers.ts`
|
||||
- Abstract reusable, cross-feature logic into `src/services/` or `src/lib/utils.ts` as appropriate
|
||||
- Build components encapsulated so they can be easily reused and abstracted elsewhere
|
||||
- Nest sub-components within a `components/` folder when they're local to the parent feature
|
||||
|
||||
### Exceptions
|
||||
|
||||
When to simplify the structure:
|
||||
|
||||
**Small hook logic (3-4 lines)**
|
||||
|
||||
If the hook logic is minimal, keep it inline with the render function:
|
||||
|
||||
```tsx
|
||||
export function ActivityAlert() {
|
||||
const [isVisible, setIsVisible] = useState(true);
|
||||
if (!isVisible) return null;
|
||||
|
||||
return (
|
||||
<Alert onClose={() => setIsVisible(false)}>New activity detected</Alert>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
**Render-only components**
|
||||
|
||||
Components with no hook logic can be direct files in `components/` without a folder:
|
||||
|
||||
```
|
||||
components/
|
||||
ActivityAlert.tsx (render-only, no folder needed)
|
||||
StatsPanel/ (has hook logic, needs folder)
|
||||
StatsPanel.tsx
|
||||
useStatsPanel.ts
|
||||
```
|
||||
|
||||
### Hook file structure
|
||||
|
||||
When separating logic into a custom hook:
|
||||
|
||||
```tsx
|
||||
// useStatsPanel.ts
|
||||
export function useStatsPanel() {
|
||||
const [data, setData] = useState<Stats[]>([]);
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
fetchStats().then(setData);
|
||||
}, []);
|
||||
|
||||
return {
|
||||
data,
|
||||
isLoading,
|
||||
refresh: () => fetchStats().then(setData),
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
Rules:
|
||||
|
||||
- **Always return an object** that exposes data and methods to the view
|
||||
- **Export a single function** named after the component (e.g., `useStatsPanel` for `StatsPanel.tsx`)
|
||||
- **Abstract into helpers.ts** when hook logic grows large, so the hook file remains readable by scanning without diving into implementation details
|
||||
|
||||
---
|
||||
|
||||
## 🔄 Data fetching patterns
|
||||
|
||||
All API hooks are generated from the backend OpenAPI specification using [Orval](https://orval.dev/). The hooks are type-safe and follow the operation names defined in the backend API.
|
||||
|
||||
### How to discover hooks
|
||||
|
||||
Most of the time you can rely on auto-import by typing the endpoint or operation name. Your IDE will suggest the generated hooks based on the OpenAPI operation IDs.
|
||||
|
||||
**Examples of hook naming patterns:**
|
||||
|
||||
- `GET /api/v1/notifications` → `useGetV1GetNotificationPreferences`
|
||||
- `POST /api/v2/store/agents` → `usePostV2CreateStoreAgent`
|
||||
- `DELETE /api/v2/store/submissions/{id}` → `useDeleteV2DeleteStoreSubmission`
|
||||
- `GET /api/v2/library/agents` → `useGetV2ListLibraryAgents`
|
||||
|
||||
**Pattern**: `use{Method}{Version}{OperationName}`
|
||||
|
||||
You can also explore the generated hooks by browsing `src/app/api/__generated__/endpoints/` which is organized by API tags (e.g., `auth`, `store`, `library`).
|
||||
|
||||
**OpenAPI specs:**
|
||||
|
||||
- Production: [https://backend.agpt.co/openapi.json](https://backend.agpt.co/openapi.json)
|
||||
- Staging: [https://dev-server.agpt.co/openapi.json](https://dev-server.agpt.co/openapi.json)
|
||||
|
||||
### Generated hooks (client)
|
||||
|
||||
Prefer the generated React Query hooks (via Orval + React Query):
|
||||
|
||||
```tsx
|
||||
import { useGetV1GetNotificationPreferences } from "@/app/api/__generated__/endpoints/auth/auth";
|
||||
|
||||
export function PreferencesPanel() {
|
||||
const { data, isLoading, isError } = useGetV1GetNotificationPreferences({
|
||||
query: {
|
||||
select: (res) => res.data,
|
||||
},
|
||||
});
|
||||
|
||||
if (isLoading) return null;
|
||||
if (isError) throw new Error("Failed to load preferences");
|
||||
return <pre>{JSON.stringify(data, null, 2)}</pre>;
|
||||
}
|
||||
```
|
||||
|
||||
### Generated mutations (client)
|
||||
|
||||
```tsx
|
||||
import { useQueryClient } from "@tanstack/react-query";
|
||||
import {
|
||||
useDeleteV2DeleteStoreSubmission,
|
||||
getGetV2ListMySubmissionsQueryKey,
|
||||
} from "@/app/api/__generated__/endpoints/store/store";
|
||||
|
||||
export function DeleteSubmissionButton({
|
||||
submissionId,
|
||||
}: {
|
||||
submissionId: string;
|
||||
}) {
|
||||
const queryClient = useQueryClient();
|
||||
const { mutateAsync: deleteSubmission, isPending } =
|
||||
useDeleteV2DeleteStoreSubmission({
|
||||
mutation: {
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: getGetV2ListMySubmissionsQueryKey(),
|
||||
});
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
async function onClick() {
|
||||
await deleteSubmission({ submissionId });
|
||||
}
|
||||
|
||||
return (
|
||||
<button disabled={isPending} onClick={onClick}>
|
||||
Delete
|
||||
</button>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### Server-side prefetch + client hydration
|
||||
|
||||
Use server-side prefetch to improve TTFB while keeping the component tree client-first (see [React Query SSR & Hydration](https://tanstack.com/query/latest/docs/framework/react/guides/ssr)):
|
||||
|
||||
```tsx
|
||||
// in a server component
|
||||
import { getQueryClient } from "@/lib/tanstack-query/getQueryClient";
|
||||
import { HydrationBoundary, dehydrate } from "@tanstack/react-query";
|
||||
import {
|
||||
prefetchGetV2ListStoreAgentsQuery,
|
||||
prefetchGetV2ListStoreCreatorsQuery,
|
||||
} from "@/app/api/__generated__/endpoints/store/store";
|
||||
|
||||
export default async function MarketplacePage() {
|
||||
const queryClient = getQueryClient();
|
||||
|
||||
await Promise.all([
|
||||
prefetchGetV2ListStoreAgentsQuery(queryClient, { featured: true }),
|
||||
prefetchGetV2ListStoreAgentsQuery(queryClient, { sorted_by: "runs" }),
|
||||
prefetchGetV2ListStoreCreatorsQuery(queryClient, {
|
||||
featured: true,
|
||||
sorted_by: "num_agents",
|
||||
}),
|
||||
]);
|
||||
|
||||
return (
|
||||
<HydrationBoundary state={dehydrate(queryClient)}>
|
||||
{/* Client component tree goes here */}
|
||||
</HydrationBoundary>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
Notes:
|
||||
|
||||
- Do not introduce new usages of `BackendAPI` or `src/lib/autogpt-server-api/*`
|
||||
- Keep transformations and mapping logic close to the consumer (hook), not in the view
|
||||
|
||||
---
|
||||
|
||||
## ⚠️ Error handling
|
||||
|
||||
The app has multiple error handling strategies depending on the type of error:
|
||||
|
||||
### Render/runtime errors
|
||||
|
||||
Use `<ErrorCard />` to display render or runtime errors gracefully:
|
||||
|
||||
```tsx
|
||||
import { ErrorCard } from "@/components/molecules/ErrorCard";
|
||||
|
||||
export function DataPanel() {
|
||||
const { data, isLoading, isError, error } = useGetData();
|
||||
|
||||
if (isLoading) return <Skeleton />;
|
||||
if (isError) return <ErrorCard error={error} />;
|
||||
|
||||
return <div>{data.content}</div>;
|
||||
}
|
||||
```
|
||||
|
||||
### API mutation errors
|
||||
|
||||
Display mutation errors using toast notifications:
|
||||
|
||||
```tsx
|
||||
import { useToast } from "@/components/ui/use-toast";
|
||||
|
||||
export function useUpdateSettings() {
|
||||
const { toast } = useToast();
|
||||
const { mutateAsync: updateSettings } = useUpdateSettingsMutation({
|
||||
mutation: {
|
||||
onError: (error) => {
|
||||
toast({
|
||||
title: "Failed to update settings",
|
||||
description: error.message,
|
||||
variant: "destructive",
|
||||
});
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
return { updateSettings };
|
||||
}
|
||||
```
|
||||
|
||||
### Manual Sentry capture
|
||||
|
||||
When needed, you can manually capture exceptions to Sentry:
|
||||
|
||||
```tsx
|
||||
import * as Sentry from "@sentry/nextjs";
|
||||
|
||||
try {
|
||||
await riskyOperation();
|
||||
} catch (error) {
|
||||
Sentry.captureException(error, {
|
||||
tags: { context: "feature-x" },
|
||||
extra: { metadata: additionalData },
|
||||
});
|
||||
throw error;
|
||||
}
|
||||
```
|
||||
|
||||
### Global error boundaries
|
||||
|
||||
The app has error boundaries already configured to:
|
||||
|
||||
- Capture uncaught errors globally and send them to Sentry
|
||||
- Display a user-friendly error UI when something breaks
|
||||
- Prevent the entire app from crashing
|
||||
|
||||
You don't need to wrap components in error boundaries manually unless you need custom error recovery logic.
|
||||
|
||||
---
|
||||
|
||||
## 🚩 Feature Flags
|
||||
|
||||
- Flags are powered by [LaunchDarkly](https://docs.launchdarkly.com/)
|
||||
- Use the helper APIs under `src/services/feature-flags`
|
||||
|
||||
Check a flag in a client component:
|
||||
|
||||
```tsx
|
||||
import { Flag, useGetFlag } from "@/services/feature-flags/use-get-flag";
|
||||
|
||||
export function AgentActivityPanel() {
|
||||
const enabled = useGetFlag(Flag.AGENT_ACTIVITY);
|
||||
if (!enabled) return null;
|
||||
return <div>Feature is enabled!</div>;
|
||||
}
|
||||
```
|
||||
|
||||
Protect a route or page component:
|
||||
|
||||
```tsx
|
||||
import { withFeatureFlag } from "@/services/feature-flags/with-feature-flag";
|
||||
|
||||
export const MyFeaturePage = withFeatureFlag(function Page() {
|
||||
return <div>My feature page</div>;
|
||||
}, "my-feature-flag");
|
||||
```
|
||||
|
||||
Local dev and Playwright:
|
||||
|
||||
- Set `NEXT_PUBLIC_PW_TEST=true` to use mocked flag values during local development and tests
|
||||
|
||||
Adding new flags:
|
||||
|
||||
1. Add the flag to the `Flag` enum and `FlagValues` type
|
||||
2. Provide a mock value in the mock map
|
||||
3. Configure the flag in LaunchDarkly
|
||||
|
||||
---
|
||||
|
||||
## 📙 Naming conventions
|
||||
|
||||
General:
|
||||
|
||||
- Variables and functions should read like plain English
|
||||
- Prefer `const` over `let` unless reassignment is required
|
||||
- Use searchable constants instead of magic numbers
|
||||
|
||||
Files:
|
||||
|
||||
- Components and hooks: `PascalCase` for component files, `camelCase` for hooks
|
||||
- Other files: `kebab-case`
|
||||
- Do not create barrel files or `index.ts` re-exports
|
||||
|
||||
Types:
|
||||
|
||||
- Prefer `interface` for object shapes
|
||||
- Component props should be `interface Props { ... }`
|
||||
- Use precise types; avoid `any` and unsafe casts
|
||||
|
||||
Parameters:
|
||||
|
||||
- If more than one parameter is needed, pass a single `Args` object for clarity
|
||||
|
||||
Comments:
|
||||
|
||||
- Keep comments minimal; code should be clear by itself
|
||||
- Only document non-obvious intent, invariants, or caveats
|
||||
|
||||
Functions:
|
||||
|
||||
- Prefer function declarations for components and handlers
|
||||
- Only use arrow functions for small inline callbacks
|
||||
|
||||
Control flow:
|
||||
|
||||
- Use early returns to reduce nesting
|
||||
- Avoid catching errors unless you handle them meaningfully
|
||||
|
||||
---
|
||||
|
||||
## 🎨 Styling
|
||||
|
||||
- Use Tailwind utilities; prefer semantic, composable class names
|
||||
- Use shadcn/ui components as building blocks when available
|
||||
- Use the `tailwind-scrollbar` utilities for scrollbar styling
|
||||
- Keep responsive and dark-mode behavior consistent with the design system
|
||||
|
||||
Additional requirements:
|
||||
|
||||
- Do not import shadcn primitives directly in feature code; only use components exposed in our design system under `src/components`. shadcn is a low-level skeleton we style on top of and is not meant to be consumed directly.
|
||||
- Prefer design tokens over Tailwind's default theme whenever possible (e.g., color, spacing, radius, and typography tokens). Avoid hardcoded values and default palette if a token exists.
|
||||
|
||||
---
|
||||
|
||||
## ⚠️ Errors and ⏳ Loading
|
||||
|
||||
- **Errors**: Use the `ErrorCard` component from the design system to display API/HTTP errors and retry actions. Keep error derivation/mapping in hooks; pass the final message to the component.
|
||||
- Component: `src/components/molecules/ErrorCard/ErrorCard.tsx`
|
||||
- **Loading**: Use the `Skeleton` component(s) from the design system for loading states. Favor domain-appropriate skeleton layouts (lists, cards, tables) over spinners.
|
||||
- See Storybook examples under Atoms/Skeleton for patterns.
|
||||
|
||||
---
|
||||
|
||||
## 🧭 Responsive and mobile-first
|
||||
|
||||
- Build mobile-first. Ensure new UI looks great from a 375px viewport width (iPhone SE) upwards.
|
||||
- Validate layouts at common breakpoints (375, 768, 1024, 1280). Prefer stacking and progressive disclosure on small screens.
|
||||
|
||||
---
|
||||
|
||||
## 🧰 State for complex flows
|
||||
|
||||
For components/flows with complex state, multi-step wizards, or cross-component coordination, prefer a small co-located store using [Zustand](https://github.com/pmndrs/zustand).
|
||||
|
||||
Guidelines:
|
||||
|
||||
- Co-locate the store with the feature (e.g., `FeatureX/store.ts`).
|
||||
- Expose typed selectors to minimize re-renders.
|
||||
- Keep effects and API calls in hooks; stores hold state and pure actions.
|
||||
|
||||
Example: simple store with selectors
|
||||
|
||||
```ts
|
||||
import { create } from "zustand";
|
||||
|
||||
interface WizardState {
|
||||
step: number;
|
||||
data: Record<string, unknown>;
|
||||
next(): void;
|
||||
back(): void;
|
||||
setField(args: { key: string; value: unknown }): void;
|
||||
}
|
||||
|
||||
export const useWizardStore = create<WizardState>((set) => ({
|
||||
step: 0,
|
||||
data: {},
|
||||
next() {
|
||||
set((state) => ({ step: state.step + 1 }));
|
||||
},
|
||||
back() {
|
||||
set((state) => ({ step: Math.max(0, state.step - 1) }));
|
||||
},
|
||||
setField({ key, value }) {
|
||||
set((state) => ({ data: { ...state.data, [key]: value } }));
|
||||
},
|
||||
}));
|
||||
|
||||
// Usage in a component (selectors keep updates scoped)
|
||||
function WizardFooter() {
|
||||
const step = useWizardStore((s) => s.step);
|
||||
const next = useWizardStore((s) => s.next);
|
||||
const back = useWizardStore((s) => s.back);
|
||||
|
||||
return (
|
||||
<div className="flex items-center gap-2">
|
||||
<button onClick={back} disabled={step === 0}>Back</button>
|
||||
<button onClick={next}>Next</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
Example: async action coordinated via hook + store
|
||||
|
||||
```ts
|
||||
// FeatureX/useFeatureX.ts
|
||||
import { useMutation } from "@tanstack/react-query";
|
||||
import { useWizardStore } from "./store";
|
||||
|
||||
export function useFeatureX() {
|
||||
const setField = useWizardStore((s) => s.setField);
|
||||
const next = useWizardStore((s) => s.next);
|
||||
|
||||
const { mutateAsync: save, isPending } = useMutation({
|
||||
mutationFn: async (payload: unknown) => {
|
||||
// call API here
|
||||
return payload;
|
||||
},
|
||||
onSuccess(data) {
|
||||
setField({ key: "result", value: data });
|
||||
next();
|
||||
},
|
||||
});
|
||||
|
||||
return { save, isSaving: isPending };
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🖼 Icons
|
||||
|
||||
- Only use Phosphor Icons. Treat all other icon libraries as deprecated for new code.
|
||||
- Package: `@phosphor-icons/react`
|
||||
- Site: [`https://phosphoricons.com/`](https://phosphoricons.com/)
|
||||
|
||||
Example usage:
|
||||
|
||||
```tsx
|
||||
import { Plus } from "@phosphor-icons/react";
|
||||
|
||||
export function CreateButton() {
|
||||
return (
|
||||
<button type="button" className="inline-flex items-center gap-2">
|
||||
<Plus size={16} />
|
||||
Create
|
||||
</button>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🧪 Testing & Storybook
|
||||
|
||||
- End-to-end: [Playwright](https://playwright.dev/docs/intro) (`pnpm test`, `pnpm test-ui`)
|
||||
- [Storybook](https://storybook.js.org/docs) for isolated UI development (`pnpm storybook` / `pnpm build-storybook`)
|
||||
- For Storybook tests in CI, see [`@storybook/test-runner`](https://storybook.js.org/docs/writing-tests/test-runner) (`test-storybook:ci`)
|
||||
- When changing components in `src/components`, update or add stories and visually verify in Storybook/Chromatic
|
||||
|
||||
---
|
||||
|
||||
## 🛠 Tooling & Scripts
|
||||
|
||||
Common scripts (see `package.json` for full list):
|
||||
|
||||
- `pnpm dev` — Start Next.js dev server (generates API client first)
|
||||
- `pnpm build` — Build for production
|
||||
- `pnpm start` — Start production server
|
||||
- `pnpm lint` — ESLint + Prettier check
|
||||
- `pnpm format` — Format code
|
||||
- `pnpm types` — Type-check
|
||||
- `pnpm storybook` — Run Storybook
|
||||
- `pnpm test` — Run Playwright tests
|
||||
|
||||
Generated API client:
|
||||
|
||||
- `pnpm generate:api` — Fetch OpenAPI spec and regenerate the client
|
||||
|
||||
---
|
||||
|
||||
## ✅ PR checklist (Frontend)
|
||||
|
||||
- Client-first: server components only for SEO or extreme TTFB needs
|
||||
- Uses generated API hooks; no new `BackendAPI` usages
|
||||
- UI uses `src/components` primitives; no new `_legacy__` components
|
||||
- Logic is separated into `use*.ts` and `helpers.ts` when non-trivial
|
||||
- Reusable logic extracted to `src/services/` or `src/lib/utils.ts` when appropriate
|
||||
- Navigation uses the Next.js router
|
||||
- Lint, format, type-check, and tests pass locally
|
||||
- Stories updated/added if UI changed; verified in Storybook
|
||||
|
||||
---
|
||||
|
||||
## ♻️ Migration guidance
|
||||
|
||||
When touching legacy code:
|
||||
|
||||
- Replace usages of `src/components/_legacy__/*` with the modern design system components under `src/components`
|
||||
- Replace `BackendAPI` or `src/lib/autogpt-server-api/*` with generated API hooks
|
||||
- Move presentational logic into render files and data/behavior into hooks
|
||||
- Keep one-off transformations in local `helpers.ts`; move reusable logic to `src/services/` or `src/lib/utils.ts`
|
||||
|
||||
---
|
||||
|
||||
## 📚 References
|
||||
|
||||
- Design system (Chromatic): [`https://dev--670f94474adee5e32c896b98.chromatic.com/`](https://dev--670f94474adee5e32c896b98.chromatic.com/)
|
||||
- Project README for setup and API client examples: `autogpt_platform/frontend/README.md`
|
||||
- Conventional Commits: [conventionalcommits.org](https://www.conventionalcommits.org/)
|
||||
@@ -4,20 +4,12 @@ This is the frontend for AutoGPT's next generation
|
||||
|
||||
This project uses [**pnpm**](https://pnpm.io/) as the package manager via **corepack**. [Corepack](https://github.com/nodejs/corepack) is a Node.js tool that automatically manages package managers without requiring global installations.
|
||||
|
||||
For architecture, conventions, data fetching, feature flags, design system usage, state management, and PR process, see [CONTRIBUTING.md](./CONTRIBUTING.md).
|
||||
|
||||
### Prerequisites
|
||||
|
||||
Make sure you have Node.js 16.10+ installed. Corepack is included with Node.js by default.
|
||||
|
||||
### ⚠️ Migrating from yarn
|
||||
|
||||
> This project was previously using yarn1, make sure to clean up the old files if you set it up previously with yarn:
|
||||
>
|
||||
> ```bash
|
||||
> rm -f yarn.lock && rm -rf node_modules
|
||||
> ```
|
||||
>
|
||||
> Then follow the setup steps below.
|
||||
|
||||
## Setup
|
||||
|
||||
### 1. **Enable corepack** (run this once on your system):
|
||||
@@ -96,184 +88,13 @@ Every time a new Front-end dependency is added by you or others, you will need t
|
||||
|
||||
This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font.
|
||||
|
||||
## 🔄 Data Fetching Strategy
|
||||
## 🔄 Data Fetching
|
||||
|
||||
> [!NOTE]
|
||||
> You don't need to run the OpenAPI commands below to run the Front-end. You will only need to run them when adding or modifying endpoints on the Backend API and wanting to use those on the Frontend.
|
||||
|
||||
This project uses an auto-generated API client powered by [**Orval**](https://orval.dev/), which creates type-safe API clients from OpenAPI specifications.
|
||||
|
||||
### How It Works
|
||||
|
||||
1. **Backend Requirements**: Each API endpoint needs a summary and tag in the OpenAPI spec
|
||||
2. **Operation ID Generation**: FastAPI generates operation IDs using the pattern `{method}{tag}{summary}`
|
||||
3. **Spec Fetching**: The OpenAPI spec is fetched from `http://localhost:8006/openapi.json` and saved to the frontend
|
||||
4. **Spec Transformation**: The OpenAPI spec is cleaned up using a custom transformer (see `autogpt_platform/frontend/src/app/api/transformers`)
|
||||
5. **Client Generation**: Auto-generated client includes TypeScript types, API endpoints, and Zod schemas, organized by tags
|
||||
|
||||
### API Client Commands
|
||||
|
||||
```bash
|
||||
# Fetch OpenAPI spec from backend and generate client
|
||||
pnpm generate:api
|
||||
|
||||
# Only fetch the OpenAPI spec
|
||||
pnpm fetch:openapi
|
||||
|
||||
# Only generate the client (after spec is fetched)
|
||||
pnpm generate:api-client
|
||||
```
|
||||
|
||||
### Using the Generated Client
|
||||
|
||||
The generated client provides React Query hooks for both queries and mutations:
|
||||
|
||||
#### Queries (GET requests)
|
||||
|
||||
```typescript
|
||||
import { useGetV1GetNotificationPreferences } from "@/app/api/__generated__/endpoints/auth/auth";
|
||||
|
||||
const { data, isLoading, isError } = useGetV1GetNotificationPreferences({
|
||||
query: {
|
||||
select: (res) => res.data,
|
||||
// Other React Query options
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
#### Mutations (POST, PUT, DELETE requests)
|
||||
|
||||
```typescript
|
||||
import { useDeleteV2DeleteStoreSubmission } from "@/app/api/__generated__/endpoints/store/store";
|
||||
import { getGetV2ListMySubmissionsQueryKey } from "@/app/api/__generated__/endpoints/store/store";
|
||||
import { useQueryClient } from "@tanstack/react-query";
|
||||
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
const { mutateAsync: deleteSubmission } = useDeleteV2DeleteStoreSubmission({
|
||||
mutation: {
|
||||
onSuccess: () => {
|
||||
// Invalidate related queries to refresh data
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: getGetV2ListMySubmissionsQueryKey(),
|
||||
});
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// Usage
|
||||
await deleteSubmission({
|
||||
submissionId: submission_id,
|
||||
});
|
||||
```
|
||||
|
||||
#### Server Actions
|
||||
|
||||
For server-side operations, you can also use the generated client functions directly:
|
||||
|
||||
```typescript
|
||||
import { postV1UpdateNotificationPreferences } from "@/app/api/__generated__/endpoints/auth/auth";
|
||||
|
||||
// In a server action
|
||||
const preferences = {
|
||||
email: "user@example.com",
|
||||
preferences: {
|
||||
AGENT_RUN: true,
|
||||
ZERO_BALANCE: false,
|
||||
// ... other preferences
|
||||
},
|
||||
daily_limit: 0,
|
||||
};
|
||||
|
||||
await postV1UpdateNotificationPreferences(preferences);
|
||||
```
|
||||
|
||||
#### Server-Side Prefetching
|
||||
|
||||
For server-side components, you can prefetch data on the server and hydrate it in the client cache. This allows immediate access to cached data when queries are called:
|
||||
|
||||
```typescript
|
||||
import { getQueryClient } from "@/lib/tanstack-query/getQueryClient";
|
||||
import {
|
||||
prefetchGetV2ListStoreAgentsQuery,
|
||||
prefetchGetV2ListStoreCreatorsQuery
|
||||
} from "@/app/api/__generated__/endpoints/store/store";
|
||||
import { HydrationBoundary, dehydrate } from "@tanstack/react-query";
|
||||
|
||||
// In your server component
|
||||
const queryClient = getQueryClient();
|
||||
|
||||
await Promise.all([
|
||||
prefetchGetV2ListStoreAgentsQuery(queryClient, {
|
||||
featured: true,
|
||||
}),
|
||||
prefetchGetV2ListStoreAgentsQuery(queryClient, {
|
||||
sorted_by: "runs",
|
||||
}),
|
||||
prefetchGetV2ListStoreCreatorsQuery(queryClient, {
|
||||
featured: true,
|
||||
sorted_by: "num_agents",
|
||||
}),
|
||||
]);
|
||||
|
||||
return (
|
||||
<HydrationBoundary state={dehydrate(queryClient)}>
|
||||
<MainMarkeplacePage />
|
||||
</HydrationBoundary>
|
||||
);
|
||||
```
|
||||
|
||||
This pattern improves performance by serving pre-fetched data from the server while maintaining the benefits of client-side React Query features.
|
||||
|
||||
### Configuration
|
||||
|
||||
The Orval configuration is located in `autogpt_platform/frontend/orval.config.ts`. It generates two separate clients:
|
||||
|
||||
1. **autogpt_api_client**: React Query hooks for client-side data fetching
|
||||
2. **autogpt_zod_schema**: Zod schemas for validation
|
||||
|
||||
For more details, see the [Orval documentation](https://orval.dev/) or check the configuration file.
|
||||
See [CONTRIBUTING.md](./CONTRIBUTING.md) for guidance on generated API hooks, SSR + hydration patterns, and usage examples. You generally do not need to run OpenAPI commands unless adding/modifying backend endpoints.
|
||||
|
||||
## 🚩 Feature Flags
|
||||
|
||||
This project uses [LaunchDarkly](https://launchdarkly.com/) for feature flags, allowing us to control feature rollouts and A/B testing.
|
||||
|
||||
### Using Feature Flags
|
||||
|
||||
#### Check if a feature is enabled
|
||||
|
||||
```typescript
|
||||
import { Flag, useGetFlag } from "@/services/feature-flags/use-get-flag";
|
||||
|
||||
function MyComponent() {
|
||||
const isAgentActivityEnabled = useGetFlag(Flag.AGENT_ACTIVITY);
|
||||
|
||||
if (!isAgentActivityEnabled) {
|
||||
return null; // Hide feature
|
||||
}
|
||||
|
||||
return <div>Feature is enabled!</div>;
|
||||
}
|
||||
```
|
||||
|
||||
#### Protect entire components
|
||||
|
||||
```typescript
|
||||
import { withFeatureFlag } from "@/services/feature-flags/with-feature-flag";
|
||||
|
||||
const MyFeaturePage = withFeatureFlag(MyPageComponent, "my-feature-flag");
|
||||
```
|
||||
|
||||
### Testing with Feature Flags
|
||||
|
||||
For local development or running Playwright tests locally, use mocked feature flags by setting `NEXT_PUBLIC_PW_TEST=true` in your `.env` file. This bypasses LaunchDarkly and uses the mock values defined in the code.
|
||||
|
||||
### Adding New Flags
|
||||
|
||||
1. Add the flag to the `Flag` enum in `use-get-flag.ts`
|
||||
2. Add the flag type to `FlagValues` type
|
||||
3. Add mock value to `mockFlags` for testing
|
||||
4. Configure the flag in LaunchDarkly dashboard
|
||||
See [CONTRIBUTING.md](./CONTRIBUTING.md) for feature flag usage patterns, local development with mocks, and how to add new flags.
|
||||
|
||||
## 🚚 Deploy
|
||||
|
||||
@@ -333,7 +154,7 @@ By integrating Storybook into our development workflow, we can streamline UI dev
|
||||
- [**Tailwind CSS**](https://tailwindcss.com/) - Utility-first CSS framework
|
||||
- [**shadcn/ui**](https://ui.shadcn.com/) - Re-usable components built with Radix UI and Tailwind CSS
|
||||
- [**Radix UI**](https://www.radix-ui.com/) - Headless UI components for accessibility
|
||||
- [**Lucide React**](https://lucide.dev/guide/packages/lucide-react) - Beautiful & consistent icons
|
||||
- [**Phosphor Icons**](https://phosphoricons.com/) - Icon set used across the app
|
||||
- [**Framer Motion**](https://motion.dev/) - Animation library for React
|
||||
|
||||
### Development & Testing
|
||||
|
||||
Reference in New Issue
Block a user