mirror of
https://github.com/inventree/InvenTree.git
synced 2025-04-28 19:46:46 +00:00
PUI location type (#7238)
* add location type to PUI * fix add form bug where it contains the previous editing data when reusing the same fields * fix sonarcloud issues
This commit is contained in:
parent
20dc0380bd
commit
548ecf58a2
@ -12,6 +12,7 @@ import {
|
|||||||
Tooltip
|
Tooltip
|
||||||
} from '@mantine/core';
|
} from '@mantine/core';
|
||||||
import { useSuspenseQuery } from '@tanstack/react-query';
|
import { useSuspenseQuery } from '@tanstack/react-query';
|
||||||
|
import { getValueAtPath } from 'mantine-datatable';
|
||||||
import { Suspense, useCallback, useMemo } from 'react';
|
import { Suspense, useCallback, useMemo } from 'react';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
|
||||||
@ -43,7 +44,7 @@ export type PartIconsType = {
|
|||||||
export type DetailsField =
|
export type DetailsField =
|
||||||
| {
|
| {
|
||||||
hidden?: boolean;
|
hidden?: boolean;
|
||||||
icon?: string;
|
icon?: InvenTreeIconType;
|
||||||
name: string;
|
name: string;
|
||||||
label?: string;
|
label?: string;
|
||||||
badge?: BadgeType;
|
badge?: BadgeType;
|
||||||
@ -382,6 +383,11 @@ export function DetailsTableField({
|
|||||||
|
|
||||||
const FieldType: any = getFieldType(field.type);
|
const FieldType: any = getFieldType(field.type);
|
||||||
|
|
||||||
|
const fieldValue = useMemo(
|
||||||
|
() => getValueAtPath(item, field.name) as string,
|
||||||
|
[item, field.name]
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Table.Tr style={{ verticalAlign: 'top' }}>
|
<Table.Tr style={{ verticalAlign: 'top' }}>
|
||||||
<Table.Td
|
<Table.Td
|
||||||
@ -390,16 +396,16 @@ export function DetailsTableField({
|
|||||||
maxWidth: '50'
|
maxWidth: '50'
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<InvenTreeIcon icon={(field.icon ?? field.name) as InvenTreeIconType} />
|
<InvenTreeIcon icon={field.icon ?? (field.name as InvenTreeIconType)} />
|
||||||
</Table.Td>
|
</Table.Td>
|
||||||
<Table.Td style={{ maxWidth: '65%' }}>
|
<Table.Td style={{ maxWidth: '65%' }}>
|
||||||
<Text>{field.label}</Text>
|
<Text>{field.label}</Text>
|
||||||
</Table.Td>
|
</Table.Td>
|
||||||
<Table.Td style={{}}>
|
<Table.Td style={{}}>
|
||||||
<FieldType field_data={field} field_value={item[field.name]} />
|
<FieldType field_data={field} field_value={fieldValue} />
|
||||||
</Table.Td>
|
</Table.Td>
|
||||||
<Table.Td style={{ width: '50' }}>
|
<Table.Td style={{ width: '50' }}>
|
||||||
{field.copy && <CopyField value={item[field.name]} />}
|
{field.copy && <CopyField value={fieldValue} />}
|
||||||
</Table.Td>
|
</Table.Td>
|
||||||
</Table.Tr>
|
</Table.Tr>
|
||||||
);
|
);
|
||||||
|
@ -25,7 +25,11 @@ import {
|
|||||||
RenderPartParameterTemplate,
|
RenderPartParameterTemplate,
|
||||||
RenderPartTestTemplate
|
RenderPartTestTemplate
|
||||||
} from './Part';
|
} from './Part';
|
||||||
import { RenderStockItem, RenderStockLocation } from './Stock';
|
import {
|
||||||
|
RenderStockItem,
|
||||||
|
RenderStockLocation,
|
||||||
|
RenderStockLocationType
|
||||||
|
} from './Stock';
|
||||||
import { RenderOwner, RenderUser } from './User';
|
import { RenderOwner, RenderUser } from './User';
|
||||||
|
|
||||||
type EnumDictionary<T extends string | symbol | number, U> = {
|
type EnumDictionary<T extends string | symbol | number, U> = {
|
||||||
@ -57,6 +61,7 @@ const RendererLookup: EnumDictionary<
|
|||||||
[ModelType.salesorder]: RenderSalesOrder,
|
[ModelType.salesorder]: RenderSalesOrder,
|
||||||
[ModelType.salesordershipment]: RenderSalesOrderShipment,
|
[ModelType.salesordershipment]: RenderSalesOrderShipment,
|
||||||
[ModelType.stocklocation]: RenderStockLocation,
|
[ModelType.stocklocation]: RenderStockLocation,
|
||||||
|
[ModelType.stocklocationtype]: RenderStockLocationType,
|
||||||
[ModelType.stockitem]: RenderStockItem,
|
[ModelType.stockitem]: RenderStockItem,
|
||||||
[ModelType.stockhistory]: RenderStockItem,
|
[ModelType.stockhistory]: RenderStockItem,
|
||||||
[ModelType.supplierpart]: RenderSupplierPart,
|
[ModelType.supplierpart]: RenderSupplierPart,
|
||||||
|
@ -79,6 +79,11 @@ export const ModelInformationDict: ModelDict = {
|
|||||||
cui_detail: '/stock/location/:pk/',
|
cui_detail: '/stock/location/:pk/',
|
||||||
api_endpoint: ApiEndpoints.stock_location_list
|
api_endpoint: ApiEndpoints.stock_location_list
|
||||||
},
|
},
|
||||||
|
stocklocationtype: {
|
||||||
|
label: t`Stock Location Type`,
|
||||||
|
label_multiple: t`Stock Location Types`,
|
||||||
|
api_endpoint: ApiEndpoints.stock_location_type_list
|
||||||
|
},
|
||||||
stockhistory: {
|
stockhistory: {
|
||||||
label: t`Stock History`,
|
label: t`Stock History`,
|
||||||
label_multiple: t`Stock Histories`,
|
label_multiple: t`Stock Histories`,
|
||||||
|
@ -17,6 +17,21 @@ export function RenderStockLocation({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inline rendering of a single StockLocationType instance
|
||||||
|
*/
|
||||||
|
export function RenderStockLocationType({
|
||||||
|
instance
|
||||||
|
}: Readonly<InstanceRenderInterface>): ReactNode {
|
||||||
|
return (
|
||||||
|
<RenderInlineModel
|
||||||
|
primary={instance.name}
|
||||||
|
// TODO: render location icon here too (ref: #7237)
|
||||||
|
secondary={instance.description + ` (${instance.location_count})`}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export function RenderStockItem({
|
export function RenderStockItem({
|
||||||
instance
|
instance
|
||||||
}: Readonly<InstanceRenderInterface>): ReactNode {
|
}: Readonly<InstanceRenderInterface>): ReactNode {
|
||||||
|
@ -94,6 +94,7 @@ export enum ApiEndpoints {
|
|||||||
stock_item_list = 'stock/',
|
stock_item_list = 'stock/',
|
||||||
stock_tracking_list = 'stock/track/',
|
stock_tracking_list = 'stock/track/',
|
||||||
stock_location_list = 'stock/location/',
|
stock_location_list = 'stock/location/',
|
||||||
|
stock_location_type_list = 'stock/location-type/',
|
||||||
stock_location_tree = 'stock/location/tree/',
|
stock_location_tree = 'stock/location/tree/',
|
||||||
stock_attachment_list = 'stock/attachment/',
|
stock_attachment_list = 'stock/attachment/',
|
||||||
stock_test_result_list = 'stock/test/',
|
stock_test_result_list = 'stock/test/',
|
||||||
|
@ -11,6 +11,7 @@ export enum ModelType {
|
|||||||
projectcode = 'projectcode',
|
projectcode = 'projectcode',
|
||||||
stockitem = 'stockitem',
|
stockitem = 'stockitem',
|
||||||
stocklocation = 'stocklocation',
|
stocklocation = 'stocklocation',
|
||||||
|
stocklocationtype = 'stocklocationtype',
|
||||||
stockhistory = 'stockhistory',
|
stockhistory = 'stockhistory',
|
||||||
build = 'build',
|
build = 'build',
|
||||||
buildline = 'buildline',
|
buildline = 'buildline',
|
||||||
|
@ -819,6 +819,7 @@ export function stockLocationFields({}: {}): ApiFormFieldSet {
|
|||||||
description: {},
|
description: {},
|
||||||
structural: {},
|
structural: {},
|
||||||
external: {},
|
external: {},
|
||||||
|
icon: {},
|
||||||
location_type: {}
|
location_type: {}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@ import {
|
|||||||
IconExclamationCircle,
|
IconExclamationCircle,
|
||||||
IconList,
|
IconList,
|
||||||
IconListDetails,
|
IconListDetails,
|
||||||
|
IconPackages,
|
||||||
IconPlugConnected,
|
IconPlugConnected,
|
||||||
IconScale,
|
IconScale,
|
||||||
IconSitemap,
|
IconSitemap,
|
||||||
@ -57,6 +58,10 @@ const PartCategoryTemplateTable = Loadable(
|
|||||||
lazy(() => import('../../../../tables/part/PartCategoryTemplateTable'))
|
lazy(() => import('../../../../tables/part/PartCategoryTemplateTable'))
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const LocationTypesTable = Loadable(
|
||||||
|
lazy(() => import('../../../../tables/stock/LocationTypesTable'))
|
||||||
|
);
|
||||||
|
|
||||||
const CurrencyTable = Loadable(
|
const CurrencyTable = Loadable(
|
||||||
lazy(() => import('../../../../tables/settings/CurrencyTable'))
|
lazy(() => import('../../../../tables/settings/CurrencyTable'))
|
||||||
);
|
);
|
||||||
@ -122,6 +127,12 @@ export default function AdminCenter() {
|
|||||||
icon: <IconSitemap />,
|
icon: <IconSitemap />,
|
||||||
content: <PartCategoryTemplateTable />
|
content: <PartCategoryTemplateTable />
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'location-types',
|
||||||
|
label: t`Location types`,
|
||||||
|
icon: <IconPackages />,
|
||||||
|
content: <LocationTypesTable />
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'templates',
|
name: 'templates',
|
||||||
label: t`Templates`,
|
label: t`Templates`,
|
||||||
|
@ -133,6 +133,14 @@ export default function Stock() {
|
|||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
name: 'external',
|
name: 'external',
|
||||||
label: t`External`
|
label: t`External`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'string',
|
||||||
|
// TODO: render location type icon here (ref: #7237)
|
||||||
|
name: 'location_type_detail.name',
|
||||||
|
label: t`Location Type`,
|
||||||
|
hidden: !location?.location_type,
|
||||||
|
icon: 'packages'
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -131,7 +131,7 @@ export default function ParametricPartTable({
|
|||||||
const addParameter = useCreateApiFormModal({
|
const addParameter = useCreateApiFormModal({
|
||||||
url: ApiEndpoints.part_parameter_list,
|
url: ApiEndpoints.part_parameter_list,
|
||||||
title: t`Add Part Parameter`,
|
title: t`Add Part Parameter`,
|
||||||
fields: partParameterFields,
|
fields: useMemo(() => ({ ...partParameterFields }), [partParameterFields]),
|
||||||
focus: 'data',
|
focus: 'data',
|
||||||
onFormSuccess: (parameter: any) => {
|
onFormSuccess: (parameter: any) => {
|
||||||
updateParameterRecord(selectedPart, parameter);
|
updateParameterRecord(selectedPart, parameter);
|
||||||
@ -146,7 +146,7 @@ export default function ParametricPartTable({
|
|||||||
url: ApiEndpoints.part_parameter_list,
|
url: ApiEndpoints.part_parameter_list,
|
||||||
title: t`Edit Part Parameter`,
|
title: t`Edit Part Parameter`,
|
||||||
pk: selectedParameter,
|
pk: selectedParameter,
|
||||||
fields: partParameterFields,
|
fields: useMemo(() => ({ ...partParameterFields }), [partParameterFields]),
|
||||||
focus: 'data',
|
focus: 'data',
|
||||||
onFormSuccess: (parameter: any) => {
|
onFormSuccess: (parameter: any) => {
|
||||||
updateParameterRecord(selectedPart, parameter);
|
updateParameterRecord(selectedPart, parameter);
|
||||||
|
@ -36,7 +36,7 @@ export default function PartCategoryTemplateTable({}: {}) {
|
|||||||
const newTemplate = useCreateApiFormModal({
|
const newTemplate = useCreateApiFormModal({
|
||||||
url: ApiEndpoints.category_parameter_list,
|
url: ApiEndpoints.category_parameter_list,
|
||||||
title: t`Add Category Parameter`,
|
title: t`Add Category Parameter`,
|
||||||
fields: formFields,
|
fields: useMemo(() => ({ ...formFields }), [formFields]),
|
||||||
onFormSuccess: table.refreshTable
|
onFormSuccess: table.refreshTable
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -44,7 +44,7 @@ export default function PartCategoryTemplateTable({}: {}) {
|
|||||||
url: ApiEndpoints.category_parameter_list,
|
url: ApiEndpoints.category_parameter_list,
|
||||||
pk: selectedTemplate,
|
pk: selectedTemplate,
|
||||||
title: t`Edit Category Parameter`,
|
title: t`Edit Category Parameter`,
|
||||||
fields: formFields,
|
fields: useMemo(() => ({ ...formFields }), [formFields]),
|
||||||
onFormSuccess: (record: any) => table.updateRecord(record)
|
onFormSuccess: (record: any) => table.updateRecord(record)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -110,7 +110,7 @@ export function PartParameterTable({ partId }: { partId: any }) {
|
|||||||
const newParameter = useCreateApiFormModal({
|
const newParameter = useCreateApiFormModal({
|
||||||
url: ApiEndpoints.part_parameter_list,
|
url: ApiEndpoints.part_parameter_list,
|
||||||
title: t`New Part Parameter`,
|
title: t`New Part Parameter`,
|
||||||
fields: partParameterFields,
|
fields: useMemo(() => ({ ...partParameterFields }), [partParameterFields]),
|
||||||
focus: 'template',
|
focus: 'template',
|
||||||
initialData: {
|
initialData: {
|
||||||
part: partId
|
part: partId
|
||||||
@ -126,7 +126,7 @@ export function PartParameterTable({ partId }: { partId: any }) {
|
|||||||
url: ApiEndpoints.part_parameter_list,
|
url: ApiEndpoints.part_parameter_list,
|
||||||
pk: selectedParameter,
|
pk: selectedParameter,
|
||||||
title: t`Edit Part Parameter`,
|
title: t`Edit Part Parameter`,
|
||||||
fields: partParameterFields,
|
fields: useMemo(() => ({ ...partParameterFields }), [partParameterFields]),
|
||||||
onFormSuccess: table.refreshTable
|
onFormSuccess: table.refreshTable
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -83,7 +83,10 @@ export default function PartParameterTemplateTable() {
|
|||||||
const newTemplate = useCreateApiFormModal({
|
const newTemplate = useCreateApiFormModal({
|
||||||
url: ApiEndpoints.part_parameter_template_list,
|
url: ApiEndpoints.part_parameter_template_list,
|
||||||
title: t`Add Parameter Template`,
|
title: t`Add Parameter Template`,
|
||||||
fields: partParameterTemplateFields,
|
fields: useMemo(
|
||||||
|
() => ({ ...partParameterTemplateFields }),
|
||||||
|
[partParameterTemplateFields]
|
||||||
|
),
|
||||||
onFormSuccess: table.refreshTable
|
onFormSuccess: table.refreshTable
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -95,7 +98,10 @@ export default function PartParameterTemplateTable() {
|
|||||||
url: ApiEndpoints.part_parameter_template_list,
|
url: ApiEndpoints.part_parameter_template_list,
|
||||||
pk: selectedTemplate,
|
pk: selectedTemplate,
|
||||||
title: t`Edit Parameter Template`,
|
title: t`Edit Parameter Template`,
|
||||||
fields: partParameterTemplateFields,
|
fields: useMemo(
|
||||||
|
() => ({ ...partParameterTemplateFields }),
|
||||||
|
[partParameterTemplateFields]
|
||||||
|
),
|
||||||
onFormSuccess: (record: any) => table.updateRecord(record)
|
onFormSuccess: (record: any) => table.updateRecord(record)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -124,7 +124,10 @@ export default function PartTestTemplateTable({ partId }: { partId: number }) {
|
|||||||
const newTestTemplate = useCreateApiFormModal({
|
const newTestTemplate = useCreateApiFormModal({
|
||||||
url: ApiEndpoints.part_test_template_list,
|
url: ApiEndpoints.part_test_template_list,
|
||||||
title: t`Add Test Template`,
|
title: t`Add Test Template`,
|
||||||
fields: partTestTemplateFields,
|
fields: useMemo(
|
||||||
|
() => ({ ...partTestTemplateFields }),
|
||||||
|
[partTestTemplateFields]
|
||||||
|
),
|
||||||
initialData: {
|
initialData: {
|
||||||
part: partId
|
part: partId
|
||||||
},
|
},
|
||||||
@ -137,7 +140,10 @@ export default function PartTestTemplateTable({ partId }: { partId: number }) {
|
|||||||
url: ApiEndpoints.part_test_template_list,
|
url: ApiEndpoints.part_test_template_list,
|
||||||
pk: selectedTest,
|
pk: selectedTest,
|
||||||
title: t`Edit Test Template`,
|
title: t`Edit Test Template`,
|
||||||
fields: partTestTemplateFields,
|
fields: useMemo(
|
||||||
|
() => ({ ...partTestTemplateFields }),
|
||||||
|
[partTestTemplateFields]
|
||||||
|
),
|
||||||
onFormSuccess: (record: any) => table.updateRecord(record)
|
onFormSuccess: (record: any) => table.updateRecord(record)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
128
src/frontend/src/tables/stock/LocationTypesTable.tsx
Normal file
128
src/frontend/src/tables/stock/LocationTypesTable.tsx
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
import { t } from '@lingui/macro';
|
||||||
|
import { useCallback, useMemo, useState } from 'react';
|
||||||
|
|
||||||
|
import { AddItemButton } from '../../components/buttons/AddItemButton';
|
||||||
|
import { ApiFormFieldSet } from '../../components/forms/fields/ApiFormField';
|
||||||
|
import { ApiEndpoints } from '../../enums/ApiEndpoints';
|
||||||
|
import { UserRoles } from '../../enums/Roles';
|
||||||
|
import {
|
||||||
|
useCreateApiFormModal,
|
||||||
|
useDeleteApiFormModal,
|
||||||
|
useEditApiFormModal
|
||||||
|
} from '../../hooks/UseForm';
|
||||||
|
import { useTable } from '../../hooks/UseTable';
|
||||||
|
import { apiUrl } from '../../states/ApiState';
|
||||||
|
import { useUserState } from '../../states/UserState';
|
||||||
|
import { TableColumn } from '../Column';
|
||||||
|
import { InvenTreeTable } from '../InvenTreeTable';
|
||||||
|
import { RowDeleteAction, RowEditAction } from '../RowActions';
|
||||||
|
|
||||||
|
export default function LocationTypesTable() {
|
||||||
|
const table = useTable('location-types');
|
||||||
|
const user = useUserState();
|
||||||
|
|
||||||
|
const formFields: ApiFormFieldSet = useMemo(() => {
|
||||||
|
return {
|
||||||
|
name: {},
|
||||||
|
description: {},
|
||||||
|
icon: {}
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const [selectedLocationType, setSelectedLocationType] = useState<number>(0);
|
||||||
|
|
||||||
|
const newLocationType = useCreateApiFormModal({
|
||||||
|
url: ApiEndpoints.stock_location_type_list,
|
||||||
|
title: t`Add Location Type`,
|
||||||
|
fields: useMemo(() => ({ ...formFields }), [formFields]),
|
||||||
|
onFormSuccess: table.refreshTable
|
||||||
|
});
|
||||||
|
|
||||||
|
const editLocationType = useEditApiFormModal({
|
||||||
|
url: ApiEndpoints.stock_location_type_list,
|
||||||
|
pk: selectedLocationType,
|
||||||
|
title: t`Edit Location Type`,
|
||||||
|
fields: useMemo(() => ({ ...formFields }), [formFields]),
|
||||||
|
onFormSuccess: (record: any) => table.updateRecord(record)
|
||||||
|
});
|
||||||
|
|
||||||
|
const deleteLocationType = useDeleteApiFormModal({
|
||||||
|
url: ApiEndpoints.stock_location_type_list,
|
||||||
|
pk: selectedLocationType,
|
||||||
|
title: t`Delete Location Type`,
|
||||||
|
onFormSuccess: table.refreshTable
|
||||||
|
});
|
||||||
|
|
||||||
|
const tableColumns: TableColumn[] = useMemo(() => {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
accessor: 'name',
|
||||||
|
title: t`Name`,
|
||||||
|
sortable: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessor: 'description',
|
||||||
|
title: t`Description`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessor: 'icon',
|
||||||
|
title: t`Icon`,
|
||||||
|
sortable: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessor: 'location_count',
|
||||||
|
sortable: true
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const rowActions = useCallback(
|
||||||
|
(record: any) => {
|
||||||
|
return [
|
||||||
|
RowEditAction({
|
||||||
|
hidden: !user.hasChangeRole(UserRoles.stock_location),
|
||||||
|
onClick: () => {
|
||||||
|
setSelectedLocationType(record.pk);
|
||||||
|
editLocationType.open();
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
RowDeleteAction({
|
||||||
|
hidden: !user.hasDeleteRole(UserRoles.stock_location),
|
||||||
|
onClick: () => {
|
||||||
|
setSelectedLocationType(record.pk);
|
||||||
|
deleteLocationType.open();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
];
|
||||||
|
},
|
||||||
|
[user]
|
||||||
|
);
|
||||||
|
|
||||||
|
const tableActions = useMemo(() => {
|
||||||
|
return [
|
||||||
|
<AddItemButton
|
||||||
|
key="add-location-type"
|
||||||
|
tooltip={t`Add Location Type`}
|
||||||
|
onClick={() => newLocationType.open()}
|
||||||
|
hidden={!user.hasAddRole(UserRoles.stock_location)}
|
||||||
|
/>
|
||||||
|
];
|
||||||
|
}, [user]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{newLocationType.modal}
|
||||||
|
{editLocationType.modal}
|
||||||
|
{deleteLocationType.modal}
|
||||||
|
<InvenTreeTable
|
||||||
|
url={apiUrl(ApiEndpoints.stock_location_type_list)}
|
||||||
|
tableState={table}
|
||||||
|
columns={tableColumns}
|
||||||
|
props={{
|
||||||
|
rowActions: rowActions,
|
||||||
|
tableActions: tableActions
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user