From b7646f3e5881f86a165ccb73b0ca0b69d6eaf0f8 Mon Sep 17 00:00:00 2001 From: Ubbe Date: Tue, 21 Oct 2025 12:26:51 +0400 Subject: [PATCH] docs(frontend): contributing guidelines (#11210) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## 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 --- .github/copilot-instructions.md | 94 ++- autogpt_platform/CLAUDE.md | 47 +- autogpt_platform/frontend/CONTRIBUTING.md | 765 ++++++++++++++++++++++ autogpt_platform/frontend/README.md | 191 +----- 4 files changed, 896 insertions(+), 201 deletions(-) create mode 100644 autogpt_platform/frontend/CONTRIBUTING.md diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index a0834a6913..870e6b4b0a 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -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 && 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 `` 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. \ No newline at end of file +For detailed platform development patterns, refer to `autogpt_platform/CLAUDE.md` and `AGENTS.md` in the repository root. diff --git a/autogpt_platform/CLAUDE.md b/autogpt_platform/CLAUDE.md index 3b8eaba0a1..b5810319a8 100644 --- a/autogpt_platform/CLAUDE.md +++ b/autogpt_platform/CLAUDE.md @@ -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 diff --git a/autogpt_platform/frontend/CONTRIBUTING.md b/autogpt_platform/frontend/CONTRIBUTING.md new file mode 100644 index 0000000000..048c088350 --- /dev/null +++ b/autogpt_platform/frontend/CONTRIBUTING.md @@ -0,0 +1,765 @@ +
+

AutoGPT Frontend โ€ข Contributing โŒจ๏ธ

+

Next.js App Router โ€ข Client-first โ€ข Type-safe generated API hooks โ€ข Tailwind + shadcn/ui

+
+ +--- + +## โ˜•๏ธ 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 ( + setIsVisible(false)}>New activity detected + ); +} +``` + +**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([]); + 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
{JSON.stringify(data, null, 2)}
; +} +``` + +### 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 ( + + ); +} +``` + +### 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 ( + + {/* Client component tree goes here */} + + ); +} +``` + +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 `` 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 ; + if (isError) return ; + + return
{data.content}
; +} +``` + +### 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
Feature is enabled!
; +} +``` + +Protect a route or page component: + +```tsx +import { withFeatureFlag } from "@/services/feature-flags/with-feature-flag"; + +export const MyFeaturePage = withFeatureFlag(function Page() { + return
My feature page
; +}, "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; + next(): void; + back(): void; + setField(args: { key: string; value: unknown }): void; +} + +export const useWizardStore = create((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 ( +
+ + +
+ ); +} +``` + +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 ( + + ); +} +``` + +--- + +## ๐Ÿงช 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/) diff --git a/autogpt_platform/frontend/README.md b/autogpt_platform/frontend/README.md index ebc6764455..f4541cdd33 100644 --- a/autogpt_platform/frontend/README.md +++ b/autogpt_platform/frontend/README.md @@ -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 ( - - - -); -``` - -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
Feature is enabled!
; -} -``` - -#### 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