fix hyper-links

This commit is contained in:
MaKarin
2026-03-24 14:43:01 +03:00
parent dd8678cb11
commit f08b862b15
6 changed files with 96 additions and 31 deletions

View File

@@ -1,13 +1,13 @@
import { Show, SimpleShowLayout, TextField } from 'react-admin'; import { Show, SimpleShowLayout, TextField, NumberField } from 'react-admin';
export const EquipmentTypeShow = () => ( export const EquipmentTypeShow = () => (
<Show> <Show>
<SimpleShowLayout> <SimpleShowLayout>
<TextField source="code" label="code" /> <TextField source="code" label="Код вида оборудования" />
<TextField source="name" label="name" /> <TextField source="name" label="Наименование вида" />
<TextField source="manufacturer" label="manufacturer" /> <TextField source="manufacturer" label="Производитель" />
<TextField source="maintenanceIntervalHours" label="maintenanceIntervalHours" /> <NumberField source="maintenanceIntervalHours" label="Периодичность ТО, моточасов" />
<TextField source="overhaulIntervalHours" label="overhaulIntervalHours" /> <NumberField source="overhaulIntervalHours" label="Периодичность КР, моточасов" />
</SimpleShowLayout> </SimpleShowLayout>
</Show> </Show>
); );

View File

@@ -1,20 +1,28 @@
import { Show, SimpleShowLayout, TextField } from 'react-admin'; import { Show, SimpleShowLayout, TextField, NumberField, DateField, SelectField, ReferenceField } from 'react-admin';
const statusChoices = [
{ id: 'Active', name: 'В эксплуатации' },
{ id: 'Repair', name: 'В ремонте' },
{ id: 'Reserve', name: 'В резерве' },
{ id: 'WriteOff', name: 'Списано' },
];
export const EquipmentShow = () => ( export const EquipmentShow = () => (
<Show> <Show>
<SimpleShowLayout> <SimpleShowLayout>
<TextField source="id" label="id" /> <TextField source="id" label="id" />
<TextField source="inventoryNumber" label="inventoryNumber" /> <TextField source="inventoryNumber" label="Инвентарный номер" />
<TextField source="serialNumber" label="serialNumber" /> <TextField source="serialNumber" label="Заводской (серийный) номер" />
<TextField source="name" label="name" /> <TextField source="name" label="Наименование единицы оборудования" />
<TextField source="equipmentTypeCode" label="equipmentTypeCode" /> <ReferenceField source="equipmentTypeCode" reference="equipment-types" label="Вид оборудования" link="show">
<TextField source="status" label="status" /> <TextField source="code" />
<TextField source="location" label="location" /> </ReferenceField>
<TextField source="commissionedAt" label="commissionedAt" /> <SelectField source="status" label="Текущий статус" choices={statusChoices} />
<TextField source="totalEngineHours" label="totalEngineHours" /> <TextField source="location" label="Место эксплуатации / скважина / куст" />
<TextField source="engineHoursSinceLastRepair" label="engineHoursSinceLastRepair" /> <DateField source="commissionedAt" label="Дата ввода в эксплуатацию" />
<TextField source="lastRepairAt" label="lastRepairAt" /> <NumberField source="totalEngineHours" label="Общая наработка, моточасов" />
<TextField source="notes" label="notes" /> <NumberField source="engineHoursSinceLastRepair" label="Наработка с последнего ремонта, моточасов" />
<DateField source="lastRepairAt" label="Дата последнего ремонта" />
<TextField source="notes" label="Примечания" />
</SimpleShowLayout> </SimpleShowLayout>
</Show> </Show>
); );

View File

