fix after review
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
import { DataProvider, fetchUtils } from 'react-admin';
|
import { DataProvider, fetchUtils } from 'react-admin';
|
||||||
|
|
||||||
const apiUrl = 'http://localhost:3001';
|
const apiUrl = 'http://localhost:3000';
|
||||||
const httpClient = fetchUtils.fetchJson;
|
const httpClient = fetchUtils.fetchJson;
|
||||||
|
|
||||||
function buildQueryString(query: Record<string, unknown>) {
|
function buildQueryString(query: Record<string, unknown>) {
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
import { Create, SimpleForm, TextInput } from 'react-admin';
|
import { Create, SimpleForm, TextInput, NumberInput } from 'react-admin';
|
||||||
|
|
||||||
|
|
||||||
export const EquipmentTypeCreate = () => (
|
export const EquipmentTypeCreate = () => (
|
||||||
<Create>
|
<Create>
|
||||||
<SimpleForm>
|
<SimpleForm>
|
||||||
<TextInput source="code" label="code" isRequired />
|
<TextInput source="code" label="Код вида оборудования" isRequired />
|
||||||
<TextInput source="name" label="name" isRequired />
|
<TextInput source="name" label="Наименование вида" isRequired />
|
||||||
<TextInput source="manufacturer" label="manufacturer" />
|
<TextInput source="manufacturer" label="Производитель" />
|
||||||
<TextInput source="maintenanceIntervalHours" label="maintenanceIntervalHours" />
|
<NumberInput source="maintenanceIntervalHours" label="Периодичность ТО, моточасов" />
|
||||||
<TextInput source="overhaulIntervalHours" label="overhaulIntervalHours" />
|
<NumberInput source="overhaulIntervalHours" label="Периодичность КР, моточасов" />
|
||||||
</SimpleForm>
|
</SimpleForm>
|
||||||
</Create>
|
</Create>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
import { Edit, SimpleForm, TextInput } from 'react-admin';
|
import { Edit, SimpleForm, TextInput, NumberInput } from 'react-admin';
|
||||||
|
|
||||||
|
|
||||||
export const EquipmentTypeEdit = () => (
|
export const EquipmentTypeEdit = () => (
|
||||||
<Edit>
|
<Edit>
|
||||||
<SimpleForm>
|
<SimpleForm>
|
||||||
<TextInput source="code" label="code" disabled />
|
<TextInput source="code" label="Код вида оборудования" disabled />
|
||||||
<TextInput source="name" label="name" isRequired />
|
<TextInput source="name" label="Наименование вида" isRequired />
|
||||||
<TextInput source="manufacturer" label="manufacturer" />
|
<TextInput source="manufacturer" label="Производитель" />
|
||||||
<TextInput source="maintenanceIntervalHours" label="maintenanceIntervalHours" />
|
<NumberInput source="maintenanceIntervalHours" label="Периодичность ТО, моточасов" />
|
||||||
<TextInput source="overhaulIntervalHours" label="overhaulIntervalHours" />
|
<NumberInput source="overhaulIntervalHours" label="Периодичность КР, моточасов" />
|
||||||
</SimpleForm>
|
</SimpleForm>
|
||||||
</Edit>
|
</Edit>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -13,8 +13,8 @@ import {
|
|||||||
|
|
||||||
const equipmentTypeFilters = [
|
const equipmentTypeFilters = [
|
||||||
<TextInput key="q" source="q" label="Поиск" alwaysOn />,
|
<TextInput key="q" source="q" label="Поиск" alwaysOn />,
|
||||||
<TextInput key="name" source="name" label="name" />,
|
<TextInput key="name" source="name" label="Наименование вида" />,
|
||||||
<TextInput key="manufacturer" source="manufacturer" label="manufacturer" />
|
<TextInput key="manufacturer" source="manufacturer" label="Производитель" />
|
||||||
];
|
];
|
||||||
|
|
||||||
const EquipmentTypeListActions = () => (
|
const EquipmentTypeListActions = () => (
|
||||||
@@ -28,11 +28,11 @@ const EquipmentTypeListActions = () => (
|
|||||||
export const EquipmentTypeList = () => (
|
export const EquipmentTypeList = () => (
|
||||||
<List actions={<EquipmentTypeListActions />} filters={equipmentTypeFilters} sort={{ field: 'code', order: 'ASC' }}>
|
<List actions={<EquipmentTypeListActions />} filters={equipmentTypeFilters} sort={{ field: 'code', order: 'ASC' }}>
|
||||||
<Datagrid rowClick="show">
|
<Datagrid rowClick="show">
|
||||||
<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="Производитель" />
|
||||||
<NumberField source="maintenanceIntervalHours" label="maintenanceIntervalHours" />
|
<NumberField source="maintenanceIntervalHours" label="Периодичность ТО, моточасов" />
|
||||||
<NumberField source="overhaulIntervalHours" label="overhaulIntervalHours" />
|
<NumberField source="overhaulIntervalHours" label="Периодичность КР, моточасов" />
|
||||||
</Datagrid>
|
</Datagrid>
|
||||||
</List>
|
</List>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,28 +1,28 @@
|
|||||||
import { Create, SimpleForm, TextInput, SelectInput, ReferenceInput, AutocompleteInput } from 'react-admin';
|
import { Create, SimpleForm, TextInput, NumberInput, DateInput, SelectInput, ReferenceInput, AutocompleteInput } from 'react-admin';
|
||||||
|
|
||||||
const statusChoices = [
|
const statusChoices = [
|
||||||
{ id: 'Active', name: 'Active' },
|
{ id: 'Active', name: 'В эксплуатации' },
|
||||||
{ id: 'Repair', name: 'Repair' },
|
{ id: 'Repair', name: 'В ремонте' },
|
||||||
{ id: 'Reserve', name: 'Reserve' },
|
{ id: 'Reserve', name: 'В резерве' },
|
||||||
{ id: 'WriteOff', name: 'WriteOff' },
|
{ id: 'WriteOff', name: 'Списано' },
|
||||||
];
|
];
|
||||||
|
|
||||||
export const EquipmentCreate = () => (
|
export const EquipmentCreate = () => (
|
||||||
<Create>
|
<Create>
|
||||||
<SimpleForm>
|
<SimpleForm>
|
||||||
<TextInput source="inventoryNumber" label="inventoryNumber" isRequired />
|
<TextInput source="inventoryNumber" label="Инвентарный номер" isRequired />
|
||||||
<TextInput source="serialNumber" label="serialNumber" />
|
<TextInput source="serialNumber" label="Заводской (серийный) номер" />
|
||||||
<TextInput source="name" label="name" isRequired />
|
<TextInput source="name" label="Наименование единицы оборудования" isRequired />
|
||||||
<ReferenceInput source="equipmentTypeCode" reference="equipment-types" label="equipmentTypeCode">
|
<ReferenceInput source="equipmentTypeCode" reference="equipment-types">
|
||||||
<AutocompleteInput optionText={(record) => record.code ? `${record.code} — ${record.name ?? record.code}` : (record.name ?? record.id)} filterToQuery={(searchText) => ({ q: searchText })} />
|
<AutocompleteInput label="Вид оборудования" optionText={(record) => record.code ? `${record.code} — ${record.name ?? record.code}` : (record.name ?? record.id)} filterToQuery={(searchText) => ({ q: searchText })} />
|
||||||
</ReferenceInput>
|
</ReferenceInput>
|
||||||
<SelectInput source="status" label="status" choices={statusChoices} emptyText="Не выбрано" />
|
<SelectInput source="status" label="Текущий статус" choices={statusChoices} emptyText="Не выбрано" />
|
||||||
<TextInput source="location" label="location" />
|
<TextInput source="location" label="Место эксплуатации / скважина / куст" />
|
||||||
<TextInput source="commissionedAt" label="commissionedAt" />
|
<DateInput source="commissionedAt" label="Дата ввода в эксплуатацию" />
|
||||||
<TextInput source="totalEngineHours" label="totalEngineHours" />
|
<NumberInput source="totalEngineHours" label="Общая наработка, моточасов" />
|
||||||
<TextInput source="engineHoursSinceLastRepair" label="engineHoursSinceLastRepair" />
|
<NumberInput source="engineHoursSinceLastRepair" label="Наработка с последнего ремонта, моточасов" />
|
||||||
<TextInput source="lastRepairAt" label="lastRepairAt" />
|
<DateInput source="lastRepairAt" label="Дата последнего ремонта" />
|
||||||
<TextInput source="notes" label="notes" />
|
<TextInput source="notes" label="Примечания" />
|
||||||
</SimpleForm>
|
</SimpleForm>
|
||||||
</Create>
|
</Create>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,29 +1,29 @@
|
|||||||
import { Edit, SimpleForm, TextInput, SelectInput, ReferenceInput, AutocompleteInput } from 'react-admin';
|
import { Edit, SimpleForm, TextInput, NumberInput, DateInput, SelectInput, ReferenceInput, AutocompleteInput } from 'react-admin';
|
||||||
|
|
||||||
const statusChoices = [
|
const statusChoices = [
|
||||||
{ id: 'Active', name: 'Active' },
|
{ id: 'Active', name: 'В эксплуатации' },
|
||||||
{ id: 'Repair', name: 'Repair' },
|
{ id: 'Repair', name: 'В ремонте' },
|
||||||
{ id: 'Reserve', name: 'Reserve' },
|
{ id: 'Reserve', name: 'В резерве' },
|
||||||
{ id: 'WriteOff', name: 'WriteOff' },
|
{ id: 'WriteOff', name: 'Списано' },
|
||||||
];
|
];
|
||||||
|
|
||||||
export const EquipmentEdit = () => (
|
export const EquipmentEdit = () => (
|
||||||
<Edit>
|
<Edit>
|
||||||
<SimpleForm>
|
<SimpleForm>
|
||||||
<TextInput source="id" label="id" disabled />
|
<TextInput source="id" label="id" disabled />
|
||||||
<TextInput source="inventoryNumber" label="inventoryNumber" isRequired />
|
<TextInput source="inventoryNumber" label="Инвентарный номер" isRequired />
|
||||||
<TextInput source="serialNumber" label="serialNumber" />
|
<TextInput source="serialNumber" label="Заводской (серийный) номер" />
|
||||||
<TextInput source="name" label="name" isRequired />
|
<TextInput source="name" label="Наименование единицы оборудования" isRequired />
|
||||||
<ReferenceInput source="equipmentTypeCode" reference="equipment-types" label="equipmentTypeCode">
|
<ReferenceInput source="equipmentTypeCode" reference="equipment-types">
|
||||||
<AutocompleteInput optionText={(record) => record.code ? `${record.code} — ${record.name ?? record.code}` : (record.name ?? record.id)} filterToQuery={(searchText) => ({ q: searchText })} />
|
<AutocompleteInput label="Вид оборудования" optionText={(record) => record.code ? `${record.code} — ${record.name ?? record.code}` : (record.name ?? record.id)} filterToQuery={(searchText) => ({ q: searchText })} />
|
||||||
</ReferenceInput>
|
</ReferenceInput>
|
||||||
<SelectInput source="status" label="status" choices={statusChoices} emptyText="Не выбрано" />
|
<SelectInput source="status" label="Текущий статус" choices={statusChoices} emptyText="Не выбрано" />
|
||||||
<TextInput source="location" label="location" />
|
<TextInput source="location" label="Место эксплуатации / скважина / куст" />
|
||||||
<TextInput source="commissionedAt" label="commissionedAt" />
|
<DateInput source="commissionedAt" label="Дата ввода в эксплуатацию" />
|
||||||
<TextInput source="totalEngineHours" label="totalEngineHours" />
|
<NumberInput source="totalEngineHours" label="Общая наработка, моточасов" />
|
||||||
<TextInput source="engineHoursSinceLastRepair" label="engineHoursSinceLastRepair" />
|
<NumberInput source="engineHoursSinceLastRepair" label="Наработка с последнего ремонта, моточасов" />
|
||||||
<TextInput source="lastRepairAt" label="lastRepairAt" />
|
<DateInput source="lastRepairAt" label="Дата последнего ремонта" />
|
||||||
<TextInput source="notes" label="notes" />
|
<TextInput source="notes" label="Примечания" />
|
||||||
</SimpleForm>
|
</SimpleForm>
|
||||||
</Edit>
|
</Edit>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -13,27 +13,60 @@ import {
|
|||||||
ReferenceField,
|
ReferenceField,
|
||||||
SelectArrayInput,
|
SelectArrayInput,
|
||||||
ReferenceInput,
|
ReferenceInput,
|
||||||
AutocompleteInput
|
AutocompleteInput,
|
||||||
} from 'react-admin';
|
} from "react-admin";
|
||||||
|
|
||||||
const statusChoices = [
|
const statusChoices = [
|
||||||
{ id: 'Active', name: 'Active' },
|
{ id: "Active", name: "В эксплуатации" },
|
||||||
{ id: 'Repair', name: 'Repair' },
|
{ id: "Repair", name: "В ремонте" },
|
||||||
{ id: 'Reserve', name: 'Reserve' },
|
{ id: "Reserve", name: "В резерве" },
|
||||||
{ id: 'WriteOff', name: 'WriteOff' },
|
{ id: "WriteOff", name: "Списано" },
|
||||||
];
|
];
|
||||||
|
|
||||||
const equipmentFilters = [
|
const equipmentFilters = [
|
||||||
<TextInput key="q" source="q" label="Поиск" alwaysOn />,
|
<TextInput key="q" source="q" label="Поиск" alwaysOn />,
|
||||||
<TextInput key="inventoryNumber" source="inventoryNumber" label="inventoryNumber" />,
|
<TextInput
|
||||||
<TextInput key="serialNumber" source="serialNumber" label="serialNumber" />,
|
key="inventoryNumber"
|
||||||
<TextInput key="name" source="name" label="name" />,
|
source="inventoryNumber"
|
||||||
<ReferenceInput key="equipmentTypeCode" source="equipmentTypeCode" reference="equipment-types" label="equipmentTypeCode">
|
label="Инвентарный номер"
|
||||||
<AutocompleteInput optionText={(record) => record.code ? `${record.code} — ${record.name ?? record.code}` : (record.name ?? record.id)} filterToQuery={(searchText) => ({ q: searchText })} />
|
/>,
|
||||||
|
<TextInput
|
||||||
|
key="serialNumber"
|
||||||
|
source="serialNumber"
|
||||||
|
label="Заводской (серийный) номер"
|
||||||
|
/>,
|
||||||
|
<TextInput
|
||||||
|
key="name"
|
||||||
|
source="name"
|
||||||
|
label="Наименование единицы оборудования"
|
||||||
|
/>,
|
||||||
|
<ReferenceInput
|
||||||
|
key="equipmentTypeCode"
|
||||||
|
source="equipmentTypeCode"
|
||||||
|
reference="equipment-types"
|
||||||
|
label="Вид оборудования"
|
||||||
|
>
|
||||||
|
<AutocompleteInput
|
||||||
|
optionText={(record) =>
|
||||||
|
record.code
|
||||||
|
? `${record.code} — ${record.name ?? record.code}`
|
||||||
|
: (record.name ?? record.id)
|
||||||
|
}
|
||||||
|
filterToQuery={(searchText) => ({ q: searchText })}
|
||||||
|
/>
|
||||||
</ReferenceInput>,
|
</ReferenceInput>,
|
||||||
<SelectArrayInput key="status" source="status" label="status" choices={statusChoices} />,
|
<SelectArrayInput
|
||||||
<TextInput key="location" source="location" label="location" />,
|
key="status"
|
||||||
<TextInput key="notes" source="notes" label="notes" />
|
source="status"
|
||||||
|
label="Текущий статус"
|
||||||
|
choices={statusChoices}
|
||||||
|
/>,
|
||||||
|
<TextInput
|
||||||
|
key="location"
|
||||||
|
source="location"
|
||||||
|
label="Место эксплуатации / скважина / куст"
|
||||||
|
/>,
|
||||||
|
<TextInput key="notes" source="notes" label="Примечания" />,
|
||||||
];
|
];
|
||||||
|
|
||||||
const EquipmentListActions = () => (
|
const EquipmentListActions = () => (
|
||||||
@@ -45,22 +78,44 @@ const EquipmentListActions = () => (
|
|||||||
);
|
);
|
||||||
|
|
||||||
export const EquipmentList = () => (
|
export const EquipmentList = () => (
|
||||||
<List actions={<EquipmentListActions />} filters={equipmentFilters} sort={{ field: 'id', order: 'ASC' }}>
|
<List
|
||||||
|
actions={<EquipmentListActions />}
|
||||||
|
filters={equipmentFilters}
|
||||||
|
sort={{ field: "inventoryNumber", order: "ASC" }}
|
||||||
|
>
|
||||||
<Datagrid rowClick="show">
|
<Datagrid rowClick="show">
|
||||||
<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="Наименование единицы оборудования" />
|
||||||
<ReferenceField source="equipmentTypeCode" reference="equipment-types" label="equipmentTypeCode" link="show">
|
<ReferenceField
|
||||||
<TextField source="name" />
|
source="equipmentTypeCode"
|
||||||
|
reference="equipment-types"
|
||||||
|
label="Вид оборудования"
|
||||||
|
link="show"
|
||||||
|
>
|
||||||
|
<TextField source="code" />
|
||||||
</ReferenceField>
|
</ReferenceField>
|
||||||
<SelectField source="status" label="status" choices={statusChoices} />
|
<SelectField
|
||||||
<TextField source="location" label="location" />
|
source="status"
|
||||||
<DateField source="commissionedAt" label="commissionedAt" />
|
label="Текущий статус"
|
||||||
<NumberField source="totalEngineHours" label="totalEngineHours" />
|
choices={statusChoices}
|
||||||
<NumberField source="engineHoursSinceLastRepair" label="engineHoursSinceLastRepair" />
|
/>
|
||||||
<DateField source="lastRepairAt" label="lastRepairAt" />
|
<TextField
|
||||||
<TextField source="notes" label="notes" />
|
source="location"
|
||||||
|
label="Место эксплуатации / скважина / куст"
|
||||||
|
/>
|
||||||
|
<DateField source="commissionedAt" label="Дата ввода в эксплуатацию" />
|
||||||
|
<NumberField
|
||||||
|
source="totalEngineHours"
|
||||||
|
label="Общая наработка, моточасов"
|
||||||
|
/>
|
||||||
|
<NumberField
|
||||||
|
source="engineHoursSinceLastRepair"
|
||||||
|
label="Наработка с последнего ремонта, моточасов"
|
||||||
|
/>
|
||||||
|
<DateField source="lastRepairAt" label="Дата последнего ремонта" />
|
||||||
|
<TextField source="notes" label="Примечания" />
|
||||||
</Datagrid>
|
</Datagrid>
|
||||||
</List>
|
</List>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,38 +1,38 @@
|
|||||||
import { Create, SimpleForm, TextInput, SelectInput, ReferenceInput, AutocompleteInput } from 'react-admin';
|
import { Create, SimpleForm, TextInput, NumberInput, DateInput, SelectInput, ReferenceInput, AutocompleteInput } from 'react-admin';
|
||||||
|
|
||||||
const repairKindChoices = [
|
const repairKindChoices = [
|
||||||
{ id: 'TO', name: 'TO' },
|
{ id: 'TO', name: 'Техническое обслуживание' },
|
||||||
{ id: 'TR', name: 'TR' },
|
{ id: 'TR', name: 'Текущий ремонт' },
|
||||||
{ id: 'TRE', name: 'TRE' },
|
{ id: 'TRE', name: 'Текущий расширенный ремонт' },
|
||||||
{ id: 'KR', name: 'KR' },
|
{ id: 'KR', name: 'Капитальный ремонт' },
|
||||||
{ id: 'AR', name: 'AR' },
|
{ id: 'AR', name: 'Аварийный ремонт' },
|
||||||
{ id: 'MP', name: 'MP' },
|
{ id: 'MP', name: 'Метрологическая поверка' },
|
||||||
];
|
];
|
||||||
|
|
||||||
const statusChoices = [
|
const statusChoices = [
|
||||||
{ id: 'Draft', name: 'Draft' },
|
{ id: 'Draft', name: 'Черновик' },
|
||||||
{ id: 'Approved', name: 'Approved' },
|
{ id: 'Approved', name: 'Утверждена' },
|
||||||
{ id: 'InWork', name: 'InWork' },
|
{ id: 'InWork', name: 'В работе' },
|
||||||
{ id: 'Done', name: 'Done' },
|
{ id: 'Done', name: 'Выполнена' },
|
||||||
{ id: 'Cancelled', name: 'Cancelled' },
|
{ id: 'Cancelled', name: 'Отменена' },
|
||||||
];
|
];
|
||||||
|
|
||||||
export const RepairOrderCreate = () => (
|
export const RepairOrderCreate = () => (
|
||||||
<Create>
|
<Create>
|
||||||
<SimpleForm>
|
<SimpleForm>
|
||||||
<TextInput source="number" label="number" isRequired />
|
<TextInput source="number" label="Номер заявки" isRequired />
|
||||||
<ReferenceInput source="equipmentId" reference="equipment" label="equipmentId">
|
<ReferenceInput source="equipmentId" reference="equipment">
|
||||||
<AutocompleteInput optionText={(record) => record.code ? `${record.code} — ${record.name ?? record.code}` : (record.name ?? record.id)} filterToQuery={(searchText) => ({ q: searchText })} />
|
<AutocompleteInput label="Оборудование" optionText={(record) => record.inventoryNumber ? `${record.inventoryNumber} — ${record.name ?? record.inventoryNumber}` : (record.name ?? record.id)} filterToQuery={(searchText) => ({ q: searchText })} />
|
||||||
</ReferenceInput>
|
</ReferenceInput>
|
||||||
<SelectInput source="repairKind" label="repairKind" choices={repairKindChoices} emptyText="Не выбрано" />
|
<SelectInput source="repairKind" label="Вид ремонта" choices={repairKindChoices} emptyText="Не выбрано" />
|
||||||
<SelectInput source="status" label="status" choices={statusChoices} emptyText="Не выбрано" />
|
<SelectInput source="status" label="Статус" choices={statusChoices} emptyText="Не выбрано" />
|
||||||
<TextInput source="plannedAt" label="plannedAt" />
|
<DateInput source="plannedAt" label="Плановая дата начала" />
|
||||||
<TextInput source="startedAt" label="startedAt" />
|
<DateInput source="startedAt" label="Фактическая дата начала" />
|
||||||
<TextInput source="completedAt" label="completedAt" />
|
<DateInput source="completedAt" label="Фактическая дата завершения" />
|
||||||
<TextInput source="contractor" label="contractor" />
|
<TextInput source="contractor" label="Подрядная организация (если внешний ремонт)" />
|
||||||
<TextInput source="engineHoursAtRepair" label="engineHoursAtRepair" />
|
<NumberInput source="engineHoursAtRepair" label="Наработка на момент ремонта, моточасов" />
|
||||||
<TextInput source="description" label="description" />
|
<TextInput source="description" label="Описание работ / дефекта" />
|
||||||
<TextInput source="notes" label="notes" />
|
<TextInput source="notes" label="Примечания" />
|
||||||
</SimpleForm>
|
</SimpleForm>
|
||||||
</Create>
|
</Create>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,39 +1,39 @@
|
|||||||
import { Edit, SimpleForm, TextInput, SelectInput, ReferenceInput, AutocompleteInput } from 'react-admin';
|
import { Edit, SimpleForm, TextInput, NumberInput, DateInput, SelectInput, ReferenceInput, AutocompleteInput } from 'react-admin';
|
||||||
|
|
||||||
const repairKindChoices = [
|
const repairKindChoices = [
|
||||||
{ id: 'TO', name: 'TO' },
|
{ id: 'TO', name: 'Техническое обслуживание' },
|
||||||
{ id: 'TR', name: 'TR' },
|
{ id: 'TR', name: 'Текущий ремонт' },
|
||||||
{ id: 'TRE', name: 'TRE' },
|
{ id: 'TRE', name: 'Текущий расширенный ремонт' },
|
||||||
{ id: 'KR', name: 'KR' },
|
{ id: 'KR', name: 'Капитальный ремонт' },
|
||||||
{ id: 'AR', name: 'AR' },
|
{ id: 'AR', name: 'Аварийный ремонт' },
|
||||||
{ id: 'MP', name: 'MP' },
|
{ id: 'MP', name: 'Метрологическая поверка' },
|
||||||
];
|
];
|
||||||
|
|
||||||
const statusChoices = [
|
const statusChoices = [
|
||||||
{ id: 'Draft', name: 'Draft' },
|
{ id: 'Draft', name: 'Черновик' },
|
||||||
{ id: 'Approved', name: 'Approved' },
|
{ id: 'Approved', name: 'Утверждена' },
|
||||||
{ id: 'InWork', name: 'InWork' },
|
{ id: 'InWork', name: 'В работе' },
|
||||||
{ id: 'Done', name: 'Done' },
|
{ id: 'Done', name: 'Выполнена' },
|
||||||
{ id: 'Cancelled', name: 'Cancelled' },
|
{ id: 'Cancelled', name: 'Отменена' },
|
||||||
];
|
];
|
||||||
|
|
||||||
export const RepairOrderEdit = () => (
|
export const RepairOrderEdit = () => (
|
||||||
<Edit>
|
<Edit>
|
||||||
<SimpleForm>
|
<SimpleForm>
|
||||||
<TextInput source="id" label="id" disabled />
|
<TextInput source="id" label="id" disabled />
|
||||||
<TextInput source="number" label="number" isRequired />
|
<TextInput source="number" label="Номер заявки" isRequired />
|
||||||
<ReferenceInput source="equipmentId" reference="equipment" label="equipmentId">
|
<ReferenceInput source="equipmentId" reference="equipment">
|
||||||
<AutocompleteInput optionText={(record) => record.code ? `${record.code} — ${record.name ?? record.code}` : (record.name ?? record.id)} filterToQuery={(searchText) => ({ q: searchText })} />
|
<AutocompleteInput label="Оборудование" optionText={(record) => record.inventoryNumber ? `${record.inventoryNumber} — ${record.name ?? record.inventoryNumber}` : (record.name ?? record.id)} filterToQuery={(searchText) => ({ q: searchText })} />
|
||||||
</ReferenceInput>
|
</ReferenceInput>
|
||||||
<SelectInput source="repairKind" label="repairKind" choices={repairKindChoices} emptyText="Не выбрано" />
|
<SelectInput source="repairKind" label="Вид ремонта" choices={repairKindChoices} emptyText="Не выбрано" />
|
||||||
<SelectInput source="status" label="status" choices={statusChoices} emptyText="Не выбрано" />
|
<SelectInput source="status" label="Статус" choices={statusChoices} emptyText="Не выбрано" />
|
||||||
<TextInput source="plannedAt" label="plannedAt" />
|
<DateInput source="plannedAt" label="Плановая дата начала" />
|
||||||
<TextInput source="startedAt" label="startedAt" />
|
<DateInput source="startedAt" label="Фактическая дата начала" />
|
||||||
<TextInput source="completedAt" label="completedAt" />
|
<DateInput source="completedAt" label="Фактическая дата завершения" />
|
||||||
<TextInput source="contractor" label="contractor" />
|
<TextInput source="contractor" label="Подрядная организация (если внешний ремонт)" />
|
||||||
<TextInput source="engineHoursAtRepair" label="engineHoursAtRepair" />
|
<NumberInput source="engineHoursAtRepair" label="Наработка на момент ремонта, моточасов" />
|
||||||
<TextInput source="description" label="description" />
|
<TextInput source="description" label="Описание работ / дефекта" />
|
||||||
<TextInput source="notes" label="notes" />
|
<TextInput source="notes" label="Примечания" />
|
||||||
</SimpleForm>
|
</SimpleForm>
|
||||||
</Edit>
|
</Edit>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -18,33 +18,33 @@ import {
|
|||||||
} from 'react-admin';
|
} from 'react-admin';
|
||||||
|
|
||||||
const repairKindChoices = [
|
const repairKindChoices = [
|
||||||
{ id: 'TO', name: 'TO' },
|
{ id: 'TO', name: 'Техническое обслуживание' },
|
||||||
{ id: 'TR', name: 'TR' },
|
{ id: 'TR', name: 'Текущий ремонт' },
|
||||||
{ id: 'TRE', name: 'TRE' },
|
{ id: 'TRE', name: 'Текущий расширенный ремонт' },
|
||||||
{ id: 'KR', name: 'KR' },
|
{ id: 'KR', name: 'Капитальный ремонт' },
|
||||||
{ id: 'AR', name: 'AR' },
|
{ id: 'AR', name: 'Аварийный ремонт' },
|
||||||
{ id: 'MP', name: 'MP' },
|
{ id: 'MP', name: 'Метрологическая поверка' },
|
||||||
];
|
];
|
||||||
|
|
||||||
const statusChoices = [
|
const statusChoices = [
|
||||||
{ id: 'Draft', name: 'Draft' },
|
{ id: 'Draft', name: 'Черновик' },
|
||||||
{ id: 'Approved', name: 'Approved' },
|
{ id: 'Approved', name: 'Утверждена' },
|
||||||
{ id: 'InWork', name: 'InWork' },
|
{ id: 'InWork', name: 'В работе' },
|
||||||
{ id: 'Done', name: 'Done' },
|
{ id: 'Done', name: 'Выполнена' },
|
||||||
{ id: 'Cancelled', name: 'Cancelled' },
|
{ id: 'Cancelled', name: 'Отменена' },
|
||||||
];
|
];
|
||||||
|
|
||||||
const repairOrderFilters = [
|
const repairOrderFilters = [
|
||||||
<TextInput key="q" source="q" label="Поиск" alwaysOn />,
|
<TextInput key="q" source="q" label="Поиск" alwaysOn />,
|
||||||
<TextInput key="number" source="number" label="number" />,
|
<TextInput key="number" source="number" label="Номер заявки" />,
|
||||||
<ReferenceInput key="equipmentId" source="equipmentId" reference="equipment" label="equipmentId">
|
<ReferenceInput key="equipmentId" source="equipmentId" reference="equipment" label="Оборудование">
|
||||||
<AutocompleteInput optionText={(record) => record.code ? `${record.code} — ${record.name ?? record.code}` : (record.name ?? record.id)} filterToQuery={(searchText) => ({ q: searchText })} />
|
<AutocompleteInput optionText={(record) => record.inventoryNumber ? `${record.inventoryNumber} — ${record.name ?? record.inventoryNumber}` : (record.name ?? record.id)} filterToQuery={(searchText) => ({ q: searchText })} />
|
||||||
</ReferenceInput>,
|
</ReferenceInput>,
|
||||||
<SelectInput key="repairKind" source="repairKind" label="repairKind" choices={repairKindChoices} emptyText="Все" />,
|
<SelectInput key="repairKind" source="repairKind" label="Вид ремонта" choices={repairKindChoices} emptyText="Все" />,
|
||||||
<SelectArrayInput key="status" source="status" label="status" choices={statusChoices} />,
|
<SelectArrayInput key="status" source="status" label="Статус" choices={statusChoices} />,
|
||||||
<TextInput key="contractor" source="contractor" label="contractor" />,
|
<TextInput key="contractor" source="contractor" label="Подрядная организация (если внешний ремонт)" />,
|
||||||
<TextInput key="description" source="description" label="description" />,
|
<TextInput key="description" source="description" label="Описание работ / дефекта" />,
|
||||||
<TextInput key="notes" source="notes" label="notes" />
|
<TextInput key="notes" source="notes" label="Примечания" />
|
||||||
];
|
];
|
||||||
|
|
||||||
const RepairOrderListActions = () => (
|
const RepairOrderListActions = () => (
|
||||||
@@ -56,22 +56,22 @@ const RepairOrderListActions = () => (
|
|||||||
);
|
);
|
||||||
|
|
||||||
export const RepairOrderList = () => (
|
export const RepairOrderList = () => (
|
||||||
<List actions={<RepairOrderListActions />} filters={repairOrderFilters} sort={{ field: 'id', order: 'ASC' }}>
|
<List actions={<RepairOrderListActions />} filters={repairOrderFilters} sort={{ field: 'number', order: 'ASC' }}>
|
||||||
<Datagrid rowClick="show">
|
<Datagrid rowClick="show">
|
||||||
<TextField source="id" label="id" />
|
<TextField source="id" label="id" />
|
||||||
<TextField source="number" label="number" />
|
<TextField source="number" label="Номер заявки" />
|
||||||
<ReferenceField source="equipmentId" reference="equipment" label="equipmentId" link="show">
|
<ReferenceField source="equipmentId" reference="equipment" label="Оборудование" link="show">
|
||||||
<TextField source="name" />
|
<TextField source="inventoryNumber" />
|
||||||
</ReferenceField>
|
</ReferenceField>
|
||||||
<SelectField source="repairKind" label="repairKind" choices={repairKindChoices} />
|
<SelectField source="repairKind" label="Вид ремонта" choices={repairKindChoices} />
|
||||||
<SelectField source="status" label="status" choices={statusChoices} />
|
<SelectField source="status" label="Статус" choices={statusChoices} />
|
||||||
<DateField source="plannedAt" label="plannedAt" />
|
<DateField source="plannedAt" label="Плановая дата начала" />
|
||||||
<DateField source="startedAt" label="startedAt" />
|
<DateField source="startedAt" label="Фактическая дата начала" />
|
||||||
<DateField source="completedAt" label="completedAt" />
|
<DateField source="completedAt" label="Фактическая дата завершения" />
|
||||||
<TextField source="contractor" label="contractor" />
|
<TextField source="contractor" label="Подрядная организация (если внешний ремонт)" />
|
||||||
<NumberField source="engineHoursAtRepair" label="engineHoursAtRepair" />
|
<NumberField source="engineHoursAtRepair" label="Наработка на момент ремонта, моточасов" />
|
||||||
<TextField source="description" label="description" />
|
<TextField source="description" label="Описание работ / дефекта" />
|
||||||
<TextField source="notes" label="notes" />
|
<TextField source="notes" label="Примечания" />
|
||||||
</Datagrid>
|
</Datagrid>
|
||||||
</List>
|
</List>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ services:
|
|||||||
POSTGRES_PASSWORD: postgres
|
POSTGRES_PASSWORD: postgres
|
||||||
POSTGRES_DB: toir
|
POSTGRES_DB: toir
|
||||||
ports:
|
ports:
|
||||||
- "5433:5432"
|
- "5432:5432"
|
||||||
volumes:
|
volumes:
|
||||||
- postgres_data:/var/lib/postgresql/data
|
- postgres_data:/var/lib/postgresql/data
|
||||||
|
|
||||||
|
|||||||
@@ -74,14 +74,20 @@ function parseBlocks(text, kind) {
|
|||||||
|
|
||||||
function parseEnum(body) {
|
function parseEnum(body) {
|
||||||
const values = [];
|
const values = [];
|
||||||
const re = /^\s*value\s+([A-Za-z_][A-Za-z0-9_]*)\s*\{/gm;
|
const labels = {};
|
||||||
|
const re = /value\s+([A-Za-z_][A-Za-z0-9_]*)\s*\{([\s\S]*?)\}/gm;
|
||||||
let m;
|
let m;
|
||||||
while ((m = re.exec(body))) values.push(m[1]);
|
while ((m = re.exec(body))) {
|
||||||
return { values };
|
values.push(m[1]);
|
||||||
|
const label = (m[2].match(/label\s+"([^"]+)"/m) || [])[1];
|
||||||
|
labels[m[1]] = label || m[1];
|
||||||
|
}
|
||||||
|
return { values, labels };
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseEntity(body) {
|
function parseEntity(body) {
|
||||||
const attrs = [];
|
const attrs = [];
|
||||||
|
const entityLabel = (body.match(/description\s+"([^"]+)"/m) || [])[1];
|
||||||
const lines = body.split(/\r?\n/);
|
const lines = body.split(/\r?\n/);
|
||||||
for (let i = 0; i < lines.length; i++) {
|
for (let i = 0; i < lines.length; i++) {
|
||||||
const m = lines[i].match(/^\s*attribute\s+([A-Za-z_][A-Za-z0-9_]*)\s*\{\s*$/);
|
const m = lines[i].match(/^\s*attribute\s+([A-Za-z_][A-Za-z0-9_]*)\s*\{\s*$/);
|
||||||
@@ -106,10 +112,12 @@ function parseEntity(body) {
|
|||||||
const isPrimary = /^\s*key primary\s*;/m.test(abody);
|
const isPrimary = /^\s*key primary\s*;/m.test(abody);
|
||||||
const defaultValue = (abody.match(/^\s*default\s+([A-Za-z_][A-Za-z0-9_]*)\s*;/m) || [])[1];
|
const defaultValue = (abody.match(/^\s*default\s+([A-Za-z_][A-Za-z0-9_]*)\s*;/m) || [])[1];
|
||||||
const foreignRel = (abody.match(/relates\s+([A-Za-z_][A-Za-z0-9_]*)\.([A-Za-z_][A-Za-z0-9_]*)\s*;/m) || []).slice(1);
|
const foreignRel = (abody.match(/relates\s+([A-Za-z_][A-Za-z0-9_]*)\.([A-Za-z_][A-Za-z0-9_]*)\s*;/m) || []).slice(1);
|
||||||
|
const description = (abody.match(/description\s+"([^"]+)"/m) || [])[1];
|
||||||
|
|
||||||
attrs.push({
|
attrs.push({
|
||||||
name,
|
name,
|
||||||
type,
|
type,
|
||||||
|
label: description || name,
|
||||||
isRequired,
|
isRequired,
|
||||||
isUnique,
|
isUnique,
|
||||||
isPrimary,
|
isPrimary,
|
||||||
@@ -122,7 +130,7 @@ function parseEntity(body) {
|
|||||||
const pk = attrs.find((a) => a.isPrimary);
|
const pk = attrs.find((a) => a.isPrimary);
|
||||||
if (!pk) throw new Error('Entity missing primary key attribute');
|
if (!pk) throw new Error('Entity missing primary key attribute');
|
||||||
|
|
||||||
return { attributes: attrs, primaryKey: pk.name };
|
return { attributes: attrs, primaryKey: pk.name, label: entityLabel };
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseDomainDSL(dslText) {
|
function parseDomainDSL(dslText) {
|
||||||
@@ -138,6 +146,45 @@ function parseDomainDSL(dslText) {
|
|||||||
return { enums, entities };
|
return { enums, entities };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getEntityAttrNames(entity) {
|
||||||
|
return new Set(entity.attributes.map((a) => a.name));
|
||||||
|
}
|
||||||
|
|
||||||
|
function getBestSortField(entity, pk) {
|
||||||
|
const attrs = getEntityAttrNames(entity);
|
||||||
|
if (attrs.has('inventoryNumber')) return 'inventoryNumber';
|
||||||
|
if (attrs.has('number')) return 'number';
|
||||||
|
if (attrs.has('code')) return 'code';
|
||||||
|
if (attrs.has('name')) return 'name';
|
||||||
|
return pk;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getReferenceDisplayExpr(foreignEntity) {
|
||||||
|
const attrs = getEntityAttrNames(foreignEntity);
|
||||||
|
if (attrs.has('inventoryNumber')) {
|
||||||
|
return "(record) => record.inventoryNumber ? `${record.inventoryNumber} — ${record.name ?? record.inventoryNumber}` : (record.name ?? record.id)";
|
||||||
|
}
|
||||||
|
if (attrs.has('code')) {
|
||||||
|
return "(record) => record.code ? `${record.code} — ${record.name ?? record.code}` : (record.name ?? record.id)";
|
||||||
|
}
|
||||||
|
if (attrs.has('number')) {
|
||||||
|
return "(record) => record.number ? `${record.number} — ${record.name ?? record.number}` : (record.name ?? record.id)";
|
||||||
|
}
|
||||||
|
if (attrs.has('name')) {
|
||||||
|
return "(record) => record.name ?? record.id";
|
||||||
|
}
|
||||||
|
return "(record) => record.id";
|
||||||
|
}
|
||||||
|
|
||||||
|
function getAttributeLabel(attr, allEntities) {
|
||||||
|
if (attr.label && attr.label !== attr.name) return attr.label;
|
||||||
|
if (attr.name === 'status') return 'Статус';
|
||||||
|
if (attr.name === 'equipmentId') return 'Оборудование';
|
||||||
|
if (attr.name === 'equipmentTypeCode') return 'Вид оборудования';
|
||||||
|
if (attr.foreign) return allEntities[attr.foreign.entity]?.label || attr.name;
|
||||||
|
return attr.name;
|
||||||
|
}
|
||||||
|
|
||||||
function prismaScalarType(dslType) {
|
function prismaScalarType(dslType) {
|
||||||
switch (dslType) {
|
switch (dslType) {
|
||||||
case 'string':
|
case 'string':
|
||||||
@@ -281,23 +328,44 @@ function renderBackendModule(entityName, entity, resourceName, pk) {
|
|||||||
if (pk !== 'id') updateDtoLines.push(` id?: string;`);
|
if (pk !== 'id') updateDtoLines.push(` id?: string;`);
|
||||||
for (const a of entity.attributes) {
|
for (const a of entity.attributes) {
|
||||||
if (pk !== 'id' && a.name === 'id') continue;
|
if (pk !== 'id' && a.name === 'id') continue;
|
||||||
updateDtoLines.push(` ${a.name}?: ${dtoType(a)} | null;`);
|
updateDtoLines.push(` ${a.name}?: ${dtoType(a)};`);
|
||||||
}
|
}
|
||||||
updateDtoLines.push('}');
|
updateDtoLines.push('}');
|
||||||
|
|
||||||
const controller = `import { Controller, Get, Post, Patch, Delete, Param, Body, Query, Res } from '@nestjs/common';\nimport { Response } from 'express';\nimport { ${serviceName} } from './${folder}.service';\nimport { Create${className}Dto } from './dto/create-${folder}.dto';\nimport { Update${className}Dto } from './dto/update-${folder}.dto';\n\n@Controller('${resourceName}')\nexport class ${controllerName} {\n constructor(private readonly service: ${serviceName}) {}\n\n @Get()\n async findAll(@Query() query: any, @Res() res: Response) {\n const result = await this.service.findAll(query);\n res.set('Content-Range', \`${resourceName} \${query._start || 0}-\${query._end || result.total}/\${result.total}\`);\n res.set('Access-Control-Expose-Headers', 'Content-Range');\n return res.json(result.data);\n }\n\n @Get(':${pk}')\n findOne(@Param('${pk}') id: string) {\n return this.service.findOne(id);\n }\n\n @Post()\n create(@Body() dto: Create${className}Dto) {\n return this.service.create(dto);\n }\n\n @Patch(':${pk}')\n update(@Param('${pk}') id: string, @Body() dto: Update${className}Dto) {\n return this.service.update(id, dto);\n }\n\n @Delete(':${pk}')\n remove(@Param('${pk}') id: string) {\n return this.service.remove(id);\n }\n}\n`;
|
const controller = `import { Controller, Get, Post, Patch, Delete, Param, Body, Query, Res } from '@nestjs/common';\nimport { Response } from 'express';\nimport { ${serviceName} } from './${folder}.service';\nimport { Create${className}Dto } from './dto/create-${folder}.dto';\nimport { Update${className}Dto } from './dto/update-${folder}.dto';\n\n@Controller('${resourceName}')\nexport class ${controllerName} {\n constructor(private readonly service: ${serviceName}) {}\n\n @Get()\n async findAll(@Query() query: any, @Res() res: Response) {\n const result = await this.service.findAll(query);\n res.set('Content-Range', \`${resourceName} \${query._start || 0}-\${query._end || result.total}/\${result.total}\`);\n res.set('Access-Control-Expose-Headers', 'Content-Range');\n return res.json(result.data);\n }\n\n @Get(':${pk}')\n findOne(@Param('${pk}') id: string) {\n return this.service.findOne(id);\n }\n\n @Post()\n create(@Body() dto: Create${className}Dto) {\n return this.service.create(dto);\n }\n\n @Patch(':${pk}')\n update(@Param('${pk}') id: string, @Body() dto: Update${className}Dto) {\n return this.service.update(id, dto);\n }\n\n @Delete(':${pk}')\n remove(@Param('${pk}') id: string) {\n return this.service.remove(id);\n }\n}\n`;
|
||||||
|
|
||||||
const service = `import { Injectable } from '@nestjs/common';\nimport { Prisma } from '@prisma/client';\nimport { PrismaService } from '../../prisma/prisma.service';\nimport { Create${className}Dto } from './dto/create-${folder}.dto';\nimport { Update${className}Dto } from './dto/update-${folder}.dto';\n\n@Injectable()\nexport class ${serviceName} {\n constructor(private readonly prisma: PrismaService) {}\n\n async findAll(query: { _start?: string; _end?: string; _sort?: string; _order?: string; [key: string]: any }) {\n const start = parseInt(query._start) || 0;\n const end = parseInt(query._end) || 10;\n const take = end - start;\n const skip = start;\n const sortField = query._sort || '${pk}';\n const sortOrder = (query._order || 'ASC').toLowerCase() as 'asc' | 'desc';\n\n const where: any = {};\n\n if (query.q) {\n const q = String(query.q);\n const ors: any[] = [];\n ${entity.attributes
|
const service = `import { Injectable } from '@nestjs/common';\nimport { Prisma } from '@prisma/client';\nimport { PrismaService } from '../../prisma/prisma.service';\nimport { Create${className}Dto } from './dto/create-${folder}.dto';\nimport { Update${className}Dto } from './dto/update-${folder}.dto';\n\nfunction serializeRecord(record: any) {\n return {\n ...record,\n${entity.attributes
|
||||||
|
.filter((a) => a.type === 'decimal')
|
||||||
|
.map((a) => ` ${a.name}: record.${a.name}?.toString() ?? null,`)
|
||||||
|
.join('\n')}\n${entity.attributes
|
||||||
|
.filter((a) => a.type === 'date')
|
||||||
|
.map((a) => ` ${a.name}: record.${a.name}?.toISOString() ?? null,`)
|
||||||
|
.join('\n')}\n };\n}\n\n@Injectable()\nexport class ${serviceName} {\n constructor(private readonly prisma: PrismaService) {}\n\n async findAll(query: { _start?: string; _end?: string; _sort?: string; _order?: string; [key: string]: any }) {\n const start = parseInt(query._start) || 0;\n const end = parseInt(query._end) || 10;\n const take = end - start;\n const skip = start;\n const sortField = query._sort || '${getBestSortField(entity, pk)}';\n const sortOrder = (query._order || 'ASC').toLowerCase() as 'asc' | 'desc';\n\n const where: any = {};\n\n if (query.q) {\n const q = String(query.q);\n const ors: any[] = [];\n ${entity.attributes
|
||||||
.filter((a) => ['string', 'text'].includes(a.type))
|
.filter((a) => ['string', 'text'].includes(a.type))
|
||||||
.slice(0, 6)
|
.slice(0, 6)
|
||||||
.map((a) => `ors.push({ ${a.name}: { contains: q, mode: 'insensitive' } });`)
|
.map((a) => `ors.push({ ${a.name}: { contains: q, mode: 'insensitive' } });`)
|
||||||
.join('\n ')}\n if (ors.length) where.OR = ors;\n }\n\n ${entity.attributes
|
.join('\n ')}\n if (ors.length) where.OR = ors;\n }\n\n ${entity.attributes
|
||||||
.filter((a) => ['string', 'text'].includes(a.type))
|
.filter((a) => ['string', 'text'].includes(a.type) && !a.foreign)
|
||||||
.map((a) => `if (query.${a.name}) where.${a.name} = { contains: query.${a.name}, mode: 'insensitive' };`)
|
.map((a) => `if (query.${a.name}) where.${a.name} = { contains: query.${a.name}, mode: 'insensitive' };`)
|
||||||
|
.join('\n ')}\n\n ${entity.attributes
|
||||||
|
.filter((a) => a.foreign)
|
||||||
|
.map((a) => `if (query.${a.name}) where.${a.name} = query.${a.name};`)
|
||||||
.join('\n ')}\n\n // Enum multi-value support (e.g. status=A&status=B)\n ${entity.attributes
|
.join('\n ')}\n\n // Enum multi-value support (e.g. status=A&status=B)\n ${entity.attributes
|
||||||
.filter((a) => !['string', 'text', 'uuid', 'integer', 'decimal', 'date'].includes(a.type))
|
.filter((a) => !['string', 'text', 'uuid', 'integer', 'decimal', 'date'].includes(a.type))
|
||||||
.map((a) => `if (query.${a.name}) { const vals = Array.isArray(query.${a.name}) ? query.${a.name} : [query.${a.name}]; where.${a.name} = vals.length > 1 ? { in: vals } : vals[0]; }`)
|
.map((a) => `if (query.${a.name}) { const vals = Array.isArray(query.${a.name}) ? query.${a.name} : [query.${a.name}]; where.${a.name} = vals.length > 1 ? { in: vals } : vals[0]; }`)
|
||||||
.join('\n ')}\n\n if (query.id) {\n const ids = Array.isArray(query.id) ? query.id : [query.id];\n where.${pk} = { in: ids };\n }\n\n const [data, total] = await Promise.all([\n this.prisma.${lowerFirst(className)}.findMany({ where, skip, take, orderBy: { [sortField]: sortOrder } }),\n this.prisma.${lowerFirst(className)}.count({ where }),\n ]);\n\n const mapped = ${pk === 'id' ? 'data' : `data.map((r: any) => ({ id: r.${pk}, ...r }))`};\n return { data: mapped, total };\n }\n\n async findOne(id: string) {\n const record = await this.prisma.${lowerFirst(className)}.findUniqueOrThrow({ where: { ${pk}: id } as any });\n return ${pk === 'id' ? 'record' : `{ id: (record as any).${pk}, ...record }`};\n }\n\n async create(dto: Create${className}Dto) {\n const record = await this.prisma.${lowerFirst(className)}.create({ data: dto as any });\n return ${pk === 'id' ? 'record' : `{ id: (record as any).${pk}, ...record }`};\n }\n\n async update(id: string, dto: Update${className}Dto) {\n const data: any = { ...(dto as any) };\n delete data.id;\n delete data.${pk};\n const record = await this.prisma.${lowerFirst(className)}.update({ where: { ${pk}: id } as any, data });\n return ${pk === 'id' ? 'record' : `{ id: (record as any).${pk}, ...record }`};\n }\n\n async remove(id: string) {\n const record = await this.prisma.${lowerFirst(className)}.delete({ where: { ${pk}: id } as any });\n return ${pk === 'id' ? 'record' : `{ id: (record as any).${pk}, ...record }`};\n }\n}\n`;
|
.join('\n ')}\n\n if (query.id) {\n const ids = Array.isArray(query.id) ? query.id : [query.id];\n where.${pk} = { in: ids };\n }\n\n const [data, total] = await Promise.all([\n this.prisma.${lowerFirst(className)}.findMany({ where, skip, take, orderBy: { [sortField]: sortOrder } }),\n this.prisma.${lowerFirst(className)}.count({ where }),\n ]);\n\n const mapped = ${pk === 'id' ? 'data.map(serializeRecord)' : `data.map((r: any) => ({ id: r.${pk}, ...serializeRecord(r) }))`};\n return { data: mapped, total };\n }\n\n async findOne(id: string) {\n const record = await this.prisma.${lowerFirst(className)}.findUniqueOrThrow({ where: { ${pk}: id } as any });\n return ${pk === 'id' ? 'serializeRecord(record)' : `{ id: (record as any).${pk}, ...serializeRecord(record) }`};\n }\n\n async create(dto: Create${className}Dto) {\n const data: any = { ...(dto as any) };\n${entity.attributes
|
||||||
|
.filter((a) => a.type === 'date')
|
||||||
|
.map((a) => ` if (data.${a.name}) data.${a.name} = new Date(data.${a.name});`)
|
||||||
|
.join('\n')}\n${entity.attributes
|
||||||
|
.filter((a) => a.type === 'decimal')
|
||||||
|
.map((a) => ` if (data.${a.name}) data.${a.name} = new Prisma.Decimal(data.${a.name});`)
|
||||||
|
.join('\n')}\n\n const record = await this.prisma.${lowerFirst(className)}.create({ data });\n return ${pk === 'id' ? 'serializeRecord(record)' : `{ id: (record as any).${pk}, ...serializeRecord(record) }`};\n }\n\n async update(id: string, dto: Update${className}Dto) {\n const data: any = { ...(dto as any) };\n delete data.id;\n delete data.${pk};\n${entity.attributes
|
||||||
|
.filter((a) => a.type === 'date')
|
||||||
|
.map((a) => ` if (data.${a.name}) data.${a.name} = new Date(data.${a.name});`)
|
||||||
|
.join('\n')}\n${entity.attributes
|
||||||
|
.filter((a) => a.type === 'decimal')
|
||||||
|
.map((a) => ` if (data.${a.name} !== undefined && data.${a.name} !== null) data.${a.name} = new Prisma.Decimal(data.${a.name});`)
|
||||||
|
.join('\n')}\n\n const record = await this.prisma.${lowerFirst(className)}.update({ where: { ${pk}: id } as any, data });\n return ${pk === 'id' ? 'serializeRecord(record)' : `{ id: (record as any).${pk}, ...serializeRecord(record) }`};\n }\n\n async remove(id: string) {\n const record = await this.prisma.${lowerFirst(className)}.delete({ where: { ${pk}: id } as any });\n return ${pk === 'id' ? 'serializeRecord(record)' : `{ id: (record as any).${pk}, ...serializeRecord(record) }`};\n }\n}\n`;
|
||||||
|
|
||||||
const mod = `import { Module } from '@nestjs/common';\nimport { ${controllerName} } from './${folder}.controller';\nimport { ${serviceName} } from './${folder}.service';\n\n@Module({\n controllers: [${controllerName}],\n providers: [${serviceName}],\n})\nexport class ${moduleName} {}\n`;
|
const mod = `import { Module } from '@nestjs/common';\nimport { ${controllerName} } from './${folder}.controller';\nimport { ${serviceName} } from './${folder}.service';\n\n@Module({\n controllers: [${controllerName}],\n providers: [${serviceName}],\n})\nexport class ${moduleName} {}\n`;
|
||||||
|
|
||||||
@@ -315,7 +383,7 @@ function renderBackendModule(entityName, entity, resourceName, pk) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderFrontendResource(entityName, entity, resourceName, pk, enums) {
|
function renderFrontendResource(entityName, entity, resourceName, pk, enums, allEntities) {
|
||||||
const folder = toKebab(entityName);
|
const folder = toKebab(entityName);
|
||||||
const className = entityName;
|
const className = entityName;
|
||||||
const enumAttrs = entity.attributes.filter(
|
const enumAttrs = entity.attributes.filter(
|
||||||
@@ -325,6 +393,7 @@ function renderFrontendResource(entityName, entity, resourceName, pk, enums) {
|
|||||||
|
|
||||||
const identBase = toIdentifierFromKebab(folder);
|
const identBase = toIdentifierFromKebab(folder);
|
||||||
const filtersIdent = `${identBase}Filters`;
|
const filtersIdent = `${identBase}Filters`;
|
||||||
|
const sortField = getBestSortField(entity, pk);
|
||||||
|
|
||||||
const hasNumber = entity.attributes.some((a) => ['integer', 'decimal'].includes(a.type));
|
const hasNumber = entity.attributes.some((a) => ['integer', 'decimal'].includes(a.type));
|
||||||
const hasDate = entity.attributes.some((a) => a.type === 'date');
|
const hasDate = entity.attributes.some((a) => a.type === 'date');
|
||||||
@@ -357,14 +426,15 @@ function renderFrontendResource(entityName, entity, resourceName, pk, enums) {
|
|||||||
for (const a of enumAttrs) {
|
for (const a of enumAttrs) {
|
||||||
const enumName = a.type;
|
const enumName = a.type;
|
||||||
const values = enums?.[enumName]?.values ?? [];
|
const values = enums?.[enumName]?.values ?? [];
|
||||||
|
const labels = enums?.[enumName]?.labels ?? {};
|
||||||
const constName = `${a.name}Choices`;
|
const constName = `${a.name}Choices`;
|
||||||
if (a.name === 'status') {
|
if (a.name === 'status') {
|
||||||
choiceConsts.push(
|
choiceConsts.push(
|
||||||
`const statusChoices = [\n${values.map((v) => ` { id: '${v}', name: '${v}' },`).join('\n')}\n];\n`
|
`const statusChoices = [\n${values.map((v) => ` { id: '${v}', name: '${labels[v] ?? v}' },`).join('\n')}\n];\n`
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
choiceConsts.push(
|
choiceConsts.push(
|
||||||
`const ${constName} = [\n${values.map((v) => ` { id: '${v}', name: '${v}' },`).join('\n')}\n];\n`
|
`const ${constName} = [\n${values.map((v) => ` { id: '${v}', name: '${labels[v] ?? v}' },`).join('\n')}\n];\n`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -375,66 +445,82 @@ function renderFrontendResource(entityName, entity, resourceName, pk, enums) {
|
|||||||
filterInputs.push(`<TextInput key="q" source="q" label="Поиск" alwaysOn />`);
|
filterInputs.push(`<TextInput key="q" source="q" label="Поиск" alwaysOn />`);
|
||||||
}
|
}
|
||||||
for (const a of entity.attributes) {
|
for (const a of entity.attributes) {
|
||||||
|
const label = getAttributeLabel(a, allEntities);
|
||||||
if (a.name === pk) continue;
|
if (a.name === pk) continue;
|
||||||
if (a.foreign) {
|
if (a.foreign) {
|
||||||
|
const referenceDisplay = getReferenceDisplayExpr(allEntities[a.foreign.entity]);
|
||||||
filterInputs.push(
|
filterInputs.push(
|
||||||
`<ReferenceInput key="${a.name}" source="${a.name}" reference="${pluralize(toKebab(a.foreign.entity))}" label="${a.name}">\n <AutocompleteInput optionText={(record) => record.code ? \`\${record.code} — \${record.name ?? record.code}\` : (record.name ?? record.id)} filterToQuery={(searchText) => ({ q: searchText })} />\n </ReferenceInput>`
|
`<ReferenceInput key="${a.name}" source="${a.name}" reference="${pluralize(toKebab(a.foreign.entity))}" label="${label}">\n <AutocompleteInput optionText={${referenceDisplay}} filterToQuery={(searchText) => ({ q: searchText })} />\n </ReferenceInput>`
|
||||||
);
|
);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (['string', 'text', 'uuid'].includes(a.type)) {
|
if (['string', 'text', 'uuid'].includes(a.type)) {
|
||||||
filterInputs.push(`<TextInput key="${a.name}" source="${a.name}" label="${a.name}" />`);
|
filterInputs.push(`<TextInput key="${a.name}" source="${a.name}" label="${label}" />`);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (['integer', 'decimal'].includes(a.type)) continue;
|
if (['integer', 'decimal'].includes(a.type)) continue;
|
||||||
if (a.type === 'date') continue;
|
if (a.type === 'date') continue;
|
||||||
// enum
|
// enum
|
||||||
if (a.name === 'status') {
|
if (a.name === 'status') {
|
||||||
filterInputs.push(`<SelectArrayInput key="${a.name}" source="${a.name}" label="${a.name}" choices={statusChoices} />`);
|
filterInputs.push(`<SelectArrayInput key="${a.name}" source="${a.name}" label="${label}" choices={statusChoices} />`);
|
||||||
} else {
|
} else {
|
||||||
filterInputs.push(`<SelectInput key="${a.name}" source="${a.name}" label="${a.name}" choices={${a.name}Choices} emptyText="Все" />`);
|
filterInputs.push(`<SelectInput key="${a.name}" source="${a.name}" label="${label}" choices={${a.name}Choices} emptyText="Все" />`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const listFields = [];
|
const listFields = [];
|
||||||
for (const a of entity.attributes) {
|
for (const a of entity.attributes) {
|
||||||
|
const label = getAttributeLabel(a, allEntities);
|
||||||
if (a.foreign) {
|
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';
|
||||||
listFields.push(
|
listFields.push(
|
||||||
`<ReferenceField source="${a.name}" reference="${pluralize(toKebab(a.foreign.entity))}" label="${a.name}" link="show">\n <TextField source="name" />\n </ReferenceField>`
|
`<ReferenceField source="${a.name}" reference="${pluralize(toKebab(a.foreign.entity))}" label="${label}" link="show">\n <TextField source="${fieldSource}" />\n </ReferenceField>`
|
||||||
);
|
);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (a.type === 'date') {
|
if (a.type === 'date') {
|
||||||
listFields.push(`<DateField source="${a.name}" label="${a.name}" />`);
|
listFields.push(`<DateField source="${a.name}" label="${label}" />`);
|
||||||
} else if (['integer', 'decimal'].includes(a.type)) {
|
} else if (['integer', 'decimal'].includes(a.type)) {
|
||||||
listFields.push(`<NumberField source="${a.name}" label="${a.name}" />`);
|
listFields.push(`<NumberField source="${a.name}" label="${label}" />`);
|
||||||
} else if (!['string', 'text', 'uuid', 'integer', 'decimal', 'date'].includes(a.type)) {
|
} else if (!['string', 'text', 'uuid', 'integer', 'decimal', 'date'].includes(a.type)) {
|
||||||
listFields.push(`<SelectField source="${a.name}" label="${a.name}" choices={${a.name === 'status' ? 'statusChoices' : `${a.name}Choices`}} />`);
|
listFields.push(`<SelectField source="${a.name}" label="${label}" choices={${a.name === 'status' ? 'statusChoices' : `${a.name}Choices`}} />`);
|
||||||
} else {
|
} else {
|
||||||
listFields.push(`<TextField source="${a.name}" label="${a.name}" />`);
|
listFields.push(`<TextField source="${a.name}" label="${label}" />`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const list = `import {\n ${listImports.join(',\n ')}\n} from 'react-admin';\n\n${choiceConsts.join('\n')}\nconst ${filtersIdent} = [\n ${filterInputs.join(',\n ')}\n];\n\nconst ${className}ListActions = () => (\n <TopToolbar>\n <FilterButton filters={${filtersIdent}} />\n <CreateButton />\n <ExportButton />\n </TopToolbar>\n);\n\nexport const ${className}List = () => (\n <List actions={<${className}ListActions />} filters={${filtersIdent}} sort={{ field: '${pk}', order: 'ASC' }}>\n <Datagrid rowClick=\"show\">\n ${listFields.join('\n ')}\n </Datagrid>\n </List>\n);\n`;
|
const list = `import {\n ${listImports.join(',\n ')}\n} from 'react-admin';\n\n${choiceConsts.join('\n')}\nconst ${filtersIdent} = [\n ${filterInputs.join(',\n ')}\n];\n\nconst ${className}ListActions = () => (\n <TopToolbar>\n <FilterButton filters={${filtersIdent}} />\n <CreateButton />\n <ExportButton />\n </TopToolbar>\n);\n\nexport const ${className}List = () => (\n <List actions={<${className}ListActions />} filters={${filtersIdent}} sort={{ field: '${sortField}', order: 'ASC' }}>\n <Datagrid rowClick=\"show\">\n ${listFields.join('\n ')}\n </Datagrid>\n </List>\n);\n`;
|
||||||
|
|
||||||
const formField = (a, mode) => {
|
const formField = (a, mode) => {
|
||||||
|
const label = getAttributeLabel(a, allEntities);
|
||||||
if (a.isPrimary && mode === 'create' && a.type === 'uuid') return null;
|
if (a.isPrimary && mode === 'create' && a.type === 'uuid') return null;
|
||||||
if (a.isPrimary && mode === 'edit') {
|
if (a.isPrimary && mode === 'edit') {
|
||||||
return `<TextInput source="${a.name}" label="${a.name}" disabled />`;
|
return `<TextInput source="${a.name}" label="${label}" disabled />`;
|
||||||
}
|
}
|
||||||
if (a.foreign) {
|
if (a.foreign) {
|
||||||
return `<ReferenceInput source="${a.name}" reference="${pluralize(toKebab(a.foreign.entity))}" label="${a.name}">\n <AutocompleteInput optionText={(record) => record.code ? \`\${record.code} — \${record.name ?? record.code}\` : (record.name ?? record.id)} filterToQuery={(searchText) => ({ q: searchText })} />\n </ReferenceInput>`;
|
const referenceDisplay = getReferenceDisplayExpr(allEntities[a.foreign.entity]);
|
||||||
|
return `<ReferenceInput source="${a.name}" reference="${pluralize(toKebab(a.foreign.entity))}">\n <AutocompleteInput label="${label}" optionText={${referenceDisplay}} filterToQuery={(searchText) => ({ q: searchText })} />\n </ReferenceInput>`;
|
||||||
}
|
}
|
||||||
if (a.type === 'date') return `<TextInput source="${a.name}" label="${a.name}" />`;
|
if (a.type === 'date') return `<DateInput source="${a.name}" label="${label}" />`;
|
||||||
if (['integer', 'decimal'].includes(a.type)) return `<TextInput source="${a.name}" label="${a.name}" />`;
|
if (['integer', 'decimal'].includes(a.type)) return `<NumberInput source="${a.name}" label="${label}" />`;
|
||||||
if (!['string', 'text', 'uuid', 'integer', 'decimal', 'date'].includes(a.type)) {
|
if (!['string', 'text', 'uuid', 'integer', 'decimal', 'date'].includes(a.type)) {
|
||||||
if (a.name === 'status' && statusEnumAttr) return `<SelectInput source="${a.name}" label="${a.name}" choices={statusChoices} emptyText="Не выбрано" />`;
|
if (a.name === 'status' && statusEnumAttr) return `<SelectInput source="${a.name}" label="${label}" choices={statusChoices} emptyText="Не выбрано" />`;
|
||||||
return `<SelectInput source="${a.name}" label="${a.name}" choices={${a.name}Choices} emptyText="Не выбрано" />`;
|
return `<SelectInput source="${a.name}" label="${label}" choices={${a.name}Choices} emptyText="Не выбрано" />`;
|
||||||
}
|
}
|
||||||
return `<TextInput source="${a.name}" label="${a.name}" ${a.isRequired ? 'isRequired' : ''} />`;
|
return `<TextInput source="${a.name}" label="${label}" ${a.isRequired ? 'isRequired' : ''} />`;
|
||||||
};
|
};
|
||||||
|
|
||||||
const formImportSet = new Set(['SimpleForm', 'TextInput']);
|
const formImportSet = new Set(['SimpleForm', 'TextInput']);
|
||||||
|
if (hasNumber) formImportSet.add('NumberInput');
|
||||||
|
if (hasDate) formImportSet.add('DateInput');
|
||||||
if (enumAttrs.length) formImportSet.add('SelectInput');
|
if (enumAttrs.length) formImportSet.add('SelectInput');
|
||||||
if (hasFK) {
|
if (hasFK) {
|
||||||
formImportSet.add('ReferenceInput');
|
formImportSet.add('ReferenceInput');
|
||||||
@@ -474,10 +560,15 @@ function ensureAppModule(apply, backendModules) {
|
|||||||
let out = src;
|
let out = src;
|
||||||
for (const m of backendModules) {
|
for (const m of backendModules) {
|
||||||
if (!out.includes(`import { ${m.moduleName} }`)) {
|
if (!out.includes(`import { ${m.moduleName} }`)) {
|
||||||
out = out.replace(
|
const importLine = `import { ${m.moduleName} } from '${m.importPath}';`;
|
||||||
/import\s+\{\s*RepairOrderModule\s*\}[^;]*;\s*/m,
|
const importMatches = [...out.matchAll(/^import\s+.*;$/gm)];
|
||||||
(x) => `${x}import { ${m.moduleName} } from '${m.importPath}';\n`
|
if (importMatches.length) {
|
||||||
);
|
const lastImport = importMatches[importMatches.length - 1];
|
||||||
|
const insertAt = lastImport.index + lastImport[0].length;
|
||||||
|
out = `${out.slice(0, insertAt)}\n${importLine}${out.slice(insertAt)}`;
|
||||||
|
} else {
|
||||||
|
out = `${importLine}\n${out}`;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
out = out.replace(/imports:\s*\[\s*([\s\S]*?)\s*\],/m, (match, inner) => {
|
out = out.replace(/imports:\s*\[\s*([\s\S]*?)\s*\],/m, (match, inner) => {
|
||||||
@@ -504,7 +595,14 @@ function ensureClientApp(apply, frontendResources) {
|
|||||||
];
|
];
|
||||||
for (const imp of imports) {
|
for (const imp of imports) {
|
||||||
if (!out.includes(imp)) {
|
if (!out.includes(imp)) {
|
||||||
out = out.replace(/import\s+\{\s*RepairOrderShow\s*\}[^;]*;\s*/m, (x) => `${x}\n${imports.join('\n')}\n`);
|
const importMatches = [...out.matchAll(/^import\s+.*;$/gm)];
|
||||||
|
if (importMatches.length) {
|
||||||
|
const lastImport = importMatches[importMatches.length - 1];
|
||||||
|
const insertAt = lastImport.index + lastImport[0].length;
|
||||||
|
out = `${out.slice(0, insertAt)}\n${imports.join('\n')}${out.slice(insertAt)}`;
|
||||||
|
} else {
|
||||||
|
out = `${imports.join('\n')}\n${out}`;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -540,7 +638,7 @@ function main() {
|
|||||||
const pk = ent.primaryKey;
|
const pk = ent.primaryKey;
|
||||||
const resource = pluralize(toKebab(entityName));
|
const resource = pluralize(toKebab(entityName));
|
||||||
const be = renderBackendModule(entityName, ent, resource, pk);
|
const be = renderBackendModule(entityName, ent, resource, pk);
|
||||||
const fe = renderFrontendResource(entityName, ent, resource, pk, parsed.enums);
|
const fe = renderFrontendResource(entityName, ent, resource, pk, parsed.enums, parsed.entities);
|
||||||
backendModules.push(be);
|
backendModules.push(be);
|
||||||
frontendResources.push(fe);
|
frontendResources.push(fe);
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
export class UpdateEquipmentTypeDto {
|
export class UpdateEquipmentTypeDto {
|
||||||
id?: string;
|
id?: string;
|
||||||
code?: string | null;
|
code?: string;
|
||||||
name?: string | null;
|
name?: string;
|
||||||
manufacturer?: string | null;
|
manufacturer?: string;
|
||||||
maintenanceIntervalHours?: number | null;
|
maintenanceIntervalHours?: number;
|
||||||
overhaulIntervalHours?: number | null;
|
overhaulIntervalHours?: number;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,14 @@ import { PrismaService } from '../../prisma/prisma.service';
|
|||||||
import { CreateEquipmentTypeDto } from './dto/create-equipment-type.dto';
|
import { CreateEquipmentTypeDto } from './dto/create-equipment-type.dto';
|
||||||
import { UpdateEquipmentTypeDto } from './dto/update-equipment-type.dto';
|
import { UpdateEquipmentTypeDto } from './dto/update-equipment-type.dto';
|
||||||
|
|
||||||
|
function serializeRecord(record: any) {
|
||||||
|
return {
|
||||||
|
...record,
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class EquipmentTypeService {
|
export class EquipmentTypeService {
|
||||||
constructor(private readonly prisma: PrismaService) {}
|
constructor(private readonly prisma: PrismaService) {}
|
||||||
@@ -31,6 +39,8 @@ export class EquipmentTypeService {
|
|||||||
if (query.name) where.name = { contains: query.name, mode: 'insensitive' };
|
if (query.name) where.name = { contains: query.name, mode: 'insensitive' };
|
||||||
if (query.manufacturer) where.manufacturer = { contains: query.manufacturer, mode: 'insensitive' };
|
if (query.manufacturer) where.manufacturer = { contains: query.manufacturer, mode: 'insensitive' };
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Enum multi-value support (e.g. status=A&status=B)
|
// Enum multi-value support (e.g. status=A&status=B)
|
||||||
|
|
||||||
|
|
||||||
@@ -44,30 +54,37 @@ export class EquipmentTypeService {
|
|||||||
this.prisma.equipmentType.count({ where }),
|
this.prisma.equipmentType.count({ where }),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const mapped = data.map((r: any) => ({ id: r.code, ...r }));
|
const mapped = data.map((r: any) => ({ id: r.code, ...serializeRecord(r) }));
|
||||||
return { data: mapped, total };
|
return { data: mapped, total };
|
||||||
}
|
}
|
||||||
|
|
||||||
async findOne(id: string) {
|
async findOne(id: string) {
|
||||||
const record = await this.prisma.equipmentType.findUniqueOrThrow({ where: { code: id } as any });
|
const record = await this.prisma.equipmentType.findUniqueOrThrow({ where: { code: id } as any });
|
||||||
return { id: (record as any).code, ...record };
|
return { id: (record as any).code, ...serializeRecord(record) };
|
||||||
}
|
}
|
||||||
|
|
||||||
async create(dto: CreateEquipmentTypeDto) {
|
async create(dto: CreateEquipmentTypeDto) {
|
||||||
const record = await this.prisma.equipmentType.create({ data: dto as any });
|
const data: any = { ...(dto as any) };
|
||||||
return { id: (record as any).code, ...record };
|
|
||||||
|
|
||||||
|
|
||||||
|
const record = await this.prisma.equipmentType.create({ data });
|
||||||
|
return { id: (record as any).code, ...serializeRecord(record) };
|
||||||
}
|
}
|
||||||
|
|
||||||
async update(id: string, dto: UpdateEquipmentTypeDto) {
|
async update(id: string, dto: UpdateEquipmentTypeDto) {
|
||||||
const data: any = { ...(dto as any) };
|
const data: any = { ...(dto as any) };
|
||||||
delete data.id;
|
delete data.id;
|
||||||
delete data.code;
|
delete data.code;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const record = await this.prisma.equipmentType.update({ where: { code: id } as any, data });
|
const record = await this.prisma.equipmentType.update({ where: { code: id } as any, data });
|
||||||
return { id: (record as any).code, ...record };
|
return { id: (record as any).code, ...serializeRecord(record) };
|
||||||
}
|
}
|
||||||
|
|
||||||
async remove(id: string) {
|
async remove(id: string) {
|
||||||
const record = await this.prisma.equipmentType.delete({ where: { code: id } as any });
|
const record = await this.prisma.equipmentType.delete({ where: { code: id } as any });
|
||||||
return { id: (record as any).code, ...record };
|
return { id: (record as any).code, ...serializeRecord(record) };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
export class UpdateEquipmentDto {
|
export class UpdateEquipmentDto {
|
||||||
id?: string | null;
|
id?: string;
|
||||||
inventoryNumber?: string | null;
|
inventoryNumber?: string;
|
||||||
serialNumber?: string | null;
|
serialNumber?: string;
|
||||||
name?: string | null;
|
name?: string;
|
||||||
equipmentTypeCode?: string | null;
|
equipmentTypeCode?: string;
|
||||||
status?: string | null;
|
status?: string;
|
||||||
location?: string | null;
|
location?: string;
|
||||||
commissionedAt?: string | null;
|
commissionedAt?: string;
|
||||||
totalEngineHours?: string | null;
|
totalEngineHours?: string;
|
||||||
engineHoursSinceLastRepair?: string | null;
|
engineHoursSinceLastRepair?: string;
|
||||||
lastRepairAt?: string | null;
|
lastRepairAt?: string;
|
||||||
notes?: string | null;
|
notes?: string;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,16 @@ import { PrismaService } from '../../prisma/prisma.service';
|
|||||||
import { CreateEquipmentDto } from './dto/create-equipment.dto';
|
import { CreateEquipmentDto } from './dto/create-equipment.dto';
|
||||||
import { UpdateEquipmentDto } from './dto/update-equipment.dto';
|
import { UpdateEquipmentDto } from './dto/update-equipment.dto';
|
||||||
|
|
||||||
|
function serializeRecord(record: any) {
|
||||||
|
return {
|
||||||
|
...record,
|
||||||
|
totalEngineHours: record.totalEngineHours?.toString() ?? null,
|
||||||
|
engineHoursSinceLastRepair: record.engineHoursSinceLastRepair?.toString() ?? null,
|
||||||
|
commissionedAt: record.commissionedAt?.toISOString() ?? null,
|
||||||
|
lastRepairAt: record.lastRepairAt?.toISOString() ?? null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class EquipmentService {
|
export class EquipmentService {
|
||||||
constructor(private readonly prisma: PrismaService) {}
|
constructor(private readonly prisma: PrismaService) {}
|
||||||
@@ -13,7 +23,7 @@ export class EquipmentService {
|
|||||||
const end = parseInt(query._end) || 10;
|
const end = parseInt(query._end) || 10;
|
||||||
const take = end - start;
|
const take = end - start;
|
||||||
const skip = start;
|
const skip = start;
|
||||||
const sortField = query._sort || 'id';
|
const sortField = query._sort || 'inventoryNumber';
|
||||||
const sortOrder = (query._order || 'ASC').toLowerCase() as 'asc' | 'desc';
|
const sortOrder = (query._order || 'ASC').toLowerCase() as 'asc' | 'desc';
|
||||||
|
|
||||||
const where: any = {};
|
const where: any = {};
|
||||||
@@ -33,10 +43,11 @@ export class EquipmentService {
|
|||||||
if (query.inventoryNumber) where.inventoryNumber = { contains: query.inventoryNumber, mode: 'insensitive' };
|
if (query.inventoryNumber) where.inventoryNumber = { contains: query.inventoryNumber, mode: 'insensitive' };
|
||||||
if (query.serialNumber) where.serialNumber = { contains: query.serialNumber, mode: 'insensitive' };
|
if (query.serialNumber) where.serialNumber = { contains: query.serialNumber, mode: 'insensitive' };
|
||||||
if (query.name) where.name = { contains: query.name, mode: 'insensitive' };
|
if (query.name) where.name = { contains: query.name, mode: 'insensitive' };
|
||||||
if (query.equipmentTypeCode) where.equipmentTypeCode = { contains: query.equipmentTypeCode, mode: 'insensitive' };
|
|
||||||
if (query.location) where.location = { contains: query.location, mode: 'insensitive' };
|
if (query.location) where.location = { contains: query.location, mode: 'insensitive' };
|
||||||
if (query.notes) where.notes = { contains: query.notes, mode: 'insensitive' };
|
if (query.notes) where.notes = { contains: query.notes, mode: 'insensitive' };
|
||||||
|
|
||||||
|
if (query.equipmentTypeCode) where.equipmentTypeCode = query.equipmentTypeCode;
|
||||||
|
|
||||||
// Enum multi-value support (e.g. status=A&status=B)
|
// Enum multi-value support (e.g. status=A&status=B)
|
||||||
if (query.status) { const vals = Array.isArray(query.status) ? query.status : [query.status]; where.status = vals.length > 1 ? { in: vals } : vals[0]; }
|
if (query.status) { const vals = Array.isArray(query.status) ? query.status : [query.status]; where.status = vals.length > 1 ? { in: vals } : vals[0]; }
|
||||||
|
|
||||||
@@ -50,30 +61,41 @@ export class EquipmentService {
|
|||||||
this.prisma.equipment.count({ where }),
|
this.prisma.equipment.count({ where }),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const mapped = data;
|
const mapped = data.map(serializeRecord);
|
||||||
return { data: mapped, total };
|
return { data: mapped, total };
|
||||||
}
|
}
|
||||||
|
|
||||||
async findOne(id: string) {
|
async findOne(id: string) {
|
||||||
const record = await this.prisma.equipment.findUniqueOrThrow({ where: { id: id } as any });
|
const record = await this.prisma.equipment.findUniqueOrThrow({ where: { id: id } as any });
|
||||||
return record;
|
return serializeRecord(record);
|
||||||
}
|
}
|
||||||
|
|
||||||
async create(dto: CreateEquipmentDto) {
|
async create(dto: CreateEquipmentDto) {
|
||||||
const record = await this.prisma.equipment.create({ data: dto as any });
|
const data: any = { ...(dto as any) };
|
||||||
return record;
|
if (data.commissionedAt) data.commissionedAt = new Date(data.commissionedAt);
|
||||||
|
if (data.lastRepairAt) data.lastRepairAt = new Date(data.lastRepairAt);
|
||||||
|
if (data.totalEngineHours) data.totalEngineHours = new Prisma.Decimal(data.totalEngineHours);
|
||||||
|
if (data.engineHoursSinceLastRepair) data.engineHoursSinceLastRepair = new Prisma.Decimal(data.engineHoursSinceLastRepair);
|
||||||
|
|
||||||
|
const record = await this.prisma.equipment.create({ data });
|
||||||
|
return serializeRecord(record);
|
||||||
}
|
}
|
||||||
|
|
||||||
async update(id: string, dto: UpdateEquipmentDto) {
|
async update(id: string, dto: UpdateEquipmentDto) {
|
||||||
const data: any = { ...(dto as any) };
|
const data: any = { ...(dto as any) };
|
||||||
delete data.id;
|
delete data.id;
|
||||||
delete data.id;
|
delete data.id;
|
||||||
|
if (data.commissionedAt) data.commissionedAt = new Date(data.commissionedAt);
|
||||||
|
if (data.lastRepairAt) data.lastRepairAt = new Date(data.lastRepairAt);
|
||||||
|
if (data.totalEngineHours !== undefined && data.totalEngineHours !== null) data.totalEngineHours = new Prisma.Decimal(data.totalEngineHours);
|
||||||
|
if (data.engineHoursSinceLastRepair !== undefined && data.engineHoursSinceLastRepair !== null) data.engineHoursSinceLastRepair = new Prisma.Decimal(data.engineHoursSinceLastRepair);
|
||||||
|
|
||||||
const record = await this.prisma.equipment.update({ where: { id: id } as any, data });
|
const record = await this.prisma.equipment.update({ where: { id: id } as any, data });
|
||||||
return record;
|
return serializeRecord(record);
|
||||||
}
|
}
|
||||||
|
|
||||||
async remove(id: string) {
|
async remove(id: string) {
|
||||||
const record = await this.prisma.equipment.delete({ where: { id: id } as any });
|
const record = await this.prisma.equipment.delete({ where: { id: id } as any });
|
||||||
return record;
|
return serializeRecord(record);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
export class UpdateRepairOrderDto {
|
export class UpdateRepairOrderDto {
|
||||||
id?: string | null;
|
id?: string;
|
||||||
number?: string | null;
|
number?: string;
|
||||||
equipmentId?: string | null;
|
equipmentId?: string;
|
||||||
repairKind?: string | null;
|
repairKind?: string;
|
||||||
status?: string | null;
|
status?: string;
|
||||||
plannedAt?: string | null;
|
plannedAt?: string;
|
||||||
startedAt?: string | null;
|
startedAt?: string;
|
||||||
completedAt?: string | null;
|
completedAt?: string;
|
||||||
contractor?: string | null;
|
contractor?: string;
|
||||||
engineHoursAtRepair?: string | null;
|
engineHoursAtRepair?: string;
|
||||||
description?: string | null;
|
description?: string;
|
||||||
notes?: string | null;
|
notes?: string;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,16 @@ import { PrismaService } from '../../prisma/prisma.service';
|
|||||||
import { CreateRepairOrderDto } from './dto/create-repair-order.dto';
|
import { CreateRepairOrderDto } from './dto/create-repair-order.dto';
|
||||||
import { UpdateRepairOrderDto } from './dto/update-repair-order.dto';
|
import { UpdateRepairOrderDto } from './dto/update-repair-order.dto';
|
||||||
|
|
||||||
|
function serializeRecord(record: any) {
|
||||||
|
return {
|
||||||
|
...record,
|
||||||
|
engineHoursAtRepair: record.engineHoursAtRepair?.toString() ?? null,
|
||||||
|
plannedAt: record.plannedAt?.toISOString() ?? null,
|
||||||
|
startedAt: record.startedAt?.toISOString() ?? null,
|
||||||
|
completedAt: record.completedAt?.toISOString() ?? null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class RepairOrderService {
|
export class RepairOrderService {
|
||||||
constructor(private readonly prisma: PrismaService) {}
|
constructor(private readonly prisma: PrismaService) {}
|
||||||
@@ -13,7 +23,7 @@ export class RepairOrderService {
|
|||||||
const end = parseInt(query._end) || 10;
|
const end = parseInt(query._end) || 10;
|
||||||
const take = end - start;
|
const take = end - start;
|
||||||
const skip = start;
|
const skip = start;
|
||||||
const sortField = query._sort || 'id';
|
const sortField = query._sort || 'number';
|
||||||
const sortOrder = (query._order || 'ASC').toLowerCase() as 'asc' | 'desc';
|
const sortOrder = (query._order || 'ASC').toLowerCase() as 'asc' | 'desc';
|
||||||
|
|
||||||
const where: any = {};
|
const where: any = {};
|
||||||
@@ -33,6 +43,8 @@ export class RepairOrderService {
|
|||||||
if (query.description) where.description = { contains: query.description, mode: 'insensitive' };
|
if (query.description) where.description = { contains: query.description, mode: 'insensitive' };
|
||||||
if (query.notes) where.notes = { contains: query.notes, mode: 'insensitive' };
|
if (query.notes) where.notes = { contains: query.notes, mode: 'insensitive' };
|
||||||
|
|
||||||
|
if (query.equipmentId) where.equipmentId = query.equipmentId;
|
||||||
|
|
||||||
// Enum multi-value support (e.g. status=A&status=B)
|
// Enum multi-value support (e.g. status=A&status=B)
|
||||||
if (query.repairKind) { const vals = Array.isArray(query.repairKind) ? query.repairKind : [query.repairKind]; where.repairKind = vals.length > 1 ? { in: vals } : vals[0]; }
|
if (query.repairKind) { const vals = Array.isArray(query.repairKind) ? query.repairKind : [query.repairKind]; where.repairKind = vals.length > 1 ? { in: vals } : vals[0]; }
|
||||||
if (query.status) { const vals = Array.isArray(query.status) ? query.status : [query.status]; where.status = vals.length > 1 ? { in: vals } : vals[0]; }
|
if (query.status) { const vals = Array.isArray(query.status) ? query.status : [query.status]; where.status = vals.length > 1 ? { in: vals } : vals[0]; }
|
||||||
@@ -47,30 +59,41 @@ export class RepairOrderService {
|
|||||||
this.prisma.repairOrder.count({ where }),
|
this.prisma.repairOrder.count({ where }),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const mapped = data;
|
const mapped = data.map(serializeRecord);
|
||||||
return { data: mapped, total };
|
return { data: mapped, total };
|
||||||
}
|
}
|
||||||
|
|
||||||
async findOne(id: string) {
|
async findOne(id: string) {
|
||||||
const record = await this.prisma.repairOrder.findUniqueOrThrow({ where: { id: id } as any });
|
const record = await this.prisma.repairOrder.findUniqueOrThrow({ where: { id: id } as any });
|
||||||
return record;
|
return serializeRecord(record);
|
||||||
}
|
}
|
||||||
|
|
||||||
async create(dto: CreateRepairOrderDto) {
|
async create(dto: CreateRepairOrderDto) {
|
||||||
const record = await this.prisma.repairOrder.create({ data: dto as any });
|
const data: any = { ...(dto as any) };
|
||||||
return record;
|
if (data.plannedAt) data.plannedAt = new Date(data.plannedAt);
|
||||||
|
if (data.startedAt) data.startedAt = new Date(data.startedAt);
|
||||||
|
if (data.completedAt) data.completedAt = new Date(data.completedAt);
|
||||||
|
if (data.engineHoursAtRepair) data.engineHoursAtRepair = new Prisma.Decimal(data.engineHoursAtRepair);
|
||||||
|
|
||||||
|
const record = await this.prisma.repairOrder.create({ data });
|
||||||
|
return serializeRecord(record);
|
||||||
}
|
}
|
||||||
|
|
||||||
async update(id: string, dto: UpdateRepairOrderDto) {
|
async update(id: string, dto: UpdateRepairOrderDto) {
|
||||||
const data: any = { ...(dto as any) };
|
const data: any = { ...(dto as any) };
|
||||||
delete data.id;
|
delete data.id;
|
||||||
delete data.id;
|
delete data.id;
|
||||||
|
if (data.plannedAt) data.plannedAt = new Date(data.plannedAt);
|
||||||
|
if (data.startedAt) data.startedAt = new Date(data.startedAt);
|
||||||
|
if (data.completedAt) data.completedAt = new Date(data.completedAt);
|
||||||
|
if (data.engineHoursAtRepair !== undefined && data.engineHoursAtRepair !== null) data.engineHoursAtRepair = new Prisma.Decimal(data.engineHoursAtRepair);
|
||||||
|
|
||||||
const record = await this.prisma.repairOrder.update({ where: { id: id } as any, data });
|
const record = await this.prisma.repairOrder.update({ where: { id: id } as any, data });
|
||||||
return record;
|
return serializeRecord(record);
|
||||||
}
|
}
|
||||||
|
|
||||||
async remove(id: string) {
|
async remove(id: string) {
|
||||||
const record = await this.prisma.repairOrder.delete({ where: { id: id } as any });
|
const record = await this.prisma.repairOrder.delete({ where: { id: id } as any });
|
||||||
return record;
|
return serializeRecord(record);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user