[PUI] Tweaks and refactor for "part details" page (#6405)

* Add getModelInfo helper function

- Extract model definition from provided modeltype

* Improvements for details.tsx

- Use defined URL functions, not strings
- Catch potential errors

* Fix PartDetail page

- Use modeltype definitions
- URL fixes
This commit is contained in:
Oliver 2024-02-05 14:25:29 +11:00 committed by GitHub
parent 7483fd203d
commit 77fd6b6bb3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 39 additions and 23 deletions

View File

@ -3,7 +3,7 @@ import { t } from '@lingui/macro';
import { ApiEndpoints } from '../../enums/ApiEndpoints'; import { ApiEndpoints } from '../../enums/ApiEndpoints';
import { ModelType } from '../../enums/ModelType'; import { ModelType } from '../../enums/ModelType';
interface ModelInformationInterface { export interface ModelInformationInterface {
label: string; label: string;
label_multiple: string; label_multiple: string;
url_overview?: string; url_overview?: string;
@ -12,11 +12,11 @@ interface ModelInformationInterface {
cui_detail?: string; cui_detail?: string;
} }
type ModelDictory = { export type ModelDict = {
[key in keyof typeof ModelType]: ModelInformationInterface; [key in keyof typeof ModelType]: ModelInformationInterface;
}; };
export const ModelInformationDict: ModelDictory = { export const ModelInformationDict: ModelDict = {
part: { part: {
label: t`Part`, label: t`Part`,
label_multiple: t`Parts`, label_multiple: t`Parts`,
@ -165,3 +165,12 @@ export const ModelInformationDict: ModelDictory = {
api_endpoint: ApiEndpoints.user_list api_endpoint: ApiEndpoints.user_list
} }
}; };
/*
* Extract model definition given the provided type
* @param type - ModelType to extract information from
* @returns ModelInformationInterface
*/
export function getModelInfo(type: ModelType): ModelInformationInterface {
return ModelInformationDict[type];
}

View File

@ -44,6 +44,7 @@ import { PartCategoryTree } from '../../components/nav/PartCategoryTree';
import { NotesEditor } from '../../components/widgets/MarkdownEditor'; import { NotesEditor } from '../../components/widgets/MarkdownEditor';
import { formatPriceRange } from '../../defaults/formatters'; import { formatPriceRange } from '../../defaults/formatters';
import { ApiEndpoints } from '../../enums/ApiEndpoints'; import { ApiEndpoints } from '../../enums/ApiEndpoints';
import { ModelType } from '../../enums/ModelType';
import { UserRoles } from '../../enums/Roles'; import { UserRoles } from '../../enums/Roles';
import { partFields } from '../../forms/PartForms'; import { partFields } from '../../forms/PartForms';
import { useEditApiFormModal } from '../../hooks/UseForm'; import { useEditApiFormModal } from '../../hooks/UseForm';
@ -122,8 +123,7 @@ export default function PartDetail() {
type: 'link', type: 'link',
name: 'variant_of', name: 'variant_of',
label: t`Variant of`, label: t`Variant of`,
path: ApiEndpoints.part_list, model: ModelType.part
dest: '/part/'
} }
]); ]);
} }
@ -226,8 +226,7 @@ export default function PartDetail() {
type: 'link', type: 'link',
name: 'category', name: 'category',
label: t`Category`, label: t`Category`,
path: ApiEndpoints.category_list, model: ModelType.partcategory
dest: '/part/category/'
} }
]); ]);
} }
@ -336,7 +335,7 @@ export default function PartDetail() {
const { data } = useSuspenseQuery({ const { data } = useSuspenseQuery({
queryKey: ['stocktake', id], queryKey: ['stocktake', id],
queryFn: async () => { queryFn: async () => {
const url = ApiEndpoints.part_stocktake_list; const url = apiUrl(ApiEndpoints.part_stocktake_list);
return api return api
.get(url, { params: { part: id, ordering: 'date' } }) .get(url, { params: { part: id, ordering: 'date' } })
@ -364,7 +363,7 @@ export default function PartDetail() {
const { data } = useSuspenseQuery({ const { data } = useSuspenseQuery({
queryKey: ['stocktake', id], queryKey: ['stocktake', id],
queryFn: async () => { queryFn: async () => {
const url = ApiEndpoints.part_stocktake_list; const url = apiUrl(ApiEndpoints.part_stocktake_list);
return api return api
.get(url, { params: { part: id, ordering: 'date' } }) .get(url, { params: { part: id, ordering: 'date' } })
@ -392,8 +391,7 @@ export default function PartDetail() {
type: 'link', type: 'link',
name: 'default_location', name: 'default_location',
label: t`Default Location`, label: t`Default Location`,
path: ApiEndpoints.stock_location_list, model: ModelType.stocklocation
dest: '/stock/location/'
} }
]); ]);
} }
@ -404,8 +402,7 @@ export default function PartDetail() {
type: 'link', type: 'link',
name: 'default_supplier', name: 'default_supplier',
label: t`Default Supplier`, label: t`Default Supplier`,
path: ApiEndpoints.supplier_part_list, model: ModelType.supplierpart
dest: '/part/'
} }
]); ]);
} }

