[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 { ModelType } from '../../enums/ModelType';
interface ModelInformationInterface {
export interface ModelInformationInterface {
label: string;
label_multiple: string;
url_overview?: string;
@ -12,11 +12,11 @@ interface ModelInformationInterface {
cui_detail?: string;
}
type ModelDictory = {
export type ModelDict = {
[key in keyof typeof ModelType]: ModelInformationInterface;
};
export const ModelInformationDict: ModelDictory = {
export const ModelInformationDict: ModelDict = {
part: {
label: t`Part`,
label_multiple: t`Parts`,
@ -165,3 +165,12 @@ export const ModelInformationDict: ModelDictory = {
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 { formatPriceRange } from '../../defaults/formatters';
import { ApiEndpoints } from '../../enums/ApiEndpoints';
import { ModelType } from '../../enums/ModelType';
import { UserRoles } from '../../enums/Roles';
import { partFields } from '../../forms/PartForms';
import { useEditApiFormModal } from '../../hooks/UseForm';
@ -122,8 +123,7 @@ export default function PartDetail() {
type: 'link',
name: 'variant_of',
label: t`Variant of`,
path: ApiEndpoints.part_list,
dest: '/part/'
model: ModelType.part
}
]);
}
@ -226,8 +226,7 @@ export default function PartDetail() {
type: 'link',
name: 'category',
label: t`Category`,
path: ApiEndpoints.category_list,
dest: '/part/category/'
model: ModelType.partcategory
}
]);
}
@ -336,7 +335,7 @@ export default function PartDetail() {
const { data } = useSuspenseQuery({
queryKey: ['stocktake', id],
queryFn: async () => {
const url = ApiEndpoints.part_stocktake_list;
const url = apiUrl(ApiEndpoints.part_stocktake_list);
return api
.get(url, { params: { part: id, ordering: 'date' } })
@ -364,7 +363,7 @@ export default function PartDetail() {
const { data } = useSuspenseQuery({
queryKey: ['stocktake', id],
queryFn: async () => {
const url = ApiEndpoints.part_stocktake_list;
const url = apiUrl(ApiEndpoints.part_stocktake_list);
return api
.get(url, { params: { part: id, ordering: 'date' } })
@ -392,8 +391,7 @@ export default function PartDetail() {
type: 'link',
name: 'default_location',
label: t`Default Location`,
path: ApiEndpoints.stock_location_list,
dest: '/stock/location/'
model: ModelType.stocklocation
}
]);
}
@ -404,8 +402,7 @@ export default function PartDetail() {
type: 'link',
name: 'default_supplier',
label: t`Default Supplier`,
path: ApiEndpoints.supplier_part_list,
dest: '/part/'
model: ModelType.supplierpart
}
]);
}

View File

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