@@ -1,20 +1,38 @@
import { Show, SimpleShowLayout, TextField } from 'react-admin'; import { Show, SimpleShowLayout, TextField, NumberField, DateField, SelectField, ReferenceField } from 'react-admin';
const repairKindChoices = [
{ id: 'TO', name: 'Техническое обслуживание' },
{ id: 'TR', name: 'Текущий ремонт' },
{ id: 'TRE', name: 'Текущий расширенный ремонт' },
{ id: 'KR', name: 'Капитальный ремонт' },
{ id: 'AR', name: 'Аварийный ремонт' },
{ id: 'MP', name: 'Метрологическая поверка' },
];
const statusChoices = [
{ id: 'Draft', name: 'Черновик' },
{ id: 'Approved', name: 'Утверждена' },
{ id: 'InWork', name: 'В работе' },
{ id: 'Done', name: 'Выполнена' },
{ id: 'Cancelled', name: 'Отменена' },
];
export const RepairOrderShow = () => ( export const RepairOrderShow = () => (
<Show> <Show>
<SimpleShowLayout> <SimpleShowLayout>
<TextField source="id" label="id" /> <TextField source="id" label="id" />
<TextField source="number" label="number" /> <TextField source="number" label="Номер заявки" />
<TextField source="equipmentId" label="equipmentId" /> <ReferenceField source="equipmentId" reference="equipment" label="Оборудование" link="show">
<TextField source="repairKind" label="repairKind" /> <TextField source="inventoryNumber" />
<TextField source="status" label="status" /> </ReferenceField>
<TextField source="plannedAt" label="plannedAt" /> <SelectField source="repairKind" label="Вид ремонта" choices={repairKindChoices} />
<TextField source="startedAt" label="startedAt" /> <SelectField source="status" label="Статус" choices={statusChoices} />
<TextField source="completedAt" label="completedAt" /> <DateField source="plannedAt" label="Плановая дата начала" />
<TextField source="contractor" label="contractor" /> <DateField source="startedAt" label="Фактическая дата начала" />
<TextField source="engineHoursAtRepair" label="engineHoursAtRepair" /> <DateField source="completedAt" label="Фактическая дата завершения" />
<TextField source="description" label="description" /> <TextField source="contractor" label="Подрядная организация (если внешний ремонт)" />
<TextField source="notes" label="notes" /> <NumberField source="engineHoursAtRepair" label="Наработка на момент ремонта, моточасов" />
<TextField source="description" label="Описание работ / дефекта" />
<TextField source="notes" label="Примечания" />
</SimpleShowLayout> </SimpleShowLayout>
</Show> </Show>
); );

View File

