create-crud-page
Skill100 starsChanged 38 days agoApache-2.0View on GitHub
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.
In this repository4 other files it gives its agents.
- AGENTS.mdAGENTS.md
- CLAUDE.mdCLAUDE.md
- design-reviewSkill
- form-patternsSkill
The file
We split this skill into the rules it gives an agent
12 rules. About 0 are common, 0 are distinctive, and 12 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈0
Distinctive · estimate
This author's own choices. Worth a look.
12
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 15
Wait for the answer, then read/fetch it.
- Very specific: line 25
Before anything: reuse common components, hooks, and utils from @gpustack/core-ui whenever possible.
- Very specific: line 31
Create the module under src/pages/{module}:
- Very specific: line 48
Use FormDrawer from @gpustack/core-ui.
- Very specific: line 49
If a table cell's render logic/structure is complex, extract it into xxx-cell.tsx.
- Very specific: line 66
Tab-based forms: use ScrollSpyTabs from @gpustack/core-ui, wrapping the Form or FormContext.Provider.
- Very specific: line 67
Required-field validation: use getRuleMessage for standard input/select.
- Very specific: line 87
Use useRequest from @gpustack/core-ui, or useQueryData (same underlying method).
- Very specific: line 97
Drawer/Modal open/close: use useBodyScroll from @gpustack/core-ui.
- Very specific: line 99
Permission-gated visibility: use Access / useAccess. Ref: src/pages/access/index.tsx.
- Very specific: line 100
Styles: avoid styled-components for complex/large styling.
- Very specific: line 104
Use StatusTag from @gpustack/core-ui for status display.
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
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→ moduleapi-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), theservicesrequest 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}:
{module}
├── components
├── config
├── forms
├── hooks
├── index.tsx
└── services1. components
Module-specific components.
- The create/edit form component is named
add-xxx-modal.tsx(repo convention — keep the-modalsuffix even though it is built withFormDrawer). - Use
FormDrawerfrom@gpustack/core-ui. - If a table cell's render logic/structure is complex, extract it into
xxx-cell.tsx.
2. config
config
├── index.ts # static configs & constants
└── types.ts # TypeScript typesNaming: 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
ScrollSpyTabsfrom@gpustack/core-ui, wrapping theFormorFormContext.Provider. Do not use tabs unless necessary. - Required-field validation: use
getRuleMessagefor standardinput/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:
useTableFetchfrom@gpustack/core-ui. - Data display:
- Standard table → Ant Design
Table. Ref:src/pages/users/index.tsx. - Expandable/collapsible rows →
Tablefrom@gpustack/core-ui. Ref:src/pages/model-routes/index.tsx. - Card-style lists → use
InfiniteScrollerProvider. Ref:src/pages/backends/index.tsx.
- Standard table → Ant Design
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
useRequestfrom@gpustack/core-ui, oruseQueryData(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 →
Emptywithimage={Empty.PRESENTED_IMAGE_SIMPLE}.
Common UI conventions
- Drawer/Modal open/close: use
useBodyScrollfrom@gpustack/core-ui. Ref:src/pages/model-routes/hooks/use-create-route.ts. - Status display (success/failed/processing/warning): use
StatusTag, neverTagfromantddirectly. 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-componentsfor complex/large styling. PrefercreateStylesfor 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.
- Define the mapping from business status values to UI status values in the module's
config/index.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
};- In table columns, pass only the UI status value and display text required by
StatusTag:
<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.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

