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 { EquipmentStatusChangeList } from './resources/equipment-status-change/EquipmentStatusChangeList';
|
||||
import { EquipmentStatusChangeShow } from './resources/equipment-status-change/EquipmentStatusChangeShow';
|
||||
import { ToirLayout } from './layout/ToirLayout';
|
||||
|
||||
function ToirAdmin() {
|
||||
const paletteMode = useEmbeddedParentTheme();
|
||||
@@ -32,6 +33,7 @@ function ToirAdmin() {
|
||||
|
||||
return (
|
||||
<Admin
|
||||
layout={ToirLayout}
|
||||
dataProvider={dataProvider}
|
||||
authProvider={authProvider}
|
||||
theme={theme}
|
||||
@@ -47,7 +49,7 @@ function ToirAdmin() {
|
||||
/>
|
||||
<Resource
|
||||
name="status-changes"
|
||||
options={{ label: 'Акты' }}
|
||||
options={{ label: 'Журнал актов' }}
|
||||
list={EquipmentStatusChangeList}
|
||||
create={EquipmentStatusChangeCreate}
|
||||
edit={EquipmentStatusChangeEdit}
|
||||
|
||||
@@ -14,7 +14,7 @@ const customRu = {
|
||||
},
|
||||
},
|
||||
'status-changes': {
|
||||
name: 'Акт |||| Акты',
|
||||
name: 'Акт |||| Журнал актов',
|
||||
fields: {
|
||||
id: 'ID',
|
||||
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 {
|
||||
CreateButton,
|
||||
Datagrid,
|
||||
@@ -5,13 +7,26 @@ import {
|
||||
FilterButton,
|
||||
List,
|
||||
SelectArrayInput,
|
||||
SelectField,
|
||||
TextField,
|
||||
TextInput,
|
||||
TopToolbar,
|
||||
} from 'react-admin';
|
||||
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 = [
|
||||
<TextInput key="q" source="q" label="Поиск" alwaysOn />,
|
||||
<SelectArrayInput key="status" source="status" label="Статус" choices={equipmentStatusChoices} />,
|
||||
@@ -25,14 +40,23 @@ const ListActions = () => (
|
||||
);
|
||||
|
||||
export function EquipmentList() {
|
||||
const location = useLocation();
|
||||
const filterDefaultValues = useMemo(() => parseListFilterFromSearch(location.search), [location.search]);
|
||||
const listKey = `${location.pathname}${location.search}`;
|
||||
|
||||
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">
|
||||
<TextField source="name" />
|
||||
<TextField source="serialNumber" />
|
||||
<DateField source="dateOfInspection" />
|
||||
<DateField source="commissionedAt" />
|
||||
<SelectField source="status" choices={equipmentStatusChoices} />
|
||||
</Datagrid>
|
||||
</List>
|
||||
);
|
||||
|
||||
@@ -82,9 +82,12 @@ export function buildToirMuiTheme(mode: Mode): Theme {
|
||||
styleOverrides: {
|
||||
head: {
|
||||
fontWeight: 700,
|
||||
fontSize: '0.9rem',
|
||||
borderBottom: `1px solid ${t.border}`,
|
||||
},
|
||||
root: {
|
||||
fontSize: '0.95rem',
|
||||
lineHeight: 1.35,
|
||||
borderBottom: `1px solid ${t.divider}`,
|
||||
},
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user