- Add "Dynamic add-item form fields" guidance to CLAUDE.md (pick component by field schema: LabelSelector / ListInput / MetadataList) - Fold StatusTag status-display recipe into create-crud-page skill - Remove DESIGN.md (content migrated to the above)
144 lines
5.5 KiB
Markdown
144 lines
5.5 KiB
Markdown
---
|
|
name: create-crud-page
|
|
description: Scaffold a CRUD list/table page module in the gpustack-ui monorepo. Use when creating a new page module, building a list/table page, adding a create/edit drawer, or setting up the components/config/forms/hooks/services structure for a feature.
|
|
---
|
|
|
|
# Create a CRUD Table Page
|
|
|
|
## Inputs (do this first)
|
|
|
|
- The argument passed to this skill is the **module name** (e.g. `/create-crud-page api-keys` → module `api-keys`). If no name was given, ask for it.
|
|
- **Always ask the user for the API documentation before generating any code**, even if a module name was provided:
|
|
|
|
> Where is the API documentation for this module? (OpenAPI/Swagger URL, schema file path, or an interface description)
|
|
|
|
- Wait for the answer, then read/fetch it. Derive `config/types.ts` (`FormData`, `ListItem`), the `services` request hooks, and form fields from that schema. Do not guess field names or endpoints — if the doc is missing details, ask.
|
|
|
|
- **Also ask which form layout to scaffold:**
|
|
|
|
> Should the form use tabs? (1) a plain form without tabs, or (2) a tabbed form
|
|
|
|
Choose the form structure in section 3 accordingly. Default to **no tabs** unless the user picks tabs or the schema clearly has many grouped sections.
|
|
|
|
---
|
|
|
|
Before anything: **reuse common `components`, `hooks`, and `utils` from `@gpustack/core-ui` whenever possible.**
|
|
|
|
Reference implementation for sections below: `src/pages/model-routes`.
|
|
|
|
## Module structure
|
|
|
|
Create the module under `src/pages/{module}`:
|
|
|
|
```text
|
|
{module}
|
|
├── components
|
|
├── config
|
|
├── forms
|
|
├── hooks
|
|
├── index.tsx
|
|
└── services
|
|
```
|
|
|
|
## 1. components
|
|
|
|
Module-specific components.
|
|
|
|
- The create/edit form component is named `add-xxx-modal.tsx` (repo convention — keep the `-modal` suffix even though it is built with `FormDrawer`).
|
|
- Use `FormDrawer` from `@gpustack/core-ui`.
|
|
- If a table cell's render logic/structure is complex, extract it into `xxx-cell.tsx`.
|
|
|
|
## 2. config
|
|
|
|
```text
|
|
config
|
|
├── index.ts # static configs & constants
|
|
└── types.ts # TypeScript types
|
|
```
|
|
|
|
Naming: form types → `FormData`; table list item types → `ListItem`.
|
|
|
|
## 3. forms
|
|
|
|
Main form component goes in `forms/index.tsx`.
|
|
|
|
- **Complex interactions** (Form.Item split across components): create a dedicated Form Context and wrap with `FormContext.Provider`.
|
|
- **Tab-based forms**: use `ScrollSpyTabs` from `@gpustack/core-ui`, wrapping the `Form` or `FormContext.Provider`. Do not use tabs unless necessary.
|
|
- **Required-field validation**: use `getRuleMessage` for standard `input`/`select`.
|
|
- For cascading selectors and async race protection, follow the **form-patterns** skill.
|
|
|
|
## 4. hooks
|
|
|
|
- Table columns → `use-xxx-columns.tsx`.
|
|
- Open/close hooks for `add-xxx-modal.tsx` → `use-create-xxx.ts`.
|
|
|
|
## 5. index.tsx (list page entry)
|
|
|
|
- **Data fetching**: `useTableFetch` from `@gpustack/core-ui`.
|
|
- **Data display**:
|
|
- Standard table → Ant Design `Table`. Ref: `src/pages/users/index.tsx`.
|
|
- Expandable/collapsible rows → `Table` from `@gpustack/core-ui`. Ref: `src/pages/model-routes/index.tsx`.
|
|
- Card-style lists → use `InfiniteScrollerProvider`. Ref: `src/pages/backends/index.tsx`.
|
|
|
|
## 6. services
|
|
|
|
`request` is injected via a provider — do **not** create a centralized `apis` directory like in `gpustack-ui`. Define request hooks directly in `services`.
|
|
|
|
- Use `useRequest` from `@gpustack/core-ui`, or `useQueryData` (same underlying method).
|
|
- Ref: `src/pages/gpu-service/storage-types/services/use-create-storage-type.ts`.
|
|
|
|
## 7. Empty data
|
|
|
|
- Page table lists → `NoResult`.
|
|
- Simple (non-page) tables → `Empty` with `image={Empty.PRESENTED_IMAGE_SIMPLE}`.
|
|
|
|
## Common UI conventions
|
|
|
|
- **Drawer/Modal open/close**: use `useBodyScroll` from `@gpustack/core-ui`. Ref: `src/pages/model-routes/hooks/use-create-route.ts`.
|
|
- **Status display** (success/failed/processing/warning): use `StatusTag`, never `Tag` from `antd` directly. See **Status display** below. Ref: `src/pages/llmodels/components/table-list.tsx`.
|
|
- **Permission-gated visibility**: use `Access` / `useAccess`. Ref: `src/pages/access/index.tsx`.
|
|
- **Styles**: avoid `styled-components` for complex/large styling. Prefer `createStyles` for component-scoped dynamic styles, CSS Modules (`xxx.module.less`) for static structured styles.
|
|
|
|
## Status display
|
|
|
|
Use `StatusTag` from `@gpustack/core-ui` for status display. Do not use `Tag` from `antd` directly.
|
|
|
|
1. Define the mapping from business status values to UI status values in the module's `config/index.ts`:
|
|
|
|
```ts
|
|
import { StatusMaps } from '@/config';
|
|
import { StatusType } from '@/config/types';
|
|
|
|
export const XxxStatusValueMap = {
|
|
Running: 'running',
|
|
Pending: 'pending',
|
|
Failed: 'failed'
|
|
};
|
|
|
|
export const XxxStatusLabelMap: Record<string, string> = {
|
|
[XxxStatusValueMap.Running]: 'Running',
|
|
[XxxStatusValueMap.Pending]: 'Pending',
|
|
[XxxStatusValueMap.Failed]: 'Failed'
|
|
};
|
|
|
|
export const status: Record<string, StatusType> = {
|
|
[XxxStatusValueMap.Running]: StatusMaps.success,
|
|
[XxxStatusValueMap.Pending]: StatusMaps.transitioning,
|
|
[XxxStatusValueMap.Failed]: StatusMaps.error
|
|
};
|
|
```
|
|
|
|
2. In table columns, pass only the UI status value and display text required by `StatusTag`:
|
|
|
|
```tsx
|
|
<StatusTag
|
|
statusValue={{
|
|
status: status[value],
|
|
text: XxxStatusLabelMap[value] || value,
|
|
message: record.state_message
|
|
}}
|
|
/>
|
|
```
|
|
|
|
`statusValue.status` must be a value mapped from `StatusMaps` (`success`, `transitioning`, `warning`, `error`, `inactive`). Do not pass business status values such as `running` or `pending` directly.
|