mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +00:00
Fix for details links (#7184)
* Fix for details URl - Do not open as a new link - Instead, use internal 'navigate' - Otherwise, triggers a login sequence again - Major improvement in workflow * Fix InvenTreeTable * Refactor * Handle case where no model available * Fix default return type * Use proper mantine table components * Fix for BomTable click-through * Details tweaks * Fix labels * Implement total price detail * Cleanup * Rendering tweaks * Fix for Details.tsx
This commit is contained in:
parent
770dbb9c35
commit
db1a2f9015
@ -12,14 +12,15 @@ import {
|
|||||||
Tooltip
|
Tooltip
|
||||||
} from '@mantine/core';
|
} from '@mantine/core';
|
||||||
import { useSuspenseQuery } from '@tanstack/react-query';
|
import { useSuspenseQuery } from '@tanstack/react-query';
|
||||||
import { Suspense, useMemo } from 'react';
|
import { Suspense, useCallback, useMemo } from 'react';
|
||||||
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
|
||||||
import { api } from '../../App';
|
import { api } from '../../App';
|
||||||
import { ApiEndpoints } from '../../enums/ApiEndpoints';
|
import { ApiEndpoints } from '../../enums/ApiEndpoints';
|
||||||
import { ModelType } from '../../enums/ModelType';
|
import { ModelType } from '../../enums/ModelType';
|
||||||
import { InvenTreeIcon, InvenTreeIconType } from '../../functions/icons';
|
import { InvenTreeIcon, InvenTreeIconType } from '../../functions/icons';
|
||||||
|
import { navigateToLink } from '../../functions/navigation';
|
||||||
import { getDetailUrl } from '../../functions/urls';
|
import { getDetailUrl } from '../../functions/urls';
|
||||||
import { base_url } from '../../main';
|
|
||||||
import { apiUrl } from '../../states/ApiState';
|
import { apiUrl } from '../../states/ApiState';
|
||||||
import { useGlobalSettingsState } from '../../states/SettingsState';
|
import { useGlobalSettingsState } from '../../states/SettingsState';
|
||||||
import { YesNoButton } from '../buttons/YesNoButton';
|
import { YesNoButton } from '../buttons/YesNoButton';
|
||||||
@ -183,7 +184,7 @@ function NameBadge({ pk, type }: { pk: string | number; type: BadgeType }) {
|
|||||||
function TableStringValue(props: Readonly<FieldProps>) {
|
function TableStringValue(props: Readonly<FieldProps>) {
|
||||||
let value = props?.field_value;
|
let value = props?.field_value;
|
||||||
|
|
||||||
if (props.field_data?.value_formatter) {
|
if (props?.field_data?.value_formatter) {
|
||||||
value = props.field_data.value_formatter();
|
value = props.field_data.value_formatter();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -222,24 +223,15 @@ function BooleanValue(props: Readonly<FieldProps>) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function TableAnchorValue(props: Readonly<FieldProps>) {
|
function TableAnchorValue(props: Readonly<FieldProps>) {
|
||||||
if (props.field_data.external) {
|
const navigate = useNavigate();
|
||||||
return (
|
|
||||||
<Anchor
|
|
||||||
href={`${props.field_value}`}
|
|
||||||
target={'_blank'}
|
|
||||||
rel={'noreferrer noopener'}
|
|
||||||
>
|
|
||||||
<span style={{ display: 'flex', alignItems: 'center', gap: '3px' }}>
|
|
||||||
<Text>{props.field_value}</Text>
|
|
||||||
<InvenTreeIcon icon="external" iconProps={{ size: 15 }} />
|
|
||||||
</span>
|
|
||||||
</Anchor>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const { data } = useSuspenseQuery({
|
const { data } = useSuspenseQuery({
|
||||||
queryKey: ['detail', props.field_data.model, props.field_value],
|
queryKey: ['detail', props.field_data.model, props.field_value],
|
||||||
queryFn: async () => {
|
queryFn: async () => {
|
||||||
|
if (!props.field_data?.model) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
const modelDef = getModelInfo(props.field_data.model);
|
const modelDef = getModelInfo(props.field_data.model);
|
||||||
|
|
||||||
if (!modelDef?.api_endpoint) {
|
if (!modelDef?.api_endpoint) {
|
||||||
@ -255,19 +247,44 @@ function TableAnchorValue(props: Readonly<FieldProps>) {
|
|||||||
case 200:
|
case 200:
|
||||||
return response.data;
|
return response.data;
|
||||||
default:
|
default:
|
||||||
return null;
|
return {};
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
return null;
|
return {};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const detailUrl = useMemo(() => {
|
const detailUrl = useMemo(() => {
|
||||||
return getDetailUrl(props.field_data.model, props.field_value);
|
return (
|
||||||
|
props?.field_data?.model &&
|
||||||
|
getDetailUrl(props.field_data.model, props.field_value)
|
||||||
|
);
|
||||||
}, [props.field_data.model, props.field_value]);
|
}, [props.field_data.model, props.field_value]);
|
||||||
|
|
||||||
|
const handleLinkClick = useCallback(
|
||||||
|
(event: any) => {
|
||||||
|
navigateToLink(detailUrl, navigate, event);
|
||||||
|
},
|
||||||
|
[detailUrl]
|
||||||
|
);
|
||||||
|
|
||||||
|
if (props.field_data.external) {
|
||||||
|
return (
|
||||||
|
<Anchor
|
||||||
|
href={`${props.field_value}`}
|
||||||
|
target={'_blank'}
|
||||||
|
rel={'noreferrer noopener'}
|
||||||
|
>
|
||||||
|
<span style={{ display: 'flex', alignItems: 'center', gap: '3px' }}>
|
||||||
|
<Text>{props.field_value}</Text>
|
||||||
|
<InvenTreeIcon icon="external" iconProps={{ size: 15 }} />
|
||||||
|
</span>
|
||||||
|
</Anchor>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
let make_link = props.field_data?.link ?? true;
|
let make_link = props.field_data?.link ?? true;
|
||||||
|
|
||||||
// Construct the "return value" for the fetched data
|
// Construct the "return value" for the fetched data
|
||||||
@ -282,18 +299,14 @@ function TableAnchorValue(props: Readonly<FieldProps>) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (value === undefined) {
|
if (value === undefined) {
|
||||||
value = data?.name ?? props.field_data?.backup_value ?? 'No name defined';
|
value = data?.name ?? props.field_data?.backup_value ?? t`No name defined`;
|
||||||
make_link = false;
|
make_link = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Suspense fallback={<Skeleton width={200} height={20} radius="xl" />}>
|
<Suspense fallback={<Skeleton width={200} height={20} radius="xl" />}>
|
||||||
{make_link ? (
|
{make_link ? (
|
||||||
<Anchor
|
<Anchor href="#" onClick={handleLinkClick}>
|
||||||
href={`/${base_url}${detailUrl}`}
|
|
||||||
target={data?.external ? '_blank' : undefined}
|
|
||||||
rel={data?.external ? 'noreferrer noopener' : undefined}
|
|
||||||
>
|
|
||||||
<Text>{value}</Text>
|
<Text>{value}</Text>
|
||||||
</Anchor>
|
</Anchor>
|
||||||
) : (
|
) : (
|
||||||
@ -370,25 +383,25 @@ export function DetailsTableField({
|
|||||||
const FieldType: any = getFieldType(field.type);
|
const FieldType: any = getFieldType(field.type);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<tr style={{ verticalAlign: 'top' }}>
|
<Table.Tr style={{ verticalAlign: 'top' }}>
|
||||||
<td
|
<Table.Td
|
||||||
style={{
|
style={{
|
||||||
gap: '20px',
|
width: '50',
|
||||||
width: '50'
|
maxWidth: '50'
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<InvenTreeIcon icon={(field.icon ?? field.name) as InvenTreeIconType} />
|
<InvenTreeIcon icon={(field.icon ?? field.name) as InvenTreeIconType} />
|
||||||
</td>
|
</Table.Td>
|
||||||
<td style={{ minWidth: '25%', maxWidth: '65%' }}>
|
<Table.Td style={{ maxWidth: '65%' }}>
|
||||||
<Text>{field.label}</Text>
|
<Text>{field.label}</Text>
|
||||||
</td>
|
</Table.Td>
|
||||||
<td style={{ width: '100%' }}>
|
<Table.Td style={{}}>
|
||||||
<FieldType field_data={field} field_value={item[field.name]} />
|
<FieldType field_data={field} field_value={item[field.name]} />
|
||||||
</td>
|
</Table.Td>
|
||||||
<td style={{ width: '50' }}>
|
<Table.Td style={{ width: '50' }}>
|
||||||
{field.copy && <CopyField value={item[field.name]} />}
|
{field.copy && <CopyField value={item[field.name]} />}
|
||||||
</td>
|
</Table.Td>
|
||||||
</tr>
|
</Table.Tr>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -405,19 +418,14 @@ export function DetailsTable({
|
|||||||
<Paper p="xs" withBorder radius="xs">
|
<Paper p="xs" withBorder radius="xs">
|
||||||
<Stack gap="xs">
|
<Stack gap="xs">
|
||||||
{title && <StylishText size="lg">{title}</StylishText>}
|
{title && <StylishText size="lg">{title}</StylishText>}
|
||||||
<Table
|
<Table striped verticalSpacing={5} horizontalSpacing="sm">
|
||||||
striped
|
<Table.Tbody>
|
||||||
verticalSpacing="sm"
|
|
||||||
horizontalSpacing="md"
|
|
||||||
withColumnBorders
|
|
||||||
>
|
|
||||||
<tbody>
|
|
||||||
{fields
|
{fields
|
||||||
.filter((field: DetailsField) => !field.hidden)
|
.filter((field: DetailsField) => !field.hidden)
|
||||||
.map((field: DetailsField, index: number) => (
|
.map((field: DetailsField, index: number) => (
|
||||||
<DetailsTableField field={field} item={item} key={index} />
|
<DetailsTableField field={field} item={item} key={index} />
|
||||||
))}
|
))}
|
||||||
</tbody>
|
</Table.Tbody>
|
||||||
</Table>
|
</Table>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Paper>
|
</Paper>
|
||||||
|
@ -163,6 +163,7 @@ const icons = {
|
|||||||
link: IconLink,
|
link: IconLink,
|
||||||
responsible: IconUserStar,
|
responsible: IconUserStar,
|
||||||
pricing: IconCurrencyDollar,
|
pricing: IconCurrencyDollar,
|
||||||
|
total_price: IconCurrencyDollar,
|
||||||
currency: IconCurrencyDollar,
|
currency: IconCurrencyDollar,
|
||||||
stocktake: IconClipboardList,
|
stocktake: IconClipboardList,
|
||||||
user: IconUser,
|
user: IconUser,
|
||||||
|
20
src/frontend/src/functions/navigation.tsx
Normal file
20
src/frontend/src/functions/navigation.tsx
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import { base_url } from '../main';
|
||||||
|
import { cancelEvent } from './events';
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Navigate to a provided link.
|
||||||
|
* - If the link is to be opened externally, open it in a new tab.
|
||||||
|
* - Otherwise, navigate using the provided navigate function.
|
||||||
|
*/
|
||||||
|
export const navigateToLink = (link: string, navigate: any, event: any) => {
|
||||||
|
cancelEvent(event);
|
||||||
|
|
||||||
|
if (event?.ctrlKey || event?.shiftKey) {
|
||||||
|
// Open the link in a new tab
|
||||||
|
const url = `/${base_url}${link}`;
|
||||||
|
window.open(url, '_blank');
|
||||||
|
} else {
|
||||||
|
// Navigate internally
|
||||||
|
navigate(link);
|
||||||
|
}
|
||||||
|
};
|
@ -102,7 +102,8 @@ export default function CategoryDetail({}: {}) {
|
|||||||
type: 'text',
|
type: 'text',
|
||||||
name: 'part_count',
|
name: 'part_count',
|
||||||
label: t`Parts`,
|
label: t`Parts`,
|
||||||
icon: 'part'
|
icon: 'part',
|
||||||
|
value_formatter: () => category?.part_count || '0'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'text',
|
type: 'text',
|
||||||
@ -233,7 +234,7 @@ export default function CategoryDetail({}: {}) {
|
|||||||
/>
|
/>
|
||||||
<PageDetail
|
<PageDetail
|
||||||
title={t`Part Category`}
|
title={t`Part Category`}
|
||||||
detail={<Text>{category.name ?? 'Top level'}</Text>}
|
subtitle={category?.name}
|
||||||
breadcrumbs={breadcrumbs}
|
breadcrumbs={breadcrumbs}
|
||||||
breadcrumbAction={() => {
|
breadcrumbAction={() => {
|
||||||
setTreeOpen(true);
|
setTreeOpen(true);
|
||||||
|
@ -28,6 +28,7 @@ import { PageDetail } from '../../components/nav/PageDetail';
|
|||||||
import { PanelGroup, PanelType } from '../../components/nav/PanelGroup';
|
import { PanelGroup, PanelType } from '../../components/nav/PanelGroup';
|
||||||
import { StatusRenderer } from '../../components/render/StatusRenderer';
|
import { StatusRenderer } from '../../components/render/StatusRenderer';
|
||||||
import { NotesEditor } from '../../components/widgets/MarkdownEditor';
|
import { NotesEditor } from '../../components/widgets/MarkdownEditor';
|
||||||
|
import { formatCurrency } from '../../defaults/formatters';
|
||||||
import { ApiEndpoints } from '../../enums/ApiEndpoints';
|
import { ApiEndpoints } from '../../enums/ApiEndpoints';
|
||||||
import { ModelType } from '../../enums/ModelType';
|
import { ModelType } from '../../enums/ModelType';
|
||||||
import { UserRoles } from '../../enums/Roles';
|
import { UserRoles } from '../../enums/Roles';
|
||||||
@ -151,18 +152,23 @@ export default function PurchaseOrderDetail() {
|
|||||||
label: t`Completed Shipments`,
|
label: t`Completed Shipments`,
|
||||||
total: order.shipments,
|
total: order.shipments,
|
||||||
progress: order.completed_shipments
|
progress: order.completed_shipments
|
||||||
// TODO: Fix this progress bar
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'text',
|
type: 'text',
|
||||||
name: 'currency',
|
name: 'currency',
|
||||||
label: t`Order Currency,`
|
label: t`Order Currency`,
|
||||||
|
value_formatter: () =>
|
||||||
|
order?.order_currency ?? order?.supplier_detail?.currency
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'text',
|
type: 'text',
|
||||||
name: 'total_cost',
|
name: 'total_price',
|
||||||
label: t`Total Cost`
|
label: t`Total Cost`,
|
||||||
// TODO: Implement this!
|
value_formatter: () => {
|
||||||
|
return formatCurrency(order?.total_price, {
|
||||||
|
currency: order?.order_currency ?? order?.supplier_detail?.currency
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -23,6 +23,7 @@ import { PageDetail } from '../../components/nav/PageDetail';
|
|||||||
import { PanelGroup, PanelType } from '../../components/nav/PanelGroup';
|
import { PanelGroup, PanelType } from '../../components/nav/PanelGroup';
|
||||||
import { StatusRenderer } from '../../components/render/StatusRenderer';
|
import { StatusRenderer } from '../../components/render/StatusRenderer';
|
||||||
import { NotesEditor } from '../../components/widgets/MarkdownEditor';
|
import { NotesEditor } from '../../components/widgets/MarkdownEditor';
|
||||||
|
import { formatCurrency } from '../../defaults/formatters';
|
||||||
import { ApiEndpoints } from '../../enums/ApiEndpoints';
|
import { ApiEndpoints } from '../../enums/ApiEndpoints';
|
||||||
import { ModelType } from '../../enums/ModelType';
|
import { ModelType } from '../../enums/ModelType';
|
||||||
import { UserRoles } from '../../enums/Roles';
|
import { UserRoles } from '../../enums/Roles';
|
||||||
@ -123,13 +124,19 @@ export default function ReturnOrderDetail() {
|
|||||||
{
|
{
|
||||||
type: 'text',
|
type: 'text',
|
||||||
name: 'currency',
|
name: 'currency',
|
||||||
label: t`Order Currency,`
|
label: t`Order Currency`,
|
||||||
|
value_formatter: () =>
|
||||||
|
order?.order_currency ?? order?.customer_detail?.currency
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'text',
|
type: 'text',
|
||||||
name: 'total_cost',
|
name: 'total_price',
|
||||||
label: t`Total Cost`
|
label: t`Total Cost`,
|
||||||
// TODO: Implement this!
|
value_formatter: () => {
|
||||||
|
return formatCurrency(order?.total_price, {
|
||||||
|
currency: order?.order_currency ?? order?.customer_detail?.currency
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -26,6 +26,7 @@ import { PageDetail } from '../../components/nav/PageDetail';
|
|||||||
import { PanelGroup, PanelType } from '../../components/nav/PanelGroup';
|
import { PanelGroup, PanelType } from '../../components/nav/PanelGroup';
|
||||||
import { StatusRenderer } from '../../components/render/StatusRenderer';
|
import { StatusRenderer } from '../../components/render/StatusRenderer';
|
||||||
import { NotesEditor } from '../../components/widgets/MarkdownEditor';
|
import { NotesEditor } from '../../components/widgets/MarkdownEditor';
|
||||||
|
import { formatCurrency } from '../../defaults/formatters';
|
||||||
import { ApiEndpoints } from '../../enums/ApiEndpoints';
|
import { ApiEndpoints } from '../../enums/ApiEndpoints';
|
||||||
import { ModelType } from '../../enums/ModelType';
|
import { ModelType } from '../../enums/ModelType';
|
||||||
import { UserRoles } from '../../enums/Roles';
|
import { UserRoles } from '../../enums/Roles';
|
||||||
@ -127,13 +128,19 @@ export default function SalesOrderDetail() {
|
|||||||
{
|
{
|
||||||
type: 'text',
|
type: 'text',
|
||||||
name: 'currency',
|
name: 'currency',
|
||||||
label: t`Order Currency,`
|
label: t`Order Currency`,
|
||||||
|
value_formatter: () =>
|
||||||
|
order?.order_currency ?? order?.customer_detail.currency
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'text',
|
type: 'text',
|
||||||
name: 'total_cost',
|
name: 'total_price',
|
||||||
label: t`Total Cost`
|
label: t`Total Cost`,
|
||||||
// TODO: Implement this!
|
value_formatter: () => {
|
||||||
|
return formatCurrency(order?.total_price, {
|
||||||
|
currency: order?.order_currency ?? order?.customer_detail?.currency
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -108,7 +108,8 @@ export default function Stock() {
|
|||||||
type: 'text',
|
type: 'text',
|
||||||
name: 'items',
|
name: 'items',
|
||||||
icon: 'stock',
|
icon: 'stock',
|
||||||
label: t`Stock Items`
|
label: t`Stock Items`,
|
||||||
|
value_formatter: () => location?.items || '0'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'text',
|
type: 'text',
|
||||||
@ -311,7 +312,7 @@ export default function Stock() {
|
|||||||
/>
|
/>
|
||||||
<PageDetail
|
<PageDetail
|
||||||
title={t`Stock Items`}
|
title={t`Stock Items`}
|
||||||
detail={<Text>{location.name ?? 'Top level'}</Text>}
|
subtitle={location?.name}
|
||||||
actions={locationActions}
|
actions={locationActions}
|
||||||
breadcrumbs={breadcrumbs}
|
breadcrumbs={breadcrumbs}
|
||||||
breadcrumbAction={() => {
|
breadcrumbAction={() => {
|
||||||
|
@ -25,7 +25,13 @@ import {
|
|||||||
DataTableCellClickHandler,
|
DataTableCellClickHandler,
|
||||||
DataTableSortStatus
|
DataTableSortStatus
|
||||||
} from 'mantine-datatable';
|
} from 'mantine-datatable';
|
||||||
import { Fragment, useCallback, useEffect, useMemo, useState } from 'react';
|
import React, {
|
||||||
|
Fragment,
|
||||||
|
useCallback,
|
||||||
|
useEffect,
|
||||||
|
useMemo,
|
||||||
|
useState
|
||||||
|
} from 'react';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
|
||||||
import { api } from '../App';
|
import { api } from '../App';
|
||||||
@ -35,7 +41,9 @@ import { ButtonMenu } from '../components/buttons/ButtonMenu';
|
|||||||
import { ApiFormFieldSet } from '../components/forms/fields/ApiFormField';
|
import { ApiFormFieldSet } from '../components/forms/fields/ApiFormField';
|
||||||
import { ModelType } from '../enums/ModelType';
|
import { ModelType } from '../enums/ModelType';
|
||||||
import { resolveItem } from '../functions/conversion';
|
import { resolveItem } from '../functions/conversion';
|
||||||
|
import { cancelEvent } from '../functions/events';
|
||||||
import { extractAvailableFields, mapFields } from '../functions/forms';
|
import { extractAvailableFields, mapFields } from '../functions/forms';
|
||||||
|
import { navigateToLink } from '../functions/navigation';
|
||||||
import { getDetailUrl } from '../functions/urls';
|
import { getDetailUrl } from '../functions/urls';
|
||||||
import { TableState } from '../hooks/UseTable';
|
import { TableState } from '../hooks/UseTable';
|
||||||
import { base_url } from '../main';
|
import { base_url } from '../main';
|
||||||
@ -525,7 +533,17 @@ export function InvenTreeTable<T = any>({
|
|||||||
|
|
||||||
// Callback when a row is clicked
|
// Callback when a row is clicked
|
||||||
const handleRowClick = useCallback(
|
const handleRowClick = useCallback(
|
||||||
({ event, record, index }: { event: any; record: any; index: number }) => {
|
({
|
||||||
|
event,
|
||||||
|
record,
|
||||||
|
index
|
||||||
|
}: {
|
||||||
|
event: React.MouseEvent;
|
||||||
|
record: any;
|
||||||
|
index: number;
|
||||||
|
}) => {
|
||||||
|
cancelEvent(event);
|
||||||
|
|
||||||
if (props.onRowClick) {
|
if (props.onRowClick) {
|
||||||
// If a custom row click handler is provided, use that
|
// If a custom row click handler is provided, use that
|
||||||
props.onRowClick(record, index, event);
|
props.onRowClick(record, index, event);
|
||||||
@ -536,16 +554,7 @@ export function InvenTreeTable<T = any>({
|
|||||||
if (pk) {
|
if (pk) {
|
||||||
// If a model type is provided, navigate to the detail view for that model
|
// If a model type is provided, navigate to the detail view for that model
|
||||||
let url = getDetailUrl(tableProps.modelType, pk);
|
let url = getDetailUrl(tableProps.modelType, pk);
|
||||||
|
navigateToLink(url, navigate, event);
|
||||||
// Should it be opened in a new tab?
|
|
||||||
if (event?.ctrlKey || event?.shiftKey) {
|
|
||||||
// Open in a new tab
|
|
||||||
url = `/${base_url}${url}`;
|
|
||||||
window.open(url, '_blank');
|
|
||||||
} else {
|
|
||||||
// Navigate internally
|
|
||||||
navigate(url);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -419,6 +419,7 @@ export function BomTable({
|
|||||||
tableActions: tableActions,
|
tableActions: tableActions,
|
||||||
tableFilters: tableFilters,
|
tableFilters: tableFilters,
|
||||||
modelType: ModelType.part,
|
modelType: ModelType.part,
|
||||||
|
modelField: 'sub_part',
|
||||||
rowActions: rowActions
|
rowActions: rowActions
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
Loading…
Reference in New Issue
Block a user