Rework menu for different statuses of equip
This commit is contained in:
@@ -15,6 +15,7 @@ import { EquipmentStatusChangeCreate } from './resources/equipment-status-change
|
|||||||
import { EquipmentStatusChangeEdit } from './resources/equipment-status-change/EquipmentStatusChangeEdit';
|
import { EquipmentStatusChangeEdit } from './resources/equipment-status-change/EquipmentStatusChangeEdit';
|
||||||
import { EquipmentStatusChangeList } from './resources/equipment-status-change/EquipmentStatusChangeList';
|
import { EquipmentStatusChangeList } from './resources/equipment-status-change/EquipmentStatusChangeList';
|
||||||
import { EquipmentStatusChangeShow } from './resources/equipment-status-change/EquipmentStatusChangeShow';
|
import { EquipmentStatusChangeShow } from './resources/equipment-status-change/EquipmentStatusChangeShow';
|
||||||
|
import { ToirLayout } from './layout/ToirLayout';
|
||||||
|
|
||||||
function ToirAdmin() {
|
function ToirAdmin() {
|
||||||
const paletteMode = useEmbeddedParentTheme();
|
const paletteMode = useEmbeddedParentTheme();
|
||||||
@@ -32,6 +33,7 @@ function ToirAdmin() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Admin
|
<Admin
|
||||||
|
layout={ToirLayout}
|
||||||
dataProvider={dataProvider}
|
dataProvider={dataProvider}
|
||||||
authProvider={authProvider}
|
authProvider={authProvider}
|
||||||
theme={theme}
|
theme={theme}
|
||||||
@@ -47,7 +49,7 @@ function ToirAdmin() {
|
|||||||
/>
|
/>
|
||||||
<Resource
|
<Resource
|
||||||
name="status-changes"
|
name="status-changes"
|
||||||
options={{ label: 'Акты' }}
|
options={{ label: 'Журнал актов' }}
|
||||||
list={EquipmentStatusChangeList}
|
list={EquipmentStatusChangeList}
|
||||||
create={EquipmentStatusChangeCreate}
|
create={EquipmentStatusChangeCreate}
|
||||||
edit={EquipmentStatusChangeEdit}
|
edit={EquipmentStatusChangeEdit}
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ const customRu = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
'status-changes': {
|
'status-changes': {
|
||||||
name: 'Акт |||| Акты',
|
name: 'Акт |||| Журнал актов',
|
||||||
fields: {
|
fields: {
|
||||||
id: 'ID',
|
id: 'ID',
|
||||||
equipmentId: 'Оборудование',
|
equipmentId: 'Оборудование',
|
||||||
|
|||||||
7
client/src/layout/ToirLayout.tsx
Normal file
7
client/src/layout/ToirLayout.tsx
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import type { ComponentProps, ReactElement } from 'react';
|
||||||
|
import { Layout } from 'react-admin';
|
||||||
|
import { ToirMenu } from './ToirMenu';
|
||||||
|
|
||||||
|
export function ToirLayout(props: ComponentProps<typeof Layout>): ReactElement {
|
||||||
|
return <Layout {...props} menu={ToirMenu} />;
|
||||||
|
}
|
||||||
23
client/src/layout/ToirMenu.tsx
Normal file
23
client/src/layout/ToirMenu.tsx
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import { Menu, MenuItemLink } from 'react-admin';
|
||||||
|
import {
|
||||||
|
EQUIPMENT_SIDEBAR_ARCHIVE_FILTER,
|
||||||
|
EQUIPMENT_SIDEBAR_IN_WORK_FILTER,
|
||||||
|
equipmentListSearch,
|
||||||
|
} from './toirMenuLinks';
|
||||||
|
|
||||||
|
export function ToirMenu() {
|
||||||
|
return (
|
||||||
|
<Menu>
|
||||||
|
<MenuItemLink to="/equipment" primaryText="Оборудование" />
|
||||||
|
<MenuItemLink
|
||||||
|
to={`/equipment?${equipmentListSearch(EQUIPMENT_SIDEBAR_IN_WORK_FILTER)}`}
|
||||||
|
primaryText="В работе"
|
||||||
|
/>
|
||||||
|
<MenuItemLink
|
||||||
|
to={`/equipment?${equipmentListSearch(EQUIPMENT_SIDEBAR_ARCHIVE_FILTER)}`}
|
||||||
|
primaryText="Архив"
|
||||||
|
/>
|
||||||
|
<MenuItemLink to="/status-changes" primaryText="Журнал актов" />
|
||||||
|
</Menu>
|
||||||
|
);
|
||||||
|
}
|
||||||
13
client/src/layout/toirMenuLinks.ts
Normal file
13
client/src/layout/toirMenuLinks.ts
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
/** Параметры списка оборудования в формате react-admin (как при сохранённом фильтре в URL). */
|
||||||
|
export function equipmentListSearch(filter: Record<string, unknown>): string {
|
||||||
|
const params = new URLSearchParams();
|
||||||
|
params.set('filter', JSON.stringify(filter));
|
||||||
|
params.set('displayedFilters', JSON.stringify({ status: true }));
|
||||||
|
return params.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** «В работе» → статус Active (в эксплуатации). См. `equipmentStatusChoices`. */
|
||||||
|
export const EQUIPMENT_SIDEBAR_IN_WORK_FILTER = { status: ['Active'] };
|
||||||
|
|
||||||
|
/** «Архив» → списанное оборудование (WriteOff). */
|
||||||
|
export const EQUIPMENT_SIDEBAR_ARCHIVE_FILTER = { status: ['WriteOff'] };
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import { useMemo } from 'react';
|
||||||
|
import { useLocation } from 'react-router-dom';
|
||||||
import {
|
import {
|
||||||
CreateButton,
|
CreateButton,
|
||||||
Datagrid,
|
Datagrid,
|
||||||
@@ -5,13 +7,26 @@ import {
|
|||||||
FilterButton,
|
FilterButton,
|
||||||
List,
|
List,
|
||||||
SelectArrayInput,
|
SelectArrayInput,
|
||||||
SelectField,
|
|
||||||
TextField,
|
TextField,
|
||||||
TextInput,
|
TextInput,
|
||||||
TopToolbar,
|
TopToolbar,
|
||||||
} from 'react-admin';
|
} from 'react-admin';
|
||||||
import { equipmentStatusChoices } from './shared';
|
import { equipmentStatusChoices } from './shared';
|
||||||
|
|
||||||
|
function parseListFilterFromSearch(search: string): Record<string, unknown> | undefined {
|
||||||
|
const params = new URLSearchParams(search);
|
||||||
|
const raw = params.get('filter');
|
||||||
|
if (!raw) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const parsed = JSON.parse(raw) as Record<string, unknown>;
|
||||||
|
return parsed && typeof parsed === 'object' && !Array.isArray(parsed) ? parsed : undefined;
|
||||||
|
} catch {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const equipmentFilters = [
|
const equipmentFilters = [
|
||||||
<TextInput key="q" source="q" label="Поиск" alwaysOn />,
|
<TextInput key="q" source="q" label="Поиск" alwaysOn />,
|
||||||
<SelectArrayInput key="status" source="status" label="Статус" choices={equipmentStatusChoices} />,
|
<SelectArrayInput key="status" source="status" label="Статус" choices={equipmentStatusChoices} />,
|
||||||
@@ -25,14 +40,23 @@ const ListActions = () => (
|
|||||||
);
|
);
|
||||||
|
|
||||||
export function EquipmentList() {
|
export function EquipmentList() {
|
||||||
|
const location = useLocation();
|
||||||
|
const filterDefaultValues = useMemo(() => parseListFilterFromSearch(location.search), [location.search]);
|
||||||
|
const listKey = `${location.pathname}${location.search}`;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<List filters={equipmentFilters} actions={<ListActions />} sort={{ field: 'name', order: 'ASC' }}>
|
<List
|
||||||
|
key={listKey}
|
||||||
|
filters={equipmentFilters}
|
||||||
|
actions={<ListActions />}
|
||||||
|
sort={{ field: 'name', order: 'ASC' }}
|
||||||
|
filterDefaultValues={filterDefaultValues}
|
||||||
|
>
|
||||||
<Datagrid rowClick="show">
|
<Datagrid rowClick="show">
|
||||||
<TextField source="name" />
|
<TextField source="name" />
|
||||||
<TextField source="serialNumber" />
|
<TextField source="serialNumber" />
|
||||||
<DateField source="dateOfInspection" />
|
<DateField source="dateOfInspection" />
|
||||||
<DateField source="commissionedAt" />
|
<DateField source="commissionedAt" />
|
||||||
<SelectField source="status" choices={equipmentStatusChoices} />
|
|
||||||
</Datagrid>
|
</Datagrid>
|
||||||
</List>
|
</List>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -82,9 +82,12 @@ export function buildToirMuiTheme(mode: Mode): Theme {
|
|||||||
styleOverrides: {
|
styleOverrides: {
|
||||||
head: {
|
head: {
|
||||||
fontWeight: 700,
|
fontWeight: 700,
|
||||||
|
fontSize: '0.9rem',
|
||||||
borderBottom: `1px solid ${t.border}`,
|
borderBottom: `1px solid ${t.border}`,
|
||||||
},
|
},
|
||||||
root: {
|
root: {
|
||||||
|
fontSize: '0.95rem',
|
||||||
|
lineHeight: 1.35,
|
||||||
borderBottom: `1px solid ${t.divider}`,
|
borderBottom: `1px solid ${t.divider}`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user