diff --git a/src/frontend/src/components/calendar/OrderCalendar.tsx b/src/frontend/src/components/calendar/OrderCalendar.tsx
index f855691dd4..c9d26f0ca2 100644
--- a/src/frontend/src/components/calendar/OrderCalendar.tsx
+++ b/src/frontend/src/components/calendar/OrderCalendar.tsx
@@ -7,7 +7,11 @@ import { ModelInformationDict } from '@lib/enums/ModelInformation';
import type { ModelType } from '@lib/enums/ModelType';
import type { UserRoles } from '@lib/enums/Roles';
import { apiUrl } from '@lib/functions/Api';
-import { getDetailUrl, navigateToLink } from '@lib/functions/Navigation';
+import {
+ eventModified,
+ getDetailUrl,
+ navigateToLink
+} from '@lib/functions/Navigation';
import type { TableFilter } from '@lib/types/Filters';
import { t } from '@lingui/core/macro';
import { ActionIcon, Group, Text } from '@mantine/core';
@@ -171,7 +175,7 @@ export default function OrderCalendar({
const onClickOrder = (info: EventClickArg) => {
if (!info.event.id) return;
- if (info.jsEvent.ctrlKey || info.jsEvent.metaKey || info.jsEvent.shiftKey) {
+ if (eventModified(event as any)) {
navigateToLink(
getDetailUrl(model, info.event.id),
navigate,
diff --git a/src/frontend/src/components/previews/AttributeGrid.tsx b/src/frontend/src/components/previews/AttributeGrid.tsx
index 0da3aeead5..93b7578712 100644
--- a/src/frontend/src/components/previews/AttributeGrid.tsx
+++ b/src/frontend/src/components/previews/AttributeGrid.tsx
@@ -28,10 +28,10 @@ export function AttributeGrid({
{valid.map((item) => (
-
+
{item.label}
- {item.value}
+ {item.value}
))}
diff --git a/src/frontend/src/components/previews/PreviewDrawer.tsx b/src/frontend/src/components/previews/PreviewDrawer.tsx
index ebcb8ee58c..fe3c97e40c 100644
--- a/src/frontend/src/components/previews/PreviewDrawer.tsx
+++ b/src/frontend/src/components/previews/PreviewDrawer.tsx
@@ -1,8 +1,20 @@
-import { Anchor, Divider, Drawer, LoadingOverlay, Stack } from '@mantine/core';
+import {
+ ActionIcon,
+ Anchor,
+ Divider,
+ Drawer,
+ Group,
+ LoadingOverlay,
+ Stack,
+ Tooltip
+} from '@mantine/core';
import { StylishText } from '@lib/components/StylishText';
import { ModelInformationDict } from '@lib/enums/ModelInformation';
+import { eventModified } from '@lib/functions/Navigation';
import { type ModelType, getDetailUrl, navigateToLink } from '@lib/index';
+import { t } from '@lingui/core/macro';
+import { IconArrowRight } from '@tabler/icons-react';
import { useCallback, useMemo } from 'react';
import { useNavigate } from 'react-router-dom';
import { useInstance } from '../../hooks/UseInstance';
@@ -73,7 +85,9 @@ export default function PreviewDrawer({
(event: MouseEvent) => {
if (!modelType || !id) return;
- onClose();
+ if (!eventModified(event as any)) {
+ onClose();
+ }
navigateToLink(getDetailUrl(modelType!, id!), navigate, event);
},
[modelType, id, navigate]
@@ -90,7 +104,14 @@ export default function PreviewDrawer({
href={getDetailUrl(modelType!, id, true)}
onClick={(e) => clickTitle(e)}
>
- {previewComponent.title}
+
+
+
+
+
+
+ {previewComponent.title}
+
) : (
{previewComponent.title}
diff --git a/src/frontend/src/components/previews/models/PartPreview.tsx b/src/frontend/src/components/previews/models/PartPreview.tsx
index 5bfc77d088..d253cac760 100644
--- a/src/frontend/src/components/previews/models/PartPreview.tsx
+++ b/src/frontend/src/components/previews/models/PartPreview.tsx
@@ -8,11 +8,12 @@ import {
import { t } from '@lingui/core/macro';
import { Group, Stack, Text } from '@mantine/core';
import { useQuery } from '@tanstack/react-query';
+import { useMemo } from 'react';
import { useApi } from '../../../contexts/ApiContext';
-import { formatDate } from '../../../defaults/formatters';
+import { formatDate, formatPriceRange } from '../../../defaults/formatters';
import { useInstance } from '../../../hooks/UseInstance';
import { ApiImage } from '../../images/ApiImage';
-import { RenderPartCategory } from '../../render/Part';
+import { RenderRemoteInstance } from '../../render/Instance';
import { RenderUser } from '../../render/User';
import { AttributeGrid, type AttributeRow } from '../AttributeGrid';
import type { PreviewType } from '../PreviewType';
@@ -20,13 +21,11 @@ import type { PreviewType } from '../PreviewType';
function PartPreviewContent({ instance }: Readonly<{ instance: any }>) {
const api = useApi();
- // Fetch part requirements
const { instance: requirements } = useInstance({
endpoint: ApiEndpoints.part_requirements,
pk: instance?.pk,
hasPrimaryKey: true,
disabled: !instance?.pk,
- params: {},
defaultValue: {}
});
@@ -45,49 +44,45 @@ function PartPreviewContent({ instance }: Readonly<{ instance: any }>) {
.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 units = instance?.units ? ` ${instance.units}` : '';
- const category = instance?.category_detail;
+ const stockItems: AttributeRow[] = useMemo(() => {
+ 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 canBuild = requirements?.can_build ?? 0;
+ const building = requirements?.building ?? 0;
- 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((x): x is NonNullable => x !== null)
- : [];
+ return !instance?.virtual
+ ? [
+ {
+ label: t`In Stock`,
+ value: formatDecimal(totalStock) + units
+ },
+ availableStock !== totalStock
+ ? {
+ label: t`Available`,
+ value: formatDecimal(availableStock) + units
+ }
+ : null,
+ minStock > 0
+ ? { label: t`Minimum`, value: formatDecimal(minStock) + units }
+ : null,
+ instance?.purchaseable && ordering > 0
+ ? { label: t`On Order`, value: formatDecimal(ordering) + units }
+ : null,
+ instance?.assembly && canBuild >= 0
+ ? { label: t`Can Build`, value: formatDecimal(canBuild) }
+ : null,
+ building > 0
+ ? { label: t`In Production`, value: formatDecimal(building) }
+ : null
+ ].filter((x): x is NonNullable => x !== null)
+ : [];
+ }, [requirements, instance, units]);
const parameterItems: AttributeRow[] = parameters.map((param: any) => ({
label: param.template_detail?.name,
@@ -96,28 +91,64 @@ function PartPreviewContent({ instance }: Readonly<{ instance: any }>) {
(param.template_detail?.units ? ` ${param.template_detail.units}` : '')
}));
- const infoItems: AttributeRow[] = [
- {
- label: t`Category`,
- value: instance?.category_detail
- ? RenderPartCategory({ instance: instance.category_detail, link: true })
- : null
- },
- {
- label: t`Creation Date`,
- value: instance?.creation_date ? formatDate(instance.creation_date) : null
- },
- {
- label: t`Created By`,
- value: instance?.creation_user_detail
- ? RenderUser({ instance: instance.creation_user_detail })
- : null
- }
- ];
+ const infoItems: AttributeRow[] = useMemo(() => {
+ const priceRange = formatPriceRange(
+ instance?.pricing_min,
+ instance?.pricing_max
+ );
+
+ return [
+ {
+ label: t`Category`,
+ value: instance?.category ? (
+
+ ) : null
+ },
+ {
+ label: t`Revision Of`,
+ value: instance?.revision_of ? (
+
+ ) : null
+ },
+ {
+ label: t`Variant Of`,
+ value: instance?.variant_of ? (
+
+ ) : null
+ },
+ {
+ label: t`Price Range`,
+ value:
+ instance?.pricing_min != null || instance?.pricing_max != null
+ ? priceRange
+ : null
+ },
+ {
+ label: t`Creation Date`,
+ value: instance?.creation_date
+ ? formatDate(instance.creation_date)
+ : null
+ },
+ {
+ label: t`Created By`,
+ value: instance?.creation_user_detail
+ ? RenderUser({ instance: instance.creation_user_detail })
+ : null
+ }
+ ];
+ }, [instance]);
return (
- {/* Headline: image + name / description / IPN / category */}
) {
)}
- {category && (
+ {instance?.revision && (
+
+
+ {t`Revision`}:
+
+
+ {instance.revision}
+
+
+ )}
+ {instance?.category_name && (
{t`Category`}:
- {category.pathstring ?? category.name}
+ {instance?.category_name}
)}