Claude-skill-registry details-view
Building view/details pages with DetailField components, model relationships, and JSONB handling
install
source · Clone the upstream repo
git clone https://github.com/majiayu000/claude-skill-registry
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/details-view" ~/.claude/skills/majiayu000-claude-skill-registry-details-view && rm -rf "$T"
manifest:
skills/data/details-view/SKILL.mdsource content
Details View Skill
Core Principles
- MobX state management - NO state hooks or other libraries
- Group related fields using
DetailFieldContainer - Ask first if unclear about field importance
Available Components
Located in:
ui/src/common/components/form/details/
Container
- Groups related fields, wraps grid safelyDetailFieldContainer
Basic Fields
- Short text fieldsDetailFieldText
- Long text fieldsDetailFieldTextArea
- Boolean 0/1 fieldsDetailFieldCheckbox
- Display-only fieldsDetailFieldReadOnly
- Date pickerDetailFieldDate
- Color pickerDetailFieldColor
- Code editorDetailFieldCodeEdit
Single Select (Constants)
import { constants } from "@/models/constants"; <DetailFieldSelect record={props.asset} field="status" label="Status" options={constants.asset.status} displayField="statusStr" />
Multi Select (Constants)
<DetailFieldMultiSelect record={props.account} field="permissions" label="Permissions" options={constants.account.permissions} />
Model Relationships
Small lists (< 50 options):
<DetailFieldModelSelect<AssetModel, OrganizationModel> record={props.asset} field="organization_id" displayField="organization_name" label="Organization" placeholder="Select Organization" modelName="organization" modelSearchField="q" modelDisplayField="label" modelSearchFilters={{ disabled: "0" }} reloadOnSave={true} />
Large lists (use search):
<DetailFieldModelSearchSelect<AssetModel, OrganizationModel> record={props.asset} field="organization_id" displayField="organization_name" label="Organization" placeholder="Search Organization" modelName="organization" modelSearchField="q" modelDisplayField="label" modelSearchFilters={{ disabled: "0" }} reloadOnSave={true} />
Multi-select (small lists):
<DetailFieldModelMultiSelect<AssetModel, TagModel> record={props.asset} field="tag_ids" label="Tags" placeholder="Select Tags" modelName="tag" modelSearchField="q" modelDisplayField="label" />
Multi-select (large lists with search):
<DetailFieldModelSearchMultiSelect<AssetModel, TagModel> record={props.asset} field="tag_ids" label="Tags" placeholder="Search Tags" modelName="tag" modelSearchField="q" modelDisplayField="label" />
Model Relationship Best Practices
- Always try to use
as modelDisplayFieldlabel - Always try to use
as modelSearchFieldq - If model doesn't have a good label, modify it in
XXModel.ts - Assume all models exist - don't create new ones
- Use
if related data needs refreshreloadOnSave={true}
JSONB Fields Pattern
For nested JSONB objects, use
SafeBaseModel helper and parentRecord:
import { SafeBaseModel } from "@/models/types"; // JSONB class must extend ValidationClass <DetailFieldTextArea record={props.organization.properties as SafeBaseModel<Properties>} parentRecord={props.organization} field="pricing_text" label="Pricing Text" placeholder="Enter pricing text..." helpText="Default text if no pricing info in plan" />
Grouping Fields with Container
<DetailFieldContainer label="Basic Information"> <DetailFieldText record={props.asset} field="name" label="Name" placeholder="Asset Name" /> <DetailFieldTextArea record={props.asset} field="description" label="Description" placeholder="Description..." /> </DetailFieldContainer> <DetailFieldContainer label="Organization Details"> <DetailFieldModelSearchSelect<AssetModel, OrganizationModel> record={props.asset} field="organization_id" displayField="organization_name" label="Organization" modelName="organization" modelSearchField="q" modelDisplayField="label" /> <DetailFieldSelect record={props.asset} field="status" label="Status" options={constants.asset.status} displayField="statusStr" /> </DetailFieldContainer>
Complete Example
import { observer } from "mobx-react-lite"; import { AssetModel } from "@/models/asset"; import { OrganizationModel } from "@/models/organization"; import { constants } from "@/models/constants"; import { DetailFieldContainer, DetailFieldText, DetailFieldTextArea, DetailFieldSelect, DetailFieldModelSearchSelect, DetailFieldCheckbox, DetailFieldDate, } from "@/ui/common/components/form/details"; interface AssetDetailsProps { asset: AssetModel; } export const AssetDetails = observer((props: AssetDetailsProps) => { return ( <div className="space-y-6"> <DetailFieldContainer label="Basic Information"> <DetailFieldText record={props.asset} field="name" label="Name" placeholder="Asset Name" /> <DetailFieldTextArea record={props.asset} field="description" label="Description" placeholder="Asset description..." /> <DetailFieldCheckbox record={props.asset} field="is_active" label="Is Active" /> </DetailFieldContainer> <DetailFieldContainer label="Organization & Status"> <DetailFieldModelSearchSelect<AssetModel, OrganizationModel> record={props.asset} field="organization_id" displayField="organization_name" label="Organization" modelName="organization" modelSearchField="q" modelDisplayField="label" modelSearchFilters={{ disabled: "0" }} reloadOnSave={true} /> <DetailFieldSelect record={props.asset} field="status" label="Status" options={constants.asset.status} displayField="statusStr" /> <DetailFieldDate record={props.asset} field="effective_date" label="Effective Date" /> </DetailFieldContainer> </div> ); });
Key Rules
- Always generate component with
#ui_code_tools - Group related fields logically with
DetailFieldContainer - Use search variants for large option lists (>50 items)
- Use
for computed string versions of IDsdisplayField - Pass
when editing JSONB nested objectsparentRecord - Constants are model-specific:
constants.{model}.{field} - Ask before implementing if field importance is unclear