View File

@ -11,12 +11,15 @@ import {
Tooltip Tooltip
} from '@mantine/core'; } from '@mantine/core';
import { useSuspenseQuery } from '@tanstack/react-query'; import { useSuspenseQuery } from '@tanstack/react-query';
import { Suspense } from 'react'; import { Suspense, useMemo } from 'react';
import { api } from '../App'; import { api } from '../App';
import { ProgressBar } from '../components/items/ProgressBar'; import { ProgressBar } from '../components/items/ProgressBar';
import { getModelInfo } from '../components/render/ModelType';
import { ApiEndpoints } from '../enums/ApiEndpoints'; import { ApiEndpoints } from '../enums/ApiEndpoints';
import { ModelType } from '../enums/ModelType';
import { InvenTreeIcon } from '../functions/icons'; import { InvenTreeIcon } from '../functions/icons';
import { getDetailUrl } from '../functions/urls';
import { apiUrl } from '../states/ApiState'; import { apiUrl } from '../states/ApiState';
import { useGlobalSettingsState } from '../states/SettingsState'; import { useGlobalSettingsState } from '../states/SettingsState';
@ -53,8 +56,7 @@ type LinkDetailField = {
} & (InternalLinkField | ExternalLinkField); } & (InternalLinkField | ExternalLinkField);
type InternalLinkField = { type InternalLinkField = {
path: ApiEndpoints; model: ModelType;
dest: string;
}; };
type ExternalLinkField = { type ExternalLinkField = {
@ -292,9 +294,15 @@ function TableAnchorValue(props: FieldProps) {
} }
const { data } = useSuspenseQuery({ const { data } = useSuspenseQuery({
queryKey: ['detail', props.field_data.path], queryKey: ['detail', props.field_data.model, props.field_value],
queryFn: async () => { queryFn: async () => {
const url = apiUrl(props.field_data.path, props.field_value); const modelDef = getModelInfo(props.field_data.model);
if (!modelDef.api_endpoint) {
return {};
}
const url = apiUrl(modelDef.api_endpoint, props.field_value);
return api return api
.get(url) .get(url)
@ -312,14 +320,16 @@ function TableAnchorValue(props: FieldProps) {
} }
}); });
const detailUrl = useMemo(() => {
return getDetailUrl(props.field_data.model, props.field_value);
}, [props.field_data.model, props.field_value]);
return ( return (
<Suspense fallback={<Skeleton width={200} height={20} radius="xl" />}> <Suspense fallback={<Skeleton width={200} height={20} radius="xl" />}>
<Anchor <Anchor
href={ href={`/platform${detailUrl}`}
'/platform' + data.url ?? props.field_data.dest + props.field_value target={data?.external ? '_blank' : undefined}
} rel={data?.external ? 'noreferrer noopener' : undefined}
target={data.external ? '_blank' : undefined}
rel={data.external ? 'noreferrer noopener' : undefined}
> >
<Text>{data.name ?? 'No name defined'}</Text> <Text>{data.name ?? 'No name defined'}</Text>
</Anchor> </Anchor>