location and category
This commit is contained in:
parent
763fab2033
commit
56ed757776
|
|
@ -3,11 +3,13 @@ import type { ReactNode } from 'react';
|
|||
import { BuildOrderPreviewComponent } from './models/BuildOrderPreview';
|
||||
import { CompanyPreviewComponent } from './models/CompanyPreview';
|
||||
import { ManufacturerPartPreviewComponent } from './models/ManufacturerPartPreview';
|
||||
import { PartCategoryPreviewComponent } from './models/PartCategoryPreview';
|
||||
import { PartPreviewComponent } from './models/PartPreview';
|
||||
import { PurchaseOrderPreviewComponent } from './models/PurchaseOrderPreview';
|
||||
import { ReturnOrderPreviewComponent } from './models/ReturnOrderPreview';
|
||||
import { SalesOrderPreviewComponent } from './models/SalesOrderPreview';
|
||||
import { SalesOrderShipmentPreviewComponent } from './models/SalesOrderShipmentPreview';
|
||||
import { StockLocationPreviewComponent } from './models/StockLocationPreview';
|
||||
import { StockPreviewComponent } from './models/StockPreview';
|
||||
import { SupplierPartPreviewComponent } from './models/SupplierPartPreview';
|
||||
import { TransferOrderPreviewComponent } from './models/TransferOrderPreview';
|
||||
|
|
@ -55,6 +57,10 @@ export function getPreviewComponentForModel({
|
|||
return SalesOrderShipmentPreviewComponent({ instance, modelId });
|
||||
case ModelType.transferorder:
|
||||
return TransferOrderPreviewComponent({ instance, modelId });
|
||||
case ModelType.stocklocation:
|
||||
return StockLocationPreviewComponent({ instance, modelId });
|
||||
case ModelType.partcategory:
|
||||
return PartCategoryPreviewComponent({ instance, modelId });
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,18 @@
|
|||
import { t } from '@lingui/core/macro';
|
||||
import { PartCategoryDetailsPanel } from '../../../pages/part/PartCategoryDetailsPanel';
|
||||
import type { PreviewType } from '../PreviewType';
|
||||
|
||||
export function PartCategoryPreviewComponent({
|
||||
instance,
|
||||
modelId
|
||||
}: Readonly<{
|
||||
instance: any;
|
||||
modelId: number;
|
||||
}>): PreviewType {
|
||||
const name = `${t`Part Category`} - ${instance?.name ?? `#${modelId}`}`;
|
||||
|
||||
return {
|
||||
title: name,
|
||||
preview: <PartCategoryDetailsPanel instance={instance} />
|
||||
};
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
import { t } from '@lingui/core/macro';
|
||||
import { StockLocationDetailsPanel } from '../../../pages/stock/StockLocationDetailsPanel';
|
||||
import type { PreviewType } from '../PreviewType';
|
||||
|
||||
export function StockLocationPreviewComponent({
|
||||
instance,
|
||||
modelId
|
||||
}: Readonly<{
|
||||
instance: any;
|
||||
modelId: number;
|
||||
}>): PreviewType {
|
||||
const name = `${t`Stock Location`} - ${instance?.name ?? `#${modelId}`}`;
|
||||
|
||||
return {
|
||||
title: name,
|
||||
preview: <StockLocationDetailsPanel instance={instance} />
|
||||
};
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
import { t } from '@lingui/core/macro';
|
||||
import { Group, LoadingOverlay, Skeleton, Stack } from '@mantine/core';
|
||||
import { LoadingOverlay, Stack } from '@mantine/core';
|
||||
import {
|
||||
IconCategory,
|
||||
IconInfoCircle,
|
||||
|
|
@ -21,8 +21,6 @@ import type { PanelType } from '@lib/types/Panel';
|
|||
import { useLocalStorage } from '@mantine/hooks';
|
||||
import AdminButton from '../../components/buttons/AdminButton';
|
||||
import StarredToggleButton from '../../components/buttons/StarredToggleButton';
|
||||
import type { DetailsField } from '../../components/details/Details';
|
||||
import { ItemDetailsGrid } from '../../components/details/ItemDetails';
|
||||
import {
|
||||
DeleteItemAction,
|
||||
EditItemAction,
|
||||
|
|
@ -49,6 +47,7 @@ import { PartCategoryTable } from '../../tables/part/PartCategoryTable';
|
|||
import PartCategoryTemplateTable from '../../tables/part/PartCategoryTemplateTable';
|
||||
import { PartListTable } from '../../tables/part/PartTable';
|
||||
import { StockItemTable } from '../../tables/stock/StockItemTable';
|
||||
import { PartCategoryDetailsPanel } from './PartCategoryDetailsPanel';
|
||||
|
||||
/**
|
||||
* Detail view for a single PartCategory instance.
|
||||
|
|
@ -103,106 +102,6 @@ export default function CategoryDetail() {
|
|||
assign: false
|
||||
});
|
||||
|
||||
const detailsPanel = useMemo(() => {
|
||||
if (id && instanceQuery.isFetching) {
|
||||
return <Skeleton />;
|
||||
}
|
||||
|
||||
const left: DetailsField[] = [
|
||||
{
|
||||
type: 'text',
|
||||
name: 'name',
|
||||
label: t`Name`,
|
||||
copy: true,
|
||||
value_formatter: () => (
|
||||
<Group gap='xs'>
|
||||
{category.icon && <ApiIcon name={category.icon} />}
|
||||
{category.name}
|
||||
</Group>
|
||||
)
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
name: 'pathstring',
|
||||
label: t`Path`,
|
||||
icon: 'sitemap',
|
||||
copy: true,
|
||||
hidden: !id
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
name: 'description',
|
||||
label: t`Description`,
|
||||
copy: true
|
||||
},
|
||||
{
|
||||
type: 'link',
|
||||
name: 'parent',
|
||||
model_field: 'name',
|
||||
icon: 'location',
|
||||
label: t`Parent Category`,
|
||||
model: ModelType.partcategory,
|
||||
hidden: !category?.parent
|
||||
},
|
||||
{
|
||||
type: 'boolean',
|
||||
name: 'starred',
|
||||
icon: 'notification',
|
||||
label: t`Subscribed`
|
||||
}
|
||||
];
|
||||
|
||||
const right: DetailsField[] = [
|
||||
{
|
||||
type: 'text',
|
||||
name: 'part_count',
|
||||
label: t`Parts`,
|
||||
icon: 'part',
|
||||
value_formatter: () => category?.part_count || '0'
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
name: 'subcategories',
|
||||
label: t`Subcategories`,
|
||||
icon: 'sitemap',
|
||||
hidden: !category?.subcategories
|
||||
},
|
||||
{
|
||||
type: 'boolean',
|
||||
name: 'structural',
|
||||
label: t`Structural`,
|
||||
icon: 'sitemap'
|
||||
},
|
||||
{
|
||||
type: 'link',
|
||||
name: 'parent_default_location',
|
||||
label: t`Parent default location`,
|
||||
model: ModelType.stocklocation,
|
||||
hidden: !category.parent_default_location || category.default_location
|
||||
},
|
||||
{
|
||||
type: 'link',
|
||||
name: 'default_location',
|
||||
label: t`Default location`,
|
||||
model: ModelType.stocklocation,
|
||||
hidden: !category.default_location
|
||||
}
|
||||
];
|
||||
|
||||
return (
|
||||
<ItemDetailsGrid
|
||||
tables={
|
||||
id && category?.pk
|
||||
? [
|
||||
{ item: category, fields: left },
|
||||
{ item: category, fields: right }
|
||||
]
|
||||
: []
|
||||
}
|
||||
/>
|
||||
);
|
||||
}, [category, instanceQuery]);
|
||||
|
||||
const editCategory = useEditApiFormModal({
|
||||
url: ApiEndpoints.category_list,
|
||||
pk: id,
|
||||
|
|
@ -299,7 +198,7 @@ export default function CategoryDetail() {
|
|||
name: 'details',
|
||||
label: t`Category Details`,
|
||||
icon: <IconInfoCircle />,
|
||||
content: detailsPanel,
|
||||
content: <PartCategoryDetailsPanel instance={category} />,
|
||||
hidden: !id || !category?.pk
|
||||
},
|
||||
{
|
||||
|
|
|
|||
|
|
@ -0,0 +1,121 @@
|
|||
import { t } from '@lingui/core/macro';
|
||||
import { Group, Skeleton } from '@mantine/core';
|
||||
import { useMemo } from 'react';
|
||||
|
||||
import { ModelType } from '@lib/enums/ModelType';
|
||||
|
||||
import type { DetailsField } from '../../components/details/Details';
|
||||
import { ItemDetailsGrid } from '../../components/details/ItemDetails';
|
||||
import { useParameterDetailsGrid } from '../../components/details/ParameterDetailsGrid';
|
||||
import { ApiIcon } from '../../components/items/ApiIcon';
|
||||
|
||||
export function PartCategoryDetailsPanel({
|
||||
instance
|
||||
}: Readonly<{
|
||||
instance: any;
|
||||
}>) {
|
||||
const left: DetailsField[] = useMemo(
|
||||
() => [
|
||||
{
|
||||
type: 'text',
|
||||
name: 'name',
|
||||
label: t`Name`,
|
||||
copy: true,
|
||||
value_formatter: () => (
|
||||
<Group gap='xs'>
|
||||
{instance?.icon && <ApiIcon name={instance.icon} />}
|
||||
{instance?.name}
|
||||
</Group>
|
||||
)
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
name: 'pathstring',
|
||||
label: t`Path`,
|
||||
icon: 'sitemap',
|
||||
copy: true,
|
||||
hidden: !instance?.pathstring
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
name: 'description',
|
||||
label: t`Description`,
|
||||
copy: true
|
||||
},
|
||||
{
|
||||
type: 'link',
|
||||
name: 'parent',
|
||||
model_field: 'name',
|
||||
icon: 'location',
|
||||
label: t`Parent Category`,
|
||||
model: ModelType.partcategory,
|
||||
hidden: !instance?.parent
|
||||
},
|
||||
{
|
||||
type: 'boolean',
|
||||
name: 'starred',
|
||||
icon: 'notification',
|
||||
label: t`Subscribed`
|
||||
}
|
||||
],
|
||||
[instance]
|
||||
);
|
||||
|
||||
const right: DetailsField[] = useMemo(
|
||||
() => [
|
||||
{
|
||||
type: 'text',
|
||||
name: 'part_count',
|
||||
label: t`Parts`,
|
||||
icon: 'part',
|
||||
value_formatter: () => instance?.part_count || '0'
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
name: 'subcategories',
|
||||
label: t`Subcategories`,
|
||||
icon: 'sitemap',
|
||||
hidden: !instance?.subcategories
|
||||
},
|
||||
{
|
||||
type: 'boolean',
|
||||
name: 'structural',
|
||||
label: t`Structural`,
|
||||
icon: 'sitemap'
|
||||
},
|
||||
{
|
||||
type: 'link',
|
||||
name: 'parent_default_location',
|
||||
label: t`Parent default location`,
|
||||
model: ModelType.stocklocation,
|
||||
hidden:
|
||||
!instance?.parent_default_location || !!instance?.default_location
|
||||
},
|
||||
{
|
||||
type: 'link',
|
||||
name: 'default_location',
|
||||
label: t`Default location`,
|
||||
model: ModelType.stocklocation,
|
||||
hidden: !instance?.default_location
|
||||
}
|
||||
],
|
||||
[instance]
|
||||
);
|
||||
|
||||
const parametersTable = useParameterDetailsGrid({
|
||||
model_type: ModelType.partcategory,
|
||||
model_id: instance?.pk
|
||||
});
|
||||
|
||||
if (!instance?.pk) return <Skeleton />;
|
||||
|
||||
return (
|
||||
<ItemDetailsGrid
|
||||
tables={[
|
||||
{ item: instance, fields: left },
|
||||
{ item: instance, fields: right },
|
||||
parametersTable
|
||||
]}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
@ -7,7 +7,7 @@ import type { TableFilter } from '@lib/index';
|
|||
import type { StockOperationProps } from '@lib/types/Forms';
|
||||
import type { PanelType } from '@lib/types/Panel';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { Group, Skeleton, Stack } from '@mantine/core';
|
||||
import { Skeleton, Stack } from '@mantine/core';
|
||||
import {
|
||||
IconCalendar,
|
||||
IconInfoCircle,
|
||||
|
|
@ -24,8 +24,6 @@ import { useBarcodeScanDialog } from '../../components/barcodes/BarcodeScanDialo
|
|||
import AdminButton from '../../components/buttons/AdminButton';
|
||||
import { PrintingActions } from '../../components/buttons/PrintingActions';
|
||||
import OrderCalendar from '../../components/calendar/OrderCalendar';
|
||||
import type { DetailsField } from '../../components/details/Details';
|
||||
import { ItemDetailsGrid } from '../../components/details/ItemDetails';
|
||||
import {
|
||||
BarcodeActionDropdown,
|
||||
DeleteItemAction,
|
||||
|
|
@ -57,6 +55,7 @@ import StockLocationParametricTable from '../../tables/stock/StockLocationParame
|
|||
import { StockLocationTable } from '../../tables/stock/StockLocationTable';
|
||||
import TransferOrderParametricTable from '../../tables/stock/TransferOrderParametricTable';
|
||||
import { TransferOrderTable } from '../../tables/stock/TransferOrderTable';
|
||||
import { StockLocationDetailsPanel } from './StockLocationDetailsPanel';
|
||||
|
||||
function TransferOrderCalendar() {
|
||||
const calendarFilters: TableFilter[] = useMemo(() => {
|
||||
|
|
@ -102,98 +101,12 @@ export default function Stock() {
|
|||
}
|
||||
});
|
||||
|
||||
const detailsPanel = useMemo(() => {
|
||||
if (id && instanceQuery.isFetching) {
|
||||
return <Skeleton />;
|
||||
}
|
||||
|
||||
const left: DetailsField[] = [
|
||||
{
|
||||
type: 'text',
|
||||
name: 'name',
|
||||
label: t`Name`,
|
||||
copy: true,
|
||||
value_formatter: () => (
|
||||
<Group gap='xs'>
|
||||
{location.icon && <ApiIcon name={location.icon} />}
|
||||
{location.name}
|
||||
</Group>
|
||||
)
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
name: 'pathstring',
|
||||
label: t`Path`,
|
||||
icon: 'sitemap',
|
||||
copy: true,
|
||||
hidden: !id
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
name: 'description',
|
||||
label: t`Description`,
|
||||
copy: true
|
||||
},
|
||||
{
|
||||
type: 'link',
|
||||
name: 'parent',
|
||||
model_field: 'name',
|
||||
icon: 'location',
|
||||
label: t`Parent Location`,
|
||||
model: ModelType.stocklocation,
|
||||
hidden: !location?.parent
|
||||
}
|
||||
];
|
||||
|
||||
const right: DetailsField[] = [
|
||||
{
|
||||
type: 'text',
|
||||
name: 'items',
|
||||
icon: 'stock',
|
||||
label: t`Stock Items`,
|
||||
value_formatter: () => location?.items || '0'
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
name: 'sublocations',
|
||||
icon: 'location',
|
||||
label: t`Sublocations`,
|
||||
hidden: !location?.sublocations
|
||||
},
|
||||
{
|
||||
type: 'boolean',
|
||||
name: 'structural',
|
||||
label: t`Structural`,
|
||||
icon: 'sitemap'
|
||||
},
|
||||
{
|
||||
type: 'boolean',
|
||||
name: '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'
|
||||
}
|
||||
];
|
||||
|
||||
return (
|
||||
<ItemDetailsGrid
|
||||
tables={
|
||||
id && location?.pk
|
||||
? [
|
||||
{ item: location, fields: left },
|
||||
{ item: location, fields: right }
|
||||
]
|
||||
: []
|
||||
}
|
||||
/>
|
||||
const detailsPanel =
|
||||
id && instanceQuery.isFetching ? (
|
||||
<Skeleton />
|
||||
) : (
|
||||
<StockLocationDetailsPanel instance={id ? location : undefined} />
|
||||
);
|
||||
}, [location, instanceQuery]);
|
||||
|
||||
const [sublocationView, setSublocationView] = useState<string>('table');
|
||||
const [transferOrderView, setTransferOrderView] = useState<string>('table');
|
||||
|
|
|
|||
|
|
@ -0,0 +1,106 @@
|
|||
import { t } from '@lingui/core/macro';
|
||||
import { Group, Skeleton } from '@mantine/core';
|
||||
|
||||
import { ModelType } from '@lib/enums/ModelType';
|
||||
|
||||
import type { DetailsField } from '../../components/details/Details';
|
||||
import { ItemDetailsGrid } from '../../components/details/ItemDetails';
|
||||
import { useParameterDetailsGrid } from '../../components/details/ParameterDetailsGrid';
|
||||
import { ApiIcon } from '../../components/items/ApiIcon';
|
||||
|
||||
export function StockLocationDetailsPanel({
|
||||
instance
|
||||
}: Readonly<{
|
||||
instance: any;
|
||||
}>) {
|
||||
const left: DetailsField[] = [
|
||||
{
|
||||
type: 'text',
|
||||
name: 'name',
|
||||
label: t`Name`,
|
||||
copy: true,
|
||||
value_formatter: () => (
|
||||
<Group gap='xs'>
|
||||
{instance?.icon && <ApiIcon name={instance.icon} />}
|
||||
{instance?.name}
|
||||
</Group>
|
||||
)
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
name: 'pathstring',
|
||||
label: t`Path`,
|
||||
icon: 'sitemap',
|
||||
copy: true,
|
||||
hidden: !instance?.pathstring
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
name: 'description',
|
||||
label: t`Description`,
|
||||
copy: true,
|
||||
hidden: !instance?.description
|
||||
},
|
||||
{
|
||||
type: 'link',
|
||||
name: 'parent',
|
||||
model_field: 'name',
|
||||
icon: 'location',
|
||||
label: t`Parent Location`,
|
||||
model: ModelType.stocklocation,
|
||||
hidden: !instance?.parent
|
||||
}
|
||||
];
|
||||
|
||||
const right: DetailsField[] = [
|
||||
{
|
||||
type: 'text',
|
||||
name: 'items',
|
||||
icon: 'stock',
|
||||
label: t`Stock Items`,
|
||||
value_formatter: () => instance?.items || '0'
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
name: 'sublocations',
|
||||
icon: 'location',
|
||||
label: t`Sublocations`,
|
||||
hidden: !instance?.sublocations
|
||||
},
|
||||
{
|
||||
type: 'boolean',
|
||||
name: 'structural',
|
||||
label: t`Structural`,
|
||||
icon: 'sitemap'
|
||||
},
|
||||
{
|
||||
type: 'boolean',
|
||||
name: 'external',
|
||||
label: t`External`
|
||||
},
|
||||
{
|
||||
type: 'string',
|
||||
name: 'location_type_detail.name',
|
||||
label: t`Location Type`,
|
||||
hidden: !instance?.location_type,
|
||||
icon: 'packages'
|
||||
}
|
||||
];
|
||||
|
||||
const parametersTable = useParameterDetailsGrid({
|
||||
model_type: ModelType.stocklocation,
|
||||
model_id: instance?.pk
|
||||
});
|
||||
|
||||
if (!instance?.pk) return <Skeleton />;
|
||||
|
||||
return (
|
||||
<ItemDetailsGrid
|
||||
tables={[
|
||||
{ item: instance, fields: left },
|
||||
{ item: instance, fields: right },
|
||||
parametersTable
|
||||
]}
|
||||
/>
|
||||
);
|
||||
}
|
||||
Loading…
Reference in New Issue