## Skills

Task-specific guides are in `.agents/skills/`. Each skill has a `SKILL.md` with instructions.

**For tools that don't auto-load `.agents/`**, create symlinks to your tool's config directory:

For example, if you are using Claude Code, you can create a symlink to the `.agents/skills` directory in the `.claude/skills` directory.

---

## Project overview

- Name: **TiDB Cloud UI** (monorepo-root: `tidbcloud/dbaas-ui`).
- Frontend app built with **React 18** + **TypeScript**.
- Build tool: **Rsbuild**.
- Primary responsibility: Dashboard UI that talks to several backend services via OpenAPI-generated clients.

---

## Tech Stack

- **Core**: React 18, TypeScript
- **Build**: Rsbuild
- **State Management**:
  - Server state: `react-query`
  - Client state: `mobx`, `mobx-react`
  - URL state: `@ahooksjs/use-url-state`
- **Routing**: `react-router-dom` (v5)
- **Forms**: `react-hook-form`, `zod`
- **UI Library**: `@tidbcloud/uikit` (internal, mantine v7 based).
  - Detailed component docs: `node_modules/@tidbcloud/uikit/llms.txt`
  - **Always search the llms.txt if you are not sure about how to use the component!!**
- **Testing**: `jest` (unit), `playwright` (e2e)
- **Linting**: `eslint`, `prettier`, `knip` (unused files)

---

## Important commands

- Install dependencies:
  - `pnpm i` (Strictly use **pnpm**)
- Start dev server:
  - `pnpm start` (runs `pnpm gen:api` first)
- Generate API clients:
  - `pnpm gen:api` (runs `tsx scripts/swagger/prepare.ts && prettier --write ./swagger/ && orval`)
- Build:
  - `pnpm build`
- Quality checks:
  - `pnpm typecheck`
  - `pnpm lint:all`

---

## Project Structure

- `src/dbaas/`: Main application logic and features.
- `src/dbaas/screens/NextGen/claim/`: Claim landing page for `/tidbs/claim/:id`.
- `src/dbaas/services/`: Generated API clients (do not edit manually).
- `src/uikit/`: Local UI components or overrides.
- `src/common/`: Shared utilities.
- `swagger/`: Stored Swagger JSON files.
- `scripts/`: Helper scripts (swagger fetching, generation, etc.).

---

## API generation (orval)

Where to look:

- Orval config: `orval.config.ts` (root)
- Swagger files: `./swagger/` (generated / committed JSON files)
- Swagger helper & fetcher scripts: `scripts/swagger/*`

How generation works:

- `pnpm gen:api` runs a small prepare step (`scripts/swagger/prepare.ts`) to pre-process some swagger files, formats `./swagger/`, then runs `orval`.
- `orval.config.ts` contains multiple generator entries. Each entry configures `input.target` (swagger file) and `output.target` and `output.schemas` under `src/dbaas/services/...`.
- Important customizations defined in `orval.config.ts`:
  - `clientBuilder` customizes the generated client implementation for react-query.
  - `override.mutator` points to `src/dbaas/services/openapi.ts` (common request mutators) and renames instances per API (e.g., `customInstance`, `alertsInstance`, `o11yInstance`).
  - `requestOptions._metadata` is injected for operation metadata (`operationName`, `pathRoute`).
  - Some entries include `override.transformer` to rewrite paths, parameter names, or operationIds when the swagger needs normalization.
  - `hooks.afterAllFilesWrite` is used (example: runs `prettier --write`) to format generated files.

Guidelines / tips:

- Do NOT edit generated files under `src/dbaas/services/**` directly — they are overwritten by `orval`.
- After every branch switch (`git switch` or `git checkout`) or rebase onto a different base, run `pnpm gen:api` before typecheck, tests, or code diagnosis. This is mandatory even when the task does not touch APIs because generated clients can differ between branches.
- If you need changes in the generated behavior, update `orval.config.ts` (or underlying swagger / transformer) and re-run `pnpm gen:api`.
- If `pnpm typecheck` reports generated-client type mismatches or API type drift, run `pnpm gen:api` before diagnosing application code. Re-run `pnpm typecheck` after generation and inspect whether `src/dbaas/services/**` or `swagger/**` changed.
- If swagger files are missing or outdated, use `pnpm swagger:download:*` scripts or `npx tsx scripts/swagger/gh-swagger-fetcher.ts` to fetch latest JSONs from GitHub.
- If a generator is failing due to GitHub CLI/auth, ensure `gh` is installed and authenticated (`gh auth login`).

---

## Import path aliases

- TypeScript path aliases live in `tsconfig.paths.json` and the project `tsconfig.json` extends it.
  - `baseUrl: "src"` — absolute imports are relative to `src/`.
  - Examples:
    - `dbaas/..` resolves to `src/dbaas/...`
    - `@tidb-cloud-uikit/...` maps to `src/uikit/...`

---

## Code style & commit conventions

- Formatting: **Prettier**. Many scripts and hooks run `prettier --write` automatically.
- Linting: **ESLint** (`pnpm lint:all`).
- Commits: follow **Conventional Commits** (e.g., `feat:`, `fix:`) — used in release tooling.
- Enum Usage from Swagger: When using enums generated from Swagger, prefer string literals for comparisons to ensure type safety and readability. For example, if a field `plan` is typed as `Commonv1beta1ServicePlan`, use `if (plan === 'Premium')` instead of `if (plan === Commonv1beta1ServicePlan.Premium)`. This is because Swagger symbol names may change, but values are generally stable, and long symbol names can be cumbersome.

---

## Analytics & Event Tracking (Mixpanel)

Event tracking system:

- Auto-tracking: Buttons and links with `data-mp-event` attribute are automatically tracked via `enableAutoClickTracking` (`src/dbaas/utils/tracking/enableAutoClickTracking.ts`).
- Event naming: Use the `MixpanelEvents` object from `src/dbaas/utils/tracking` for type-safe event names.

Event naming convention:

- Format: `Feature.Function.Action`
- Example: `MixpanelEvents.PremiumRestore.Create.SubmitClicked`
- Available actions: `SubmitClicked`, `CancelClicked`, `BackClicked`, `ButtonClicked`, `EnterClicked`, etc.
- Available functions: `Create`, `Update`, `Delete`, `View`, `Operate`, `Enable`, `Disable`, `Validate`, `Restore`, etc.

How to add event tracking:

1. **For buttons**: Add `data-mp-event={MixpanelEvents.Feature.Function.Action}` attribute.

   ```tsx
   <Button onClick={handleSubmit} data-mp-event={MixpanelEvents.PremiumRestore.Create.SubmitClicked}>
     Submit
   </Button>
   ```

2. **Pass additional properties**: Use `data-mp-*` attributes to attach custom metadata to events.

   ```tsx
   <Button
     onClick={handleDelete}
     data-mp-event={MixpanelEvents.PremiumBackup.Delete.SubmitClicked}
     data-mp-backup-id={backup.id}
     data-mp-backup-type={backup.type}
   >
     Delete
   </Button>
   ```

   All `data-mp-*` attributes (except `data-mp-propagation-ignored`) will be automatically extracted and included in the event payload with camelCase keys (e.g., `data-mp-backup-id` → `backupId`).

3. **For new features**: Add the feature to `FeatureEvents` interface in `src/dbaas/utils/tracking/event.ts`.

   ```typescript
   interface FeatureEvents {
     // ... existing features
     YourNewFeature: Functions
   }
   ```

4. **Auto-tracking behavior**:
   - Buttons with `data-mp-event` will track on click.
   - Links (`<a>`) without `data-mp-event` auto-track as "Click Route Link" (internal) or "Click Outbound Link" (external).
   - Use `data-mp-propagation-ignored` to prevent event bubbling when nested elements have their own tracking.
   - All `data-mp-*` attributes are automatically included in the event payload (e.g., `data-mp-cluster-id` → `clusterId` in payload).

Best practices:

- **Always** add `data-mp-event` to user-interactive buttons (Submit, Cancel, Delete, etc.).
- Use descriptive feature names that match the domain (e.g., `PremiumRestore`, `RecycleBinPremium`).
- Keep consistency: same action types should use same event names across features.

---

## Frontend `data-testid` Convention

To ensure consistent and reliable testing across the application, follow these conventions when adding `data-testid` attributes:

1. **Add `data-testid` to every UI component's top-level DOM node.**

   - This ensures that all components can be easily located in tests.

2. **Also tag key DOM structures:**

   - Scroll containers
   - Drag/resize handles
   - Virtualized lists
   - Primary action controls

3. **Use kebab-case IDs prefixed with the component name.**

   - Examples: `app-sidebar-scroll`, `history-panel-resize-handle`, `cluster-list-container`
   - This naming pattern makes it easy to identify which component a test ID belongs to.

4. **For repeated list items, reuse a generic test id and add unique data attributes.**
   - Use a generic test ID like `history-list-item` for all items in a list.
   - Add unique data attributes such as `data-path` or `data-sha` to distinguish individual items.
   - Example:
     ```tsx
     {
       items.map((item) => (
         <div key={item.id} data-testid="history-list-item" data-path={item.path} data-sha={item.sha}>
           {item.content}
         </div>
       ))
     }
     ```

Best practices:

- **Always** add `data-testid` to components when creating new UI features.
- Keep test IDs descriptive and consistent across similar components.
- Use the component name as a prefix to avoid naming conflicts.
- For dynamic lists, prefer data attributes over dynamic test IDs to improve test stability.

---

## Useful files & locations

- `orval.config.ts` — API generator config
- `scripts/swagger/` — helper scripts for preparing and fetching swagger JSONs
- `swagger/` — stored swagger JSON files
- `src/dbaas/services/` — generated API clients and schemas
- `src/dbaas/services/openapi.ts` — common mutator / request adapter used by generated clients
- `rsbuild.config.ts` — dev/build config replacing legacy CRA setup
- `tsconfig.paths.json` & `tsconfig.json` — TypeScript path alias configuration
- `package.json` — scripts, jest mapping and other useful commands