@@ -550,7 +550,42 @@ function renderFrontendResource(entityName, entity, resourceName, pk, enums, all
const editImports = ['Edit', ...Array.from(formImportSet)].join(', '); const editImports = ['Edit', ...Array.from(formImportSet)].join(', ');
const edit = `import { ${editImports} } from 'react-admin';\n\n${choiceConsts.join('\n')}\nexport const ${className}Edit = () => (\n <Edit>\n <SimpleForm>\n ${entity.attributes.map((a) => formField(a, 'edit')).filter(Boolean).join('\n ')}\n </SimpleForm>\n </Edit>\n);\n`; const edit = `import { ${editImports} } from 'react-admin';\n\n${choiceConsts.join('\n')}\nexport const ${className}Edit = () => (\n <Edit>\n <SimpleForm>\n ${entity.attributes.map((a) => formField(a, 'edit')).filter(Boolean).join('\n ')}\n </SimpleForm>\n </Edit>\n);\n`;
const show = `import { Show, SimpleShowLayout, TextField } from 'react-admin';\n\nexport const ${className}Show = () => (\n <Show>\n <SimpleShowLayout>\n ${entity.attributes.map((a) => `<TextField source="${a.name}" label="${a.name}" />`).join('\n ')}\n </SimpleShowLayout>\n </Show>\n);\n`; const showImportSet = new Set(['Show', 'SimpleShowLayout', 'TextField']);
if (hasNumber) showImportSet.add('NumberField');
if (hasDate) showImportSet.add('DateField');
if (enumAttrs.length) showImportSet.add('SelectField');
if (hasFK) showImportSet.add('ReferenceField');
const showFields = [];
for (const a of entity.attributes) {
const label = getAttributeLabel(a, allEntities);
if (a.foreign) {
const referenceEntity = allEntities[a.foreign.entity];
const referenceAttrs = getEntityAttrNames(referenceEntity);
const fieldSource = referenceAttrs.has('inventoryNumber')
? 'inventoryNumber'
: referenceAttrs.has('code')
? 'code'
: referenceAttrs.has('number')
? 'number'
: 'name';
showFields.push(
`<ReferenceField source="${a.name}" reference="${pluralize(toKebab(a.foreign.entity))}" label="${label}" link="show">\n <TextField source="${fieldSource}" />\n </ReferenceField>`
);
continue;
}
if (a.type === 'date') {
showFields.push(`<DateField source="${a.name}" label="${label}" />`);
} else if (['integer', 'decimal'].includes(a.type)) {
showFields.push(`<NumberField source="${a.name}" label="${label}" />`);
} else if (!['string', 'text', 'uuid', 'integer', 'decimal', 'date'].includes(a.type)) {
showFields.push(`<SelectField source="${a.name}" label="${label}" choices={${a.name === 'status' ? 'statusChoices' : `${a.name}Choices`}} />`);
} else {
showFields.push(`<TextField source="${a.name}" label="${label}" />`);
}
}
const show = `import { ${Array.from(showImportSet).join(', ')} } from 'react-admin';\n\n${choiceConsts.join('\n')}export const ${className}Show = () => (\n <Show>\n <SimpleShowLayout>\n ${showFields.join('\n ')}\n </SimpleShowLayout>\n </Show>\n);\n`;
return { return {
files: { files: {

View File

@@ -25,6 +25,7 @@ The frontend stays a React Admin SPA generated from `domain/*.dsl` and anchored
- Each entity becomes a React Admin resource with list/create/edit/show views. - Each entity becomes a React Admin resource with list/create/edit/show views.
- Resource names must stay aligned with backend path segments. - Resource names must stay aligned with backend path segments.
- Foreign keys must use `ReferenceInput` / `ReferenceField`. - Foreign keys must use `ReferenceInput` / `ReferenceField`.
- Foreign keys shown in list/show views must stay clickable via `ReferenceField link="show"` to open full details of the related resource.
- Lists must expose filters through `List` `filters` and an actions toolbar with `FilterButton`. - Lists must expose filters through `List` `filters` and an actions toolbar with `FilterButton`.
- For enum fields where multi-select is required (for example `status`), use `SelectArrayInput` in list filters. - For enum fields where multi-select is required (for example `status`), use `SelectArrayInput` in list filters.
- For foreign key filters and form selection use `ReferenceInput` + `AutocompleteInput` with `filterToQuery={(searchText) => ({ q: searchText })}`. - For foreign key filters and form selection use `ReferenceInput` + `AutocompleteInput` with `filterToQuery={(searchText) => ({ q: searchText })}`.
@@ -50,6 +51,7 @@ The frontend stays a React Admin SPA generated from `domain/*.dsl` and anchored
- Natural-key resources must preserve route, update, and sort compatibility with React Admin contracts. - Natural-key resources must preserve route, update, and sort compatibility with React Admin contracts.
- Frontend requests must continue to work when the real primary key is not named `id`. - Frontend requests must continue to work when the real primary key is not named `id`.
- `dataProvider` query serialization must preserve repeated query params for array filters (for example enum multi-select). - `dataProvider` query serialization must preserve repeated query params for array filters (for example enum multi-select).
- `Resource` wiring in `App.tsx` must keep `show={...}` registration for all generated resources.
## Reproducibility invariants ## Reproducibility invariants

View File

@@ -52,6 +52,8 @@ Validation is now a lightweight automated gate instead of a prose-only checklist
- typed form mapping is preserved: - typed form mapping is preserved:
- `integer` / `decimal` -> `NumberInput` - `integer` / `decimal` -> `NumberInput`
- `date` -> `DateInput` - `date` -> `DateInput`
- reference fields intended for navigation keep `ReferenceField link="show"`
- resources keep `show={...}` registration in `App.tsx`
### Natural-key checks ### Natural-key checks