AttributeGrid
This commit is contained in:
parent
448d500665
commit
194780f0b7
|
|
@ -0,0 +1,40 @@
|
|||
import { Divider, SimpleGrid, Text, Title } from '@mantine/core';
|
||||
import type { ReactNode } from 'react';
|
||||
|
||||
export interface AttributeRow {
|
||||
label: string;
|
||||
value: ReactNode | null | undefined;
|
||||
}
|
||||
|
||||
export function AttributeGrid({
|
||||
title,
|
||||
items
|
||||
}: Readonly<{
|
||||
title: string;
|
||||
items: AttributeRow[];
|
||||
}>) {
|
||||
const valid = items.filter(
|
||||
(item) => item.value !== null && item.value !== undefined
|
||||
);
|
||||
|
||||
if (valid.length === 0) return null;
|
||||
|
||||
return (
|
||||
<>
|
||||
<Divider />
|
||||
<Title order={4}>{title}</Title>
|
||||
<SimpleGrid cols={2} spacing='xs'>
|
||||
{valid.map((item) => (
|
||||
<>
|
||||
<Text key={`${item.label}-label`} fw={'bold'} size='sm'>
|
||||
{item.label}
|
||||
</Text>
|
||||
<Text key={`${item.label}-value`} size='sm'>
|
||||
{item.value}
|
||||
</Text>
|
||||
</>
|
||||
))}
|
||||
</SimpleGrid>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
@ -0,0 +1,160 @@
|
|||
import {
|
||||
ApiEndpoints,
|
||||
ModelType,
|
||||
TagsList,
|
||||
apiUrl,
|
||||
formatDecimal
|
||||
} from '@lib/index';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { Group, Stack, Text } from '@mantine/core';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { useApi } from '../../../contexts/ApiContext';
|
||||
import { useInstance } from '../../../hooks/UseInstance';
|
||||
import { ApiImage } from '../../images/ApiImage';
|
||||
import { AttributeGrid, type AttributeRow } from '../AttributeGrid';
|
||||
import type { PreviewType } from '../PreviewType';
|
||||
|
||||
function PartPreviewContent({ instance }: Readonly<{ instance: any }>) {
|
||||
const api = useApi();
|
||||
|
||||
const { instance: requirements } = useInstance({
|
||||
endpoint: ApiEndpoints.part_requirements,
|
||||
pk: instance?.pk,
|
||||
hasPrimaryKey: true,
|
||||
disabled: !instance?.pk,
|
||||
defaultValue: {}
|
||||
});
|
||||
|
||||
const { data: parameters = [] } = useQuery({
|
||||
queryKey: ['part-preview-parameters', instance?.pk],
|
||||
enabled: !!instance?.pk,
|
||||
queryFn: () =>
|
||||
api
|
||||
.get(apiUrl(ApiEndpoints.parameter_list), {
|
||||
params: {
|
||||
model_type: ModelType.part,
|
||||
model_id: instance.pk,
|
||||
limit: 100
|
||||
}
|
||||
})
|
||||
.then((res) => res.data?.results ?? [])
|
||||
});
|
||||
|
||||
const totalStock = requirements?.total_stock ?? instance?.total_in_stock ?? 0;
|
||||
const availableStock =
|
||||
requirements?.unallocated_stock ?? instance?.unallocated_stock ?? 0;
|
||||
const minStock = instance?.minimum_stock ?? 0;
|
||||
const ordering = requirements?.ordering ?? 0;
|
||||
const isPurchaseable = instance?.purchaseable ?? false;
|
||||
|
||||
const category = instance?.category_detail;
|
||||
|
||||
const stockItems: AttributeRow[] = !instance?.virtual
|
||||
? [
|
||||
{
|
||||
label: t`In Stock`,
|
||||
value:
|
||||
formatDecimal(totalStock) +
|
||||
(instance?.units ? ` ${instance.units}` : '')
|
||||
},
|
||||
availableStock !== totalStock
|
||||
? {
|
||||
label: t`Available`,
|
||||
value:
|
||||
formatDecimal(availableStock) +
|
||||
(instance?.units ? ` ${instance.units}` : '')
|
||||
}
|
||||
: null,
|
||||
minStock > 0
|
||||
? {
|
||||
label: t`Minimum`,
|
||||
value:
|
||||
formatDecimal(minStock) +
|
||||
(instance?.units ? ` ${instance.units}` : '')
|
||||
}
|
||||
: null,
|
||||
isPurchaseable && ordering > 0
|
||||
? {
|
||||
label: t`On Order`,
|
||||
value:
|
||||
formatDecimal(ordering) +
|
||||
(instance?.units ? ` ${instance.units}` : '')
|
||||
}
|
||||
: null
|
||||
].filter(Boolean)
|
||||
: [];
|
||||
|
||||
const parameterItems: AttributeRow[] = parameters.map((param: any) => ({
|
||||
label: param.template_detail?.name,
|
||||
value:
|
||||
param.data +
|
||||
(param.template_detail?.units ? ` ${param.template_detail.units}` : '')
|
||||
}));
|
||||
|
||||
return (
|
||||
<Stack gap='md'>
|
||||
{/* Headline: image + name / description / IPN / category */}
|
||||
<Group align='flex-start' gap='md'>
|
||||
<ApiImage
|
||||
src={
|
||||
instance?.image ||
|
||||
instance?.thumbnail ||
|
||||
'/static/img/blank_image.png'
|
||||
}
|
||||
w={80}
|
||||
h={80}
|
||||
fit='contain'
|
||||
radius='sm'
|
||||
/>
|
||||
<Stack gap={2} style={{ flex: 1 }}>
|
||||
<Text fw={700} size='lg'>
|
||||
{instance?.full_name || instance?.name}
|
||||
</Text>
|
||||
{instance?.description && (
|
||||
<Text c='dimmed' size='sm'>
|
||||
{instance.description}
|
||||
</Text>
|
||||
)}
|
||||
{instance?.IPN && (
|
||||
<Group gap='xs'>
|
||||
<Text size='xs' c='dimmed'>
|
||||
{t`IPN`}:
|
||||
</Text>
|
||||
<Text size='xs' fw={500}>
|
||||
{instance.IPN}
|
||||
</Text>
|
||||
</Group>
|
||||
)}
|
||||
{category && (
|
||||
<Group gap='xs'>
|
||||
<Text size='xs' c='dimmed'>
|
||||
{t`Category`}:
|
||||
</Text>
|
||||
<Text size='xs'>{category.pathstring ?? category.name}</Text>
|
||||
</Group>
|
||||
)}
|
||||
</Stack>
|
||||
</Group>
|
||||
|
||||
{instance?.tags?.length > 0 && <TagsList tags={instance.tags} />}
|
||||
|
||||
{!instance?.virtual && (
|
||||
<AttributeGrid title={t`Stock Information`} items={stockItems} />
|
||||
)}
|
||||
<AttributeGrid title={t`Parameters`} items={parameterItems} />
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
export function PartPreviewComponent({
|
||||
instance,
|
||||
modelId
|
||||
}: Readonly<{
|
||||
instance: any;
|
||||
modelId: number;
|
||||
}>): PreviewType {
|
||||
return {
|
||||
title: instance?.full_name || instance?.name || `Part #${modelId}`,
|
||||
preview: <PartPreviewContent instance={instance} />
|
||||
};
|
||||
}
|
||||
|
|
@ -1,15 +1,18 @@
|
|||
import type { ModelType } from '@lib/enums/ModelType';
|
||||
import { create } from 'zustand';
|
||||
import type { PreviewType } from '../components/previews/PreviewType';
|
||||
|
||||
interface PreviewDrawerStateProps {
|
||||
isOpen: boolean;
|
||||
modelType?: ModelType;
|
||||
id?: number | string;
|
||||
instance?: any;
|
||||
preview?: PreviewType;
|
||||
openPreview: (
|
||||
modelType: ModelType,
|
||||
id: number | string | undefined,
|
||||
instance?: any
|
||||
instance?: any,
|
||||
preview?: PreviewType
|
||||
) => void;
|
||||
closePreview: () => void;
|
||||
}
|
||||
|
|
@ -20,13 +23,15 @@ export const usePreviewDrawerState = create<PreviewDrawerStateProps>()(
|
|||
modelType: undefined,
|
||||
id: undefined,
|
||||
instance: undefined,
|
||||
preview: undefined,
|
||||
|
||||
openPreview: (
|
||||
modelType: ModelType,
|
||||
id: number | string | undefined,
|
||||
instance?: any
|
||||
instance?: any,
|
||||
preview?: PreviewType
|
||||
) => {
|
||||
set({ modelType, id, instance, isOpen: true });
|
||||
set({ modelType, id, instance, preview, isOpen: true });
|
||||
},
|
||||
|
||||
closePreview: () => {
|
||||
|
|
@ -38,9 +43,12 @@ export const usePreviewDrawerState = create<PreviewDrawerStateProps>()(
|
|||
export function openGlobalPreview(
|
||||
modelType: ModelType,
|
||||
id?: number | string | undefined,
|
||||
instance?: any
|
||||
instance?: any,
|
||||
preview?: PreviewType
|
||||
) {
|
||||
usePreviewDrawerState.getState().openPreview(modelType, id, instance);
|
||||
usePreviewDrawerState
|
||||
.getState()
|
||||
.openPreview(modelType, id, instance, preview);
|
||||
}
|
||||
|
||||
export function closeGlobalPreview() {
|
||||
|
|
|
|||
Loading…
Reference in New Issue