mirror of
https://github.com/inventree/InvenTree.git
synced 2025-06-17 04:25:42 +00:00
Part Category detail
This commit is contained in:
@ -74,6 +74,7 @@ class CategorySerializer(InvenTree.serializers.InvenTreeModelSerializer):
|
|||||||
'level',
|
'level',
|
||||||
'parent',
|
'parent',
|
||||||
'part_count',
|
'part_count',
|
||||||
|
'subcategories',
|
||||||
'pathstring',
|
'pathstring',
|
||||||
'path',
|
'path',
|
||||||
'starred',
|
'starred',
|
||||||
@ -99,13 +100,18 @@ class CategorySerializer(InvenTree.serializers.InvenTreeModelSerializer):
|
|||||||
def annotate_queryset(queryset):
|
def annotate_queryset(queryset):
|
||||||
"""Annotate extra information to the queryset."""
|
"""Annotate extra information to the queryset."""
|
||||||
# Annotate the number of 'parts' which exist in each category (including subcategories!)
|
# Annotate the number of 'parts' which exist in each category (including subcategories!)
|
||||||
queryset = queryset.annotate(part_count=part.filters.annotate_category_parts())
|
queryset = queryset.annotate(
|
||||||
|
part_count=part.filters.annotate_category_parts(),
|
||||||
|
subcategories=part.filters.annotate_sub_categories(),
|
||||||
|
)
|
||||||
|
|
||||||
return queryset
|
return queryset
|
||||||
|
|
||||||
url = serializers.CharField(source='get_absolute_url', read_only=True)
|
url = serializers.CharField(source='get_absolute_url', read_only=True)
|
||||||
|
|
||||||
part_count = serializers.IntegerField(read_only=True)
|
part_count = serializers.IntegerField(read_only=True, label=_('Parts'))
|
||||||
|
|
||||||
|
subcategories = serializers.IntegerField(read_only=True, label=_('Subcategories'))
|
||||||
|
|
||||||
level = serializers.IntegerField(read_only=True)
|
level = serializers.IntegerField(read_only=True)
|
||||||
|
|
||||||
|
@ -1,21 +1,24 @@
|
|||||||
import { t } from '@lingui/macro';
|
import { t } from '@lingui/macro';
|
||||||
import { LoadingOverlay, Stack, Text } from '@mantine/core';
|
import { LoadingOverlay, Skeleton, Stack, Text } from '@mantine/core';
|
||||||
import {
|
import {
|
||||||
IconCategory,
|
IconCategory,
|
||||||
|
IconInfoCircle,
|
||||||
IconListDetails,
|
IconListDetails,
|
||||||
IconSitemap
|
IconSitemap
|
||||||
} from '@tabler/icons-react';
|
} from '@tabler/icons-react';
|
||||||
import { useMemo, useState } from 'react';
|
import { useMemo, useState } from 'react';
|
||||||
import { useParams } from 'react-router-dom';
|
import { useParams } from 'react-router-dom';
|
||||||
|
|
||||||
|
import { ItemDetailsGrid } from '../../components/details/ItemDetails';
|
||||||
import { PageDetail } from '../../components/nav/PageDetail';
|
import { PageDetail } from '../../components/nav/PageDetail';
|
||||||
import { PanelGroup, PanelType } from '../../components/nav/PanelGroup';
|
import { PanelGroup, PanelType } from '../../components/nav/PanelGroup';
|
||||||
import { PartCategoryTree } from '../../components/nav/PartCategoryTree';
|
import { PartCategoryTree } from '../../components/nav/PartCategoryTree';
|
||||||
import { ApiEndpoints } from '../../enums/ApiEndpoints';
|
import { ApiEndpoints } from '../../enums/ApiEndpoints';
|
||||||
|
import { ModelType } from '../../enums/ModelType';
|
||||||
import { useInstance } from '../../hooks/UseInstance';
|
import { useInstance } from '../../hooks/UseInstance';
|
||||||
|
import { DetailsField, DetailsTable } from '../../tables/Details';
|
||||||
import ParametricPartTable from '../../tables/part/ParametricPartTable';
|
import ParametricPartTable from '../../tables/part/ParametricPartTable';
|
||||||
import { PartCategoryTable } from '../../tables/part/PartCategoryTable';
|
import { PartCategoryTable } from '../../tables/part/PartCategoryTable';
|
||||||
import { PartParameterTable } from '../../tables/part/PartParameterTable';
|
|
||||||
import { PartListTable } from '../../tables/part/PartTable';
|
import { PartListTable } from '../../tables/part/PartTable';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -45,8 +48,86 @@ export default function CategoryDetail({}: {}) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const detailsPanel = useMemo(() => {
|
||||||
|
if (id && instanceQuery.isFetching) {
|
||||||
|
return <Skeleton />;
|
||||||
|
}
|
||||||
|
|
||||||
|
let left: DetailsField[] = [
|
||||||
|
{
|
||||||
|
type: 'text',
|
||||||
|
name: 'name',
|
||||||
|
label: t`Name`,
|
||||||
|
copy: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
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
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
let right: DetailsField[] = [
|
||||||
|
{
|
||||||
|
type: 'text',
|
||||||
|
name: 'part_count',
|
||||||
|
label: t`Parts`,
|
||||||
|
icon: 'part'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'text',
|
||||||
|
name: 'subcategories',
|
||||||
|
label: t`Subcategories`,
|
||||||
|
icon: 'sitemap',
|
||||||
|
hidden: !category?.subcategories
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'boolean',
|
||||||
|
name: 'structural',
|
||||||
|
label: t`Structural`,
|
||||||
|
icon: 'sitemap'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ItemDetailsGrid>
|
||||||
|
{id && category?.pk ? (
|
||||||
|
<DetailsTable item={category} fields={left} />
|
||||||
|
) : (
|
||||||
|
<Text>{t`Top level part category`}</Text>
|
||||||
|
)}
|
||||||
|
{id && category?.pk && <DetailsTable item={category} fields={right} />}
|
||||||
|
</ItemDetailsGrid>
|
||||||
|
);
|
||||||
|
}, [category, instanceQuery]);
|
||||||
|
|
||||||
const categoryPanels: PanelType[] = useMemo(
|
const categoryPanels: PanelType[] = useMemo(
|
||||||
() => [
|
() => [
|
||||||
|
{
|
||||||
|
name: 'details',
|
||||||
|
label: t`Details`,
|
||||||
|
icon: <IconInfoCircle />,
|
||||||
|
content: detailsPanel
|
||||||
|
// hidden: !category?.pk,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'parts',
|
name: 'parts',
|
||||||
label: t`Parts`,
|
label: t`Parts`,
|
||||||
|
@ -104,12 +104,12 @@ export default function Stock() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<ItemDetailsGrid>
|
<ItemDetailsGrid>
|
||||||
{id && location ? (
|
{id && location?.pk ? (
|
||||||
<DetailsTable item={location} fields={left} />
|
<DetailsTable item={location} fields={left} />
|
||||||
) : (
|
) : (
|
||||||
<Text>{t`Top level stock location`}</Text>
|
<Text>{t`Top level stock location`}</Text>
|
||||||
)}
|
)}
|
||||||
{id && location && <DetailsTable item={location} fields={right} />}
|
{id && location?.pk && <DetailsTable item={location} fields={right} />}
|
||||||
</ItemDetailsGrid>
|
</ItemDetailsGrid>
|
||||||
);
|
);
|
||||||
}, [location, instanceQuery]);
|
}, [location, instanceQuery]);
|
||||||
|
Reference in New Issue
Block a user