mirror of
https://github.com/inventree/InvenTree.git
synced 2025-05-04 22:38:49 +00:00
[React] SupplierPart table (#5833)
* Fix TableHoverCard component * Improving handling of very wide table cells * Update panels for PartDetail * Refactor <Thumbnail> component * Add SupplierPart table * Refactor forms - Do not need to specify custom form name any more * More fixes for modal forms * Refactor forms field code * Add generic row action components for edit and delete * Add placeholder comments * Add ability to edit supplier part from table * Create supplier part * Add missing import * Revert scroll behaviour for wide cells - Does not play nice on chrome * Add placeholder panel for part manufacturers * Fix inline renderer for manufacturerpart * Cleanup unused imports * Add icons to supplier part fields * Increase size of form titles * Another fix
This commit is contained in:
parent
a11418398f
commit
f71322ecd3
@ -372,6 +372,7 @@ class SupplierPartSerializer(InvenTreeTagModelSerializer):
|
|||||||
|
|
||||||
# Annotated field showing total in-stock quantity
|
# Annotated field showing total in-stock quantity
|
||||||
in_stock = serializers.FloatField(read_only=True)
|
in_stock = serializers.FloatField(read_only=True)
|
||||||
|
available = serializers.FloatField(required=False)
|
||||||
|
|
||||||
part_detail = PartBriefSerializer(source='part', many=False, read_only=True)
|
part_detail = PartBriefSerializer(source='part', many=False, read_only=True)
|
||||||
|
|
||||||
|
@ -16,7 +16,6 @@ import { ApiFormField, ApiFormFieldSet } from './fields/ApiFormField';
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Properties for the ApiForm component
|
* Properties for the ApiForm component
|
||||||
* @param name : The name (identifier) for this form
|
|
||||||
* @param url : The API endpoint to fetch the form data from
|
* @param url : The API endpoint to fetch the form data from
|
||||||
* @param pk : Optional primary-key value when editing an existing object
|
* @param pk : Optional primary-key value when editing an existing object
|
||||||
* @param title : The title to display in the form header
|
* @param title : The title to display in the form header
|
||||||
@ -35,7 +34,6 @@ import { ApiFormField, ApiFormFieldSet } from './fields/ApiFormField';
|
|||||||
* @param onFormError : A callback function to call when the form is submitted with errors.
|
* @param onFormError : A callback function to call when the form is submitted with errors.
|
||||||
*/
|
*/
|
||||||
export interface ApiFormProps {
|
export interface ApiFormProps {
|
||||||
name: string;
|
|
||||||
url: ApiPaths;
|
url: ApiPaths;
|
||||||
pk?: number | string | undefined;
|
pk?: number | string | undefined;
|
||||||
title: string;
|
title: string;
|
||||||
@ -104,7 +102,7 @@ export function ApiForm({
|
|||||||
// Query manager for retrieiving initial data from the server
|
// Query manager for retrieiving initial data from the server
|
||||||
const initialDataQuery = useQuery({
|
const initialDataQuery = useQuery({
|
||||||
enabled: false,
|
enabled: false,
|
||||||
queryKey: ['form-initial-data', props.name, props.url, props.pk],
|
queryKey: ['form-initial-data', modalId, props.method, props.url, props.pk],
|
||||||
queryFn: async () => {
|
queryFn: async () => {
|
||||||
return api
|
return api
|
||||||
.get(url)
|
.get(url)
|
||||||
@ -150,7 +148,13 @@ export function ApiForm({
|
|||||||
// Fetch initial data if the fetchInitialData property is set
|
// Fetch initial data if the fetchInitialData property is set
|
||||||
if (props.fetchInitialData) {
|
if (props.fetchInitialData) {
|
||||||
queryClient.removeQueries({
|
queryClient.removeQueries({
|
||||||
queryKey: ['form-initial-data', props.name, props.url, props.pk]
|
queryKey: [
|
||||||
|
'form-initial-data',
|
||||||
|
modalId,
|
||||||
|
props.method,
|
||||||
|
props.url,
|
||||||
|
props.pk
|
||||||
|
]
|
||||||
});
|
});
|
||||||
initialDataQuery.refetch();
|
initialDataQuery.refetch();
|
||||||
}
|
}
|
||||||
@ -159,7 +163,7 @@ export function ApiForm({
|
|||||||
// Query manager for submitting data
|
// Query manager for submitting data
|
||||||
const submitQuery = useQuery({
|
const submitQuery = useQuery({
|
||||||
enabled: false,
|
enabled: false,
|
||||||
queryKey: ['form-submit', props.name, props.url, props.pk],
|
queryKey: ['form-submit', modalId, props.method, props.url, props.pk],
|
||||||
queryFn: async () => {
|
queryFn: async () => {
|
||||||
let method = props.method?.toLowerCase() ?? 'get';
|
let method = props.method?.toLowerCase() ?? 'get';
|
||||||
|
|
||||||
|
@ -2,35 +2,43 @@ import { t } from '@lingui/macro';
|
|||||||
import { Anchor } from '@mantine/core';
|
import { Anchor } from '@mantine/core';
|
||||||
import { Group } from '@mantine/core';
|
import { Group } from '@mantine/core';
|
||||||
import { Text } from '@mantine/core';
|
import { Text } from '@mantine/core';
|
||||||
import { useMemo } from 'react';
|
import { ReactNode, useMemo } from 'react';
|
||||||
|
|
||||||
import { ApiImage } from './ApiImage';
|
import { ApiImage } from './ApiImage';
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Render an image, loaded via the API
|
||||||
|
*/
|
||||||
export function Thumbnail({
|
export function Thumbnail({
|
||||||
src,
|
src,
|
||||||
alt = t`Thumbnail`,
|
alt = t`Thumbnail`,
|
||||||
size = 20
|
size = 20,
|
||||||
|
text
|
||||||
}: {
|
}: {
|
||||||
src?: string | undefined;
|
src?: string | undefined;
|
||||||
alt?: string;
|
alt?: string;
|
||||||
size?: number;
|
size?: number;
|
||||||
|
text?: ReactNode;
|
||||||
}) {
|
}) {
|
||||||
// TODO: Use HoverCard to display a larger version of the image
|
const backup_image = '/static/img/blank_image.png';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ApiImage
|
<Group align="left" spacing="xs" noWrap={true}>
|
||||||
src={src || '/static/img/blank_image.png'}
|
<ApiImage
|
||||||
alt={alt}
|
src={src || backup_image}
|
||||||
width={size}
|
alt={alt}
|
||||||
fit="contain"
|
width={size}
|
||||||
radius="xs"
|
fit="contain"
|
||||||
withPlaceholder
|
radius="xs"
|
||||||
imageProps={{
|
withPlaceholder
|
||||||
style: {
|
imageProps={{
|
||||||
maxHeight: size
|
style: {
|
||||||
}
|
maxHeight: size
|
||||||
}}
|
}
|
||||||
/>
|
}}
|
||||||
|
/>
|
||||||
|
{text}
|
||||||
|
</Group>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -39,7 +47,7 @@ export function ThumbnailHoverCard({
|
|||||||
text,
|
text,
|
||||||
link = '',
|
link = '',
|
||||||
alt = t`Thumbnail`,
|
alt = t`Thumbnail`,
|
||||||
size = 24
|
size = 20
|
||||||
}: {
|
}: {
|
||||||
src: string;
|
src: string;
|
||||||
text: string;
|
text: string;
|
||||||
@ -56,12 +64,13 @@ export function ThumbnailHoverCard({
|
|||||||
);
|
);
|
||||||
}, [src, text, alt, size]);
|
}, [src, text, alt, size]);
|
||||||
|
|
||||||
if (link)
|
if (link) {
|
||||||
return (
|
return (
|
||||||
<Anchor href={link} style={{ textDecoration: 'none' }}>
|
<Anchor href={link} style={{ textDecoration: 'none' }}>
|
||||||
{card}
|
{card}
|
||||||
</Anchor>
|
</Anchor>
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return <div>{card}</div>;
|
return <div>{card}</div>;
|
||||||
}
|
}
|
||||||
|
@ -70,15 +70,20 @@ export function RenderSupplierPart({ instance }: { instance: any }): ReactNode {
|
|||||||
/**
|
/**
|
||||||
* Inline rendering of a single ManufacturerPart instance
|
* Inline rendering of a single ManufacturerPart instance
|
||||||
*/
|
*/
|
||||||
export function ManufacturerPart({ instance }: { instance: any }): ReactNode {
|
export function RenderManufacturerPart({
|
||||||
let supplier = instance.supplier_detail ?? {};
|
instance
|
||||||
|
}: {
|
||||||
|
instance: any;
|
||||||
|
}): ReactNode {
|
||||||
let part = instance.part_detail ?? {};
|
let part = instance.part_detail ?? {};
|
||||||
|
let manufacturer = instance.manufacturer_detail ?? {};
|
||||||
|
|
||||||
let text = instance.SKU;
|
return (
|
||||||
|
<RenderInlineModel
|
||||||
if (supplier.name) {
|
primary={manufacturer.name}
|
||||||
text = `${supplier.name} | ${text}`;
|
secondary={instance.MPN}
|
||||||
}
|
suffix={part.full_name}
|
||||||
|
image={manufacturer?.thumnbnail ?? manufacturer.image}
|
||||||
return <RenderInlineModel primary={text} secondary={part.full_name} />;
|
/>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,7 @@ import {
|
|||||||
RenderAddress,
|
RenderAddress,
|
||||||
RenderCompany,
|
RenderCompany,
|
||||||
RenderContact,
|
RenderContact,
|
||||||
|
RenderManufacturerPart,
|
||||||
RenderSupplierPart
|
RenderSupplierPart
|
||||||
} from './Company';
|
} from './Company';
|
||||||
import { ModelType } from './ModelType';
|
import { ModelType } from './ModelType';
|
||||||
@ -41,6 +42,7 @@ const RendererLookup: EnumDictionary<
|
|||||||
[ModelType.build]: RenderBuildOrder,
|
[ModelType.build]: RenderBuildOrder,
|
||||||
[ModelType.company]: RenderCompany,
|
[ModelType.company]: RenderCompany,
|
||||||
[ModelType.contact]: RenderContact,
|
[ModelType.contact]: RenderContact,
|
||||||
|
[ModelType.manufacturerpart]: RenderManufacturerPart,
|
||||||
[ModelType.owner]: RenderOwner,
|
[ModelType.owner]: RenderOwner,
|
||||||
[ModelType.part]: RenderPart,
|
[ModelType.part]: RenderPart,
|
||||||
[ModelType.partcategory]: RenderPartCategory,
|
[ModelType.partcategory]: RenderPartCategory,
|
||||||
@ -54,8 +56,7 @@ const RendererLookup: EnumDictionary<
|
|||||||
[ModelType.stockitem]: RenderStockItem,
|
[ModelType.stockitem]: RenderStockItem,
|
||||||
[ModelType.stockhistory]: RenderStockItem,
|
[ModelType.stockhistory]: RenderStockItem,
|
||||||
[ModelType.supplierpart]: RenderSupplierPart,
|
[ModelType.supplierpart]: RenderSupplierPart,
|
||||||
[ModelType.user]: RenderUser,
|
[ModelType.user]: RenderUser
|
||||||
[ModelType.manufacturerpart]: RenderPart
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// import { ApiFormFieldType } from "../forms/fields/ApiFormField";
|
// import { ApiFormFieldType } from "../forms/fields/ApiFormField";
|
||||||
|
@ -51,7 +51,6 @@ function SettingValue({
|
|||||||
}
|
}
|
||||||
|
|
||||||
openModalApiForm({
|
openModalApiForm({
|
||||||
name: 'setting-edit',
|
|
||||||
url: settingsState.endpoint,
|
url: settingsState.endpoint,
|
||||||
pk: setting.key,
|
pk: setting.key,
|
||||||
method: 'PATCH',
|
method: 'PATCH',
|
||||||
|
@ -524,7 +524,11 @@ export function InvenTreeTable({
|
|||||||
onRowClick={tableProps.onRowClick}
|
onRowClick={tableProps.onRowClick}
|
||||||
defaultColumnProps={{
|
defaultColumnProps={{
|
||||||
noWrap: true,
|
noWrap: true,
|
||||||
textAlignment: 'left'
|
textAlignment: 'left',
|
||||||
|
cellsStyle: {
|
||||||
|
// TODO: Need a better way of handling "wide" cells,
|
||||||
|
overflow: 'hidden'
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
@ -2,7 +2,7 @@ import { t } from '@lingui/macro';
|
|||||||
import { ActionIcon, Tooltip } from '@mantine/core';
|
import { ActionIcon, Tooltip } from '@mantine/core';
|
||||||
import { Menu, Text } from '@mantine/core';
|
import { Menu, Text } from '@mantine/core';
|
||||||
import { IconDots } from '@tabler/icons-react';
|
import { IconDots } from '@tabler/icons-react';
|
||||||
import { ReactNode, useState } from 'react';
|
import { ReactNode, useMemo, useState } from 'react';
|
||||||
|
|
||||||
import { notYetImplemented } from '../../functions/notifications';
|
import { notYetImplemented } from '../../functions/notifications';
|
||||||
|
|
||||||
@ -12,9 +12,41 @@ export type RowAction = {
|
|||||||
color?: string;
|
color?: string;
|
||||||
onClick?: () => void;
|
onClick?: () => void;
|
||||||
tooltip?: string;
|
tooltip?: string;
|
||||||
icon?: ReactNode;
|
hidden?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Component for ediitng a row in a table
|
||||||
|
export function RowEditAction({
|
||||||
|
onClick,
|
||||||
|
hidden
|
||||||
|
}: {
|
||||||
|
onClick?: () => void;
|
||||||
|
hidden?: boolean;
|
||||||
|
}): RowAction {
|
||||||
|
return {
|
||||||
|
title: t`Edit`,
|
||||||
|
color: 'green',
|
||||||
|
onClick: onClick,
|
||||||
|
hidden: hidden
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Component for deleting a row in a table
|
||||||
|
export function RowDeleteAction({
|
||||||
|
onClick,
|
||||||
|
hidden
|
||||||
|
}: {
|
||||||
|
onClick?: () => void;
|
||||||
|
hidden?: boolean;
|
||||||
|
}): RowAction {
|
||||||
|
return {
|
||||||
|
title: t`Delete`,
|
||||||
|
color: 'red',
|
||||||
|
onClick: onClick,
|
||||||
|
hidden: hidden
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Component for displaying actions for a row in a table.
|
* Component for displaying actions for a row in a table.
|
||||||
* Displays a simple dropdown menu with a list of actions.
|
* Displays a simple dropdown menu with a list of actions.
|
||||||
@ -39,8 +71,12 @@ export function RowActions({
|
|||||||
|
|
||||||
const [opened, setOpened] = useState(false);
|
const [opened, setOpened] = useState(false);
|
||||||
|
|
||||||
|
const visibleActions = useMemo(() => {
|
||||||
|
return actions.filter((action) => !action.hidden);
|
||||||
|
}, [actions]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
actions.length > 0 && (
|
visibleActions.length > 0 && (
|
||||||
<Menu
|
<Menu
|
||||||
withinPortal={true}
|
withinPortal={true}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
@ -61,7 +97,7 @@ export function RowActions({
|
|||||||
</Menu.Target>
|
</Menu.Target>
|
||||||
<Menu.Dropdown>
|
<Menu.Dropdown>
|
||||||
<Menu.Label>{title || t`Actions`}</Menu.Label>
|
<Menu.Label>{title || t`Actions`}</Menu.Label>
|
||||||
{actions.map((action, idx) => (
|
{visibleActions.map((action, idx) => (
|
||||||
<Menu.Item
|
<Menu.Item
|
||||||
key={idx}
|
key={idx}
|
||||||
onClick={(event) => {
|
onClick={(event) => {
|
||||||
@ -75,7 +111,6 @@ export function RowActions({
|
|||||||
notYetImplemented();
|
notYetImplemented();
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
icon={action.icon}
|
|
||||||
title={action.tooltip || action.title}
|
title={action.tooltip || action.title}
|
||||||
>
|
>
|
||||||
<Text size="xs" color={action.color}>
|
<Text size="xs" color={action.color}>
|
||||||
|
@ -23,7 +23,7 @@ export function TableHoverCard({
|
|||||||
return (
|
return (
|
||||||
<HoverCard>
|
<HoverCard>
|
||||||
<HoverCard.Target>
|
<HoverCard.Target>
|
||||||
<Group spacing="xs" position="apart">
|
<Group spacing="xs" position="apart" noWrap={true}>
|
||||||
{value}
|
{value}
|
||||||
<IconInfoCircle size="16" color="blue" />
|
<IconInfoCircle size="16" color="blue" />
|
||||||
</Group>
|
</Group>
|
||||||
|
@ -11,7 +11,7 @@ import { YesNoButton } from '../../items/YesNoButton';
|
|||||||
import { TableColumn } from '../Column';
|
import { TableColumn } from '../Column';
|
||||||
import { TableFilter } from '../Filter';
|
import { TableFilter } from '../Filter';
|
||||||
import { InvenTreeTable } from '../InvenTreeTable';
|
import { InvenTreeTable } from '../InvenTreeTable';
|
||||||
import { RowAction } from '../RowActions';
|
import { RowAction, RowDeleteAction, RowEditAction } from '../RowActions';
|
||||||
import { TableHoverCard } from '../TableHoverCard';
|
import { TableHoverCard } from '../TableHoverCard';
|
||||||
|
|
||||||
export function BomTable({
|
export function BomTable({
|
||||||
@ -204,14 +204,11 @@ export function BomTable({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
actions.push({
|
// TODO: Action on edit
|
||||||
title: t`Edit`
|
actions.push(RowEditAction({}));
|
||||||
});
|
|
||||||
|
|
||||||
actions.push({
|
// TODO: Action on delete
|
||||||
title: t`Delete`,
|
actions.push(RowDeleteAction({}));
|
||||||
color: 'red'
|
|
||||||
});
|
|
||||||
|
|
||||||
return actions;
|
return actions;
|
||||||
},
|
},
|
||||||
|
@ -11,13 +11,13 @@ import {
|
|||||||
addAttachment,
|
addAttachment,
|
||||||
deleteAttachment,
|
deleteAttachment,
|
||||||
editAttachment
|
editAttachment
|
||||||
} from '../../../functions/forms/AttachmentForms';
|
} from '../../../forms/AttachmentForms';
|
||||||
import { useTableRefresh } from '../../../hooks/TableRefresh';
|
import { useTableRefresh } from '../../../hooks/TableRefresh';
|
||||||
import { ApiPaths, apiUrl } from '../../../states/ApiState';
|
import { ApiPaths, apiUrl } from '../../../states/ApiState';
|
||||||
import { AttachmentLink } from '../../items/AttachmentLink';
|
import { AttachmentLink } from '../../items/AttachmentLink';
|
||||||
import { TableColumn } from '../Column';
|
import { TableColumn } from '../Column';
|
||||||
import { InvenTreeTable } from '../InvenTreeTable';
|
import { InvenTreeTable } from '../InvenTreeTable';
|
||||||
import { RowAction } from '../RowActions';
|
import { RowAction, RowDeleteAction, RowEditAction } from '../RowActions';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Define set of columns to display for the attachment table
|
* Define set of columns to display for the attachment table
|
||||||
@ -113,32 +113,33 @@ export function AttachmentTable({
|
|||||||
let actions: RowAction[] = [];
|
let actions: RowAction[] = [];
|
||||||
|
|
||||||
if (allowEdit) {
|
if (allowEdit) {
|
||||||
actions.push({
|
actions.push(
|
||||||
title: t`Edit`,
|
RowEditAction({
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
editAttachment({
|
editAttachment({
|
||||||
endpoint: endpoint,
|
endpoint: endpoint,
|
||||||
model: model,
|
model: model,
|
||||||
pk: record.pk,
|
pk: record.pk,
|
||||||
attachmentType: record.attachment ? 'file' : 'link',
|
attachmentType: record.attachment ? 'file' : 'link',
|
||||||
callback: refreshTable
|
callback: refreshTable
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (allowDelete) {
|
if (allowDelete) {
|
||||||
actions.push({
|
actions.push(
|
||||||
title: t`Delete`,
|
RowDeleteAction({
|
||||||
color: 'red',
|
onClick: () => {
|
||||||
onClick: () => {
|
deleteAttachment({
|
||||||
deleteAttachment({
|
endpoint: endpoint,
|
||||||
endpoint: endpoint,
|
pk: record.pk,
|
||||||
pk: record.pk,
|
callback: refreshTable
|
||||||
callback: refreshTable
|
});
|
||||||
});
|
}
|
||||||
}
|
})
|
||||||
});
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return actions;
|
return actions;
|
||||||
|
@ -14,6 +14,7 @@ import { Thumbnail } from '../../images/Thumbnail';
|
|||||||
import { YesNoButton } from '../../items/YesNoButton';
|
import { YesNoButton } from '../../items/YesNoButton';
|
||||||
import { TableColumn } from '../Column';
|
import { TableColumn } from '../Column';
|
||||||
import { InvenTreeTable } from '../InvenTreeTable';
|
import { InvenTreeTable } from '../InvenTreeTable';
|
||||||
|
import { RowDeleteAction, RowEditAction } from '../RowActions';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct a table listing parameters for a given part
|
* Construct a table listing parameters for a given part
|
||||||
@ -103,44 +104,43 @@ export function PartParameterTable({ partId }: { partId: any }) {
|
|||||||
|
|
||||||
let actions = [];
|
let actions = [];
|
||||||
|
|
||||||
actions.push({
|
actions.push(
|
||||||
title: t`Edit`,
|
RowEditAction({
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
openEditApiForm({
|
openEditApiForm({
|
||||||
name: 'edit-part-parameter',
|
url: ApiPaths.part_parameter_list,
|
||||||
url: ApiPaths.part_parameter_list,
|
pk: record.pk,
|
||||||
pk: record.pk,
|
title: t`Edit Part Parameter`,
|
||||||
title: t`Edit Part Parameter`,
|
fields: {
|
||||||
fields: {
|
part: {
|
||||||
part: {
|
hidden: true
|
||||||
hidden: true
|
},
|
||||||
|
template: {},
|
||||||
|
data: {}
|
||||||
},
|
},
|
||||||
template: {},
|
successMessage: t`Part parameter updated`,
|
||||||
data: {}
|
onFormSuccess: refreshTable
|
||||||
},
|
});
|
||||||
successMessage: t`Part parameter updated`,
|
}
|
||||||
onFormSuccess: refreshTable
|
})
|
||||||
});
|
);
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
actions.push({
|
actions.push(
|
||||||
title: t`Delete`,
|
RowDeleteAction({
|
||||||
color: 'red',
|
onClick: () => {
|
||||||
onClick: () => {
|
openDeleteApiForm({
|
||||||
openDeleteApiForm({
|
url: ApiPaths.part_parameter_list,
|
||||||
name: 'delete-part-parameter',
|
pk: record.pk,
|
||||||
url: ApiPaths.part_parameter_list,
|
title: t`Delete Part Parameter`,
|
||||||
pk: record.pk,
|
successMessage: t`Part parameter deleted`,
|
||||||
title: t`Delete Part Parameter`,
|
onFormSuccess: refreshTable,
|
||||||
successMessage: t`Part parameter deleted`,
|
preFormContent: (
|
||||||
onFormSuccess: refreshTable,
|
<Text>{t`Are you sure you want to remove this parameter?`}</Text>
|
||||||
preFormContent: (
|
)
|
||||||
<Text>{t`Are you sure you want to remove this parameter?`}</Text>
|
});
|
||||||
)
|
}
|
||||||
});
|
})
|
||||||
}
|
);
|
||||||
});
|
|
||||||
|
|
||||||
return actions;
|
return actions;
|
||||||
},
|
},
|
||||||
@ -153,7 +153,6 @@ export function PartParameterTable({ partId }: { partId: any }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
openCreateApiForm({
|
openCreateApiForm({
|
||||||
name: 'add-part-parameter',
|
|
||||||
url: ApiPaths.part_parameter_list,
|
url: ApiPaths.part_parameter_list,
|
||||||
title: t`Add Part Parameter`,
|
title: t`Add Part Parameter`,
|
||||||
fields: {
|
fields: {
|
||||||
|
@ -23,16 +23,12 @@ function partTableColumns(): TableColumn[] {
|
|||||||
noWrap: true,
|
noWrap: true,
|
||||||
title: t`Part`,
|
title: t`Part`,
|
||||||
render: function (record: any) {
|
render: function (record: any) {
|
||||||
// TODO - Link to the part detail page
|
|
||||||
return (
|
return (
|
||||||
<Group spacing="xs" align="left" noWrap={true}>
|
<Thumbnail
|
||||||
<Thumbnail
|
src={record.thumbnail || record.image}
|
||||||
src={record.thumbnail || record.image}
|
alt={record.name}
|
||||||
alt={record.name}
|
text={record.full_name}
|
||||||
size={24}
|
/>
|
||||||
/>
|
|
||||||
<Text>{record.full_name}</Text>
|
|
||||||
</Group>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -10,6 +10,7 @@ import { ApiPaths, apiUrl } from '../../../states/ApiState';
|
|||||||
import { Thumbnail } from '../../images/Thumbnail';
|
import { Thumbnail } from '../../images/Thumbnail';
|
||||||
import { TableColumn } from '../Column';
|
import { TableColumn } from '../Column';
|
||||||
import { InvenTreeTable } from '../InvenTreeTable';
|
import { InvenTreeTable } from '../InvenTreeTable';
|
||||||
|
import { RowDeleteAction } from '../RowActions';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct a table listing related parts for a given part
|
* Construct a table listing related parts for a given part
|
||||||
@ -33,7 +34,6 @@ export function RelatedPartTable({ partId }: { partId: number }): ReactNode {
|
|||||||
{
|
{
|
||||||
accessor: 'part',
|
accessor: 'part',
|
||||||
title: t`Part`,
|
title: t`Part`,
|
||||||
noWrap: true,
|
|
||||||
render: (record: any) => {
|
render: (record: any) => {
|
||||||
let part = getPart(record);
|
let part = getPart(record);
|
||||||
return (
|
return (
|
||||||
@ -63,7 +63,6 @@ export function RelatedPartTable({ partId }: { partId: number }): ReactNode {
|
|||||||
|
|
||||||
const addRelatedPart = useCallback(() => {
|
const addRelatedPart = useCallback(() => {
|
||||||
openCreateApiForm({
|
openCreateApiForm({
|
||||||
name: 'add-related-part',
|
|
||||||
title: t`Add Related Part`,
|
title: t`Add Related Part`,
|
||||||
url: ApiPaths.related_part_list,
|
url: ApiPaths.related_part_list,
|
||||||
fields: {
|
fields: {
|
||||||
@ -99,12 +98,9 @@ export function RelatedPartTable({ partId }: { partId: number }): ReactNode {
|
|||||||
// TODO: Hide if user does not have permission to edit parts
|
// TODO: Hide if user does not have permission to edit parts
|
||||||
const rowActions = useCallback((record: any) => {
|
const rowActions = useCallback((record: any) => {
|
||||||
return [
|
return [
|
||||||
{
|
RowDeleteAction({
|
||||||
title: t`Delete`,
|
|
||||||
color: 'red',
|
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
openDeleteApiForm({
|
openDeleteApiForm({
|
||||||
name: 'delete-related-part',
|
|
||||||
url: ApiPaths.related_part_list,
|
url: ApiPaths.related_part_list,
|
||||||
pk: record.pk,
|
pk: record.pk,
|
||||||
title: t`Delete Related Part`,
|
title: t`Delete Related Part`,
|
||||||
@ -115,7 +111,7 @@ export function RelatedPartTable({ partId }: { partId: number }): ReactNode {
|
|||||||
onFormSuccess: refreshTable
|
onFormSuccess: refreshTable
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
];
|
];
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import { t } from '@lingui/macro';
|
import { t } from '@lingui/macro';
|
||||||
import { Group, Text } from '@mantine/core';
|
|
||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
|
||||||
@ -45,10 +44,11 @@ export function PurchaseOrderTable({ params }: { params?: any }) {
|
|||||||
let supplier = record.supplier_detail ?? {};
|
let supplier = record.supplier_detail ?? {};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Group spacing="xs" noWrap={true}>
|
<Thumbnail
|
||||||
<Thumbnail src={supplier?.image} alt={supplier.name} />
|
src={supplier?.image}
|
||||||
<Text>{supplier?.name}</Text>
|
alt={supplier.name}
|
||||||
</Group>
|
text={supplier.name}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -0,0 +1,258 @@
|
|||||||
|
import { t } from '@lingui/macro';
|
||||||
|
import { ActionIcon, Stack, Text, Tooltip } from '@mantine/core';
|
||||||
|
import { IconCirclePlus } from '@tabler/icons-react';
|
||||||
|
import { ReactNode, useCallback, useMemo } from 'react';
|
||||||
|
|
||||||
|
import { supplierPartFields } from '../../../forms/CompanyForms';
|
||||||
|
import {
|
||||||
|
openCreateApiForm,
|
||||||
|
openDeleteApiForm,
|
||||||
|
openEditApiForm
|
||||||
|
} from '../../../functions/forms';
|
||||||
|
import { useTableRefresh } from '../../../hooks/TableRefresh';
|
||||||
|
import { ApiPaths, apiUrl } from '../../../states/ApiState';
|
||||||
|
import { useUserState } from '../../../states/UserState';
|
||||||
|
import { Thumbnail } from '../../images/Thumbnail';
|
||||||
|
import { TableColumn } from '../Column';
|
||||||
|
import { InvenTreeTable } from '../InvenTreeTable';
|
||||||
|
import { RowDeleteAction, RowEditAction } from '../RowActions';
|
||||||
|
import { TableHoverCard } from '../TableHoverCard';
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Construct a table listing supplier parts
|
||||||
|
*/
|
||||||
|
|
||||||
|
export function SupplierPartTable({ params }: { params: any }): ReactNode {
|
||||||
|
const { tableKey, refreshTable } = useTableRefresh('supplierparts');
|
||||||
|
|
||||||
|
const user = useUserState();
|
||||||
|
|
||||||
|
// Construct table columns for this table
|
||||||
|
const tableColumns: TableColumn[] = useMemo(() => {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
accessor: 'part',
|
||||||
|
title: t`Part`,
|
||||||
|
switchable: 'part' in params,
|
||||||
|
sortable: true,
|
||||||
|
render: (record: any) => {
|
||||||
|
let part = record?.part_detail ?? {};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Thumbnail src={part?.thumbnail ?? part.image} text={part.name} />
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessor: 'supplier',
|
||||||
|
title: t`Supplier`,
|
||||||
|
sortable: true,
|
||||||
|
render: (record: any) => {
|
||||||
|
let supplier = record?.supplier_detail ?? {};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Thumbnail
|
||||||
|
src={supplier?.thumbnail ?? supplier.image}
|
||||||
|
text={supplier.name}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessor: 'SKU',
|
||||||
|
title: t`Supplier Part`,
|
||||||
|
sortable: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessor: 'description',
|
||||||
|
title: t`Description`,
|
||||||
|
sortable: false,
|
||||||
|
switchable: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessor: 'manufacturer',
|
||||||
|
switchable: true,
|
||||||
|
sortable: true,
|
||||||
|
title: t`Manufacturer`,
|
||||||
|
render: (record: any) => {
|
||||||
|
let manufacturer = record?.manufacturer_detail ?? {};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Thumbnail
|
||||||
|
src={manufacturer?.thumbnail ?? manufacturer.image}
|
||||||
|
text={manufacturer.name}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessor: 'MPN',
|
||||||
|
switchable: true,
|
||||||
|
sortable: true,
|
||||||
|
title: t`MPN`,
|
||||||
|
render: (record: any) => record?.manufacturer_part_detail?.MPN
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessor: 'in_stock',
|
||||||
|
title: t`In Stock`,
|
||||||
|
sortable: true,
|
||||||
|
switchable: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessor: 'packaging',
|
||||||
|
title: t`Packaging`,
|
||||||
|
sortable: true,
|
||||||
|
switchable: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessor: 'pack_quantity',
|
||||||
|
title: t`Pack Quantity`,
|
||||||
|
sortable: true,
|
||||||
|
switchable: true,
|
||||||
|
render: (record: any) => {
|
||||||
|
let part = record?.part_detail ?? {};
|
||||||
|
|
||||||
|
let extra = [];
|
||||||
|
|
||||||
|
if (part.units) {
|
||||||
|
extra.push(
|
||||||
|
<Text>
|
||||||
|
{t`Base units`} : {part.units}
|
||||||
|
</Text>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TableHoverCard
|
||||||
|
value={record.pack_quantity}
|
||||||
|
extra={extra.length > 0 && <Stack spacing="xs">{extra}</Stack>}
|
||||||
|
title={t`Pack Quantity`}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessor: 'link',
|
||||||
|
title: t`Link`,
|
||||||
|
sortable: false,
|
||||||
|
switchable: true
|
||||||
|
// TODO: custom link renderer?
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessor: 'note',
|
||||||
|
title: t`Notes`,
|
||||||
|
sortable: false,
|
||||||
|
switchable: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessor: 'available',
|
||||||
|
title: t`Availability`,
|
||||||
|
sortable: true,
|
||||||
|
switchable: true,
|
||||||
|
render: (record: any) => {
|
||||||
|
let extra = [];
|
||||||
|
|
||||||
|
if (record.availablility_updated) {
|
||||||
|
extra.push(
|
||||||
|
<Text>
|
||||||
|
{t`Updated`} : {record.availablility_updated}
|
||||||
|
</Text>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TableHoverCard
|
||||||
|
value={record.available}
|
||||||
|
extra={extra.length > 0 && <Stack spacing="xs">{extra}</Stack>}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}, [params]);
|
||||||
|
|
||||||
|
const addSupplierPart = useCallback(() => {
|
||||||
|
let fields = supplierPartFields();
|
||||||
|
|
||||||
|
fields.part.value = params?.part;
|
||||||
|
fields.supplier.value = params?.supplier;
|
||||||
|
|
||||||
|
openCreateApiForm({
|
||||||
|
url: ApiPaths.supplier_part_list,
|
||||||
|
title: t`Add Supplier Part`,
|
||||||
|
fields: fields,
|
||||||
|
onFormSuccess: refreshTable,
|
||||||
|
successMessage: t`Supplier part created`
|
||||||
|
});
|
||||||
|
}, [params]);
|
||||||
|
|
||||||
|
// Table actions
|
||||||
|
const tableActions = useMemo(() => {
|
||||||
|
// TODO: Hide actions based on user permissions
|
||||||
|
|
||||||
|
return [
|
||||||
|
// TODO: Refactor this component out to something reusable
|
||||||
|
<Tooltip label={t`Add supplier part`}>
|
||||||
|
<ActionIcon radius="sm" onClick={addSupplierPart}>
|
||||||
|
<IconCirclePlus color="green" />
|
||||||
|
</ActionIcon>
|
||||||
|
</Tooltip>
|
||||||
|
];
|
||||||
|
}, [user]);
|
||||||
|
|
||||||
|
// Row action callback
|
||||||
|
const rowActions = useCallback(
|
||||||
|
(record: any) => {
|
||||||
|
// TODO: Adjust actions based on user permissions
|
||||||
|
return [
|
||||||
|
RowEditAction({
|
||||||
|
onClick: () => {
|
||||||
|
record.pk &&
|
||||||
|
openEditApiForm({
|
||||||
|
url: ApiPaths.supplier_part_list,
|
||||||
|
pk: record.pk,
|
||||||
|
title: t`Edit Supplier Part`,
|
||||||
|
fields: supplierPartFields(),
|
||||||
|
onFormSuccess: refreshTable,
|
||||||
|
successMessage: t`Supplier part updated`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
RowDeleteAction({
|
||||||
|
onClick: () => {
|
||||||
|
record.pk &&
|
||||||
|
openDeleteApiForm({
|
||||||
|
url: ApiPaths.supplier_part_list,
|
||||||
|
pk: record.pk,
|
||||||
|
title: t`Delete Supplier Part`,
|
||||||
|
successMessage: t`Supplier part deleted`,
|
||||||
|
onFormSuccess: refreshTable,
|
||||||
|
preFormContent: (
|
||||||
|
<Text>{t`Are you sure you want to remove this supplier part?`}</Text>
|
||||||
|
)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
];
|
||||||
|
},
|
||||||
|
[user]
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<InvenTreeTable
|
||||||
|
url={apiUrl(ApiPaths.supplier_part_list)}
|
||||||
|
tableKey={tableKey}
|
||||||
|
columns={tableColumns}
|
||||||
|
props={{
|
||||||
|
params: {
|
||||||
|
...params,
|
||||||
|
part_detail: true,
|
||||||
|
supplier_detail: true,
|
||||||
|
manufacturer_detail: true
|
||||||
|
},
|
||||||
|
rowActions: rowActions,
|
||||||
|
customActionGroups: tableActions
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
@ -1,5 +1,4 @@
|
|||||||
import { t } from '@lingui/macro';
|
import { t } from '@lingui/macro';
|
||||||
import { Group, Text } from '@mantine/core';
|
|
||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
|
||||||
@ -41,10 +40,11 @@ export function ReturnOrderTable({ params }: { params?: any }) {
|
|||||||
let customer = record.customer_detail ?? {};
|
let customer = record.customer_detail ?? {};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Group spacing="xs" noWrap={true}>
|
<Thumbnail
|
||||||
<Thumbnail src={customer?.image} alt={customer.name} />
|
src={customer?.image}
|
||||||
<Text>{customer?.name}</Text>
|
alt={customer.name}
|
||||||
</Group>
|
text={customer.name}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import { t } from '@lingui/macro';
|
import { t } from '@lingui/macro';
|
||||||
import { Group, Text } from '@mantine/core';
|
|
||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
|
||||||
@ -42,10 +41,11 @@ export function SalesOrderTable({ params }: { params?: any }) {
|
|||||||
let customer = record.customer_detail ?? {};
|
let customer = record.customer_detail ?? {};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Group spacing="xs" noWrap={true}>
|
<Thumbnail
|
||||||
<Thumbnail src={customer?.image} alt={customer.name} />
|
src={customer?.image}
|
||||||
<Text>{customer?.name}</Text>
|
alt={customer.name}
|
||||||
</Group>
|
text={customer.name}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -12,7 +12,7 @@ import { useTableRefresh } from '../../../hooks/TableRefresh';
|
|||||||
import { ApiPaths, apiUrl } from '../../../states/ApiState';
|
import { ApiPaths, apiUrl } from '../../../states/ApiState';
|
||||||
import { TableColumn } from '../Column';
|
import { TableColumn } from '../Column';
|
||||||
import { InvenTreeTable } from '../InvenTreeTable';
|
import { InvenTreeTable } from '../InvenTreeTable';
|
||||||
import { RowAction } from '../RowActions';
|
import { RowAction, RowDeleteAction, RowEditAction } from '../RowActions';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Table for displaying list of custom physical units
|
* Table for displaying list of custom physical units
|
||||||
@ -45,11 +45,9 @@ export function CustomUnitsTable() {
|
|||||||
|
|
||||||
const rowActions = useCallback((record: any): RowAction[] => {
|
const rowActions = useCallback((record: any): RowAction[] => {
|
||||||
return [
|
return [
|
||||||
{
|
RowEditAction({
|
||||||
title: t`Edit`,
|
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
openEditApiForm({
|
openEditApiForm({
|
||||||
name: 'edit-custom-unit',
|
|
||||||
url: ApiPaths.custom_unit_list,
|
url: ApiPaths.custom_unit_list,
|
||||||
pk: record.pk,
|
pk: record.pk,
|
||||||
title: t`Edit custom unit`,
|
title: t`Edit custom unit`,
|
||||||
@ -62,12 +60,10 @@ export function CustomUnitsTable() {
|
|||||||
successMessage: t`Custom unit updated`
|
successMessage: t`Custom unit updated`
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
}),
|
||||||
{
|
RowDeleteAction({
|
||||||
title: t`Delete`,
|
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
openDeleteApiForm({
|
openDeleteApiForm({
|
||||||
name: 'delete-custom-unit',
|
|
||||||
url: ApiPaths.custom_unit_list,
|
url: ApiPaths.custom_unit_list,
|
||||||
pk: record.pk,
|
pk: record.pk,
|
||||||
title: t`Delete custom unit`,
|
title: t`Delete custom unit`,
|
||||||
@ -78,13 +74,12 @@ export function CustomUnitsTable() {
|
|||||||
)
|
)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
];
|
];
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const addCustomUnit = useCallback(() => {
|
const addCustomUnit = useCallback(() => {
|
||||||
openCreateApiForm({
|
openCreateApiForm({
|
||||||
name: 'add-custom-unit',
|
|
||||||
url: ApiPaths.custom_unit_list,
|
url: ApiPaths.custom_unit_list,
|
||||||
title: t`Add custom unit`,
|
title: t`Add custom unit`,
|
||||||
fields: {
|
fields: {
|
||||||
|
@ -12,7 +12,7 @@ import { useTableRefresh } from '../../../hooks/TableRefresh';
|
|||||||
import { ApiPaths, apiUrl } from '../../../states/ApiState';
|
import { ApiPaths, apiUrl } from '../../../states/ApiState';
|
||||||
import { TableColumn } from '../Column';
|
import { TableColumn } from '../Column';
|
||||||
import { InvenTreeTable } from '../InvenTreeTable';
|
import { InvenTreeTable } from '../InvenTreeTable';
|
||||||
import { RowAction } from '../RowActions';
|
import { RowAction, RowDeleteAction, RowEditAction } from '../RowActions';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Table for displaying list of project codes
|
* Table for displaying list of project codes
|
||||||
@ -37,11 +37,9 @@ export function ProjectCodeTable() {
|
|||||||
|
|
||||||
const rowActions = useCallback((record: any): RowAction[] => {
|
const rowActions = useCallback((record: any): RowAction[] => {
|
||||||
return [
|
return [
|
||||||
{
|
RowEditAction({
|
||||||
title: t`Edit`,
|
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
openEditApiForm({
|
openEditApiForm({
|
||||||
name: 'edit-project-code',
|
|
||||||
url: ApiPaths.project_code_list,
|
url: ApiPaths.project_code_list,
|
||||||
pk: record.pk,
|
pk: record.pk,
|
||||||
title: t`Edit project code`,
|
title: t`Edit project code`,
|
||||||
@ -53,13 +51,10 @@ export function ProjectCodeTable() {
|
|||||||
successMessage: t`Project code updated`
|
successMessage: t`Project code updated`
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
}),
|
||||||
{
|
RowDeleteAction({
|
||||||
title: t`Delete`,
|
|
||||||
color: 'red',
|
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
openDeleteApiForm({
|
openDeleteApiForm({
|
||||||
name: 'delete-project-code',
|
|
||||||
url: ApiPaths.project_code_list,
|
url: ApiPaths.project_code_list,
|
||||||
pk: record.pk,
|
pk: record.pk,
|
||||||
title: t`Delete project code`,
|
title: t`Delete project code`,
|
||||||
@ -70,13 +65,12 @@ export function ProjectCodeTable() {
|
|||||||
)
|
)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
];
|
];
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const addProjectCode = useCallback(() => {
|
const addProjectCode = useCallback(() => {
|
||||||
openCreateApiForm({
|
openCreateApiForm({
|
||||||
name: 'add-project-code',
|
|
||||||
url: ApiPaths.project_code_list,
|
url: ApiPaths.project_code_list,
|
||||||
title: t`Add project code`,
|
title: t`Add project code`,
|
||||||
fields: {
|
fields: {
|
||||||
|
@ -3,7 +3,6 @@ import { Group, Stack, Text } from '@mantine/core';
|
|||||||
import { ReactNode, useMemo } from 'react';
|
import { ReactNode, useMemo } from 'react';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
|
||||||
import { notYetImplemented } from '../../../functions/notifications';
|
|
||||||
import { useTableRefresh } from '../../../hooks/TableRefresh';
|
import { useTableRefresh } from '../../../hooks/TableRefresh';
|
||||||
import { ApiPaths, apiUrl } from '../../../states/ApiState';
|
import { ApiPaths, apiUrl } from '../../../states/ApiState';
|
||||||
import { Thumbnail } from '../../images/Thumbnail';
|
import { Thumbnail } from '../../images/Thumbnail';
|
||||||
@ -219,13 +218,7 @@ export function StockItemTable({ params = {} }: { params?: any }) {
|
|||||||
function stockItemRowActions(record: any): RowAction[] {
|
function stockItemRowActions(record: any): RowAction[] {
|
||||||
let actions: RowAction[] = [];
|
let actions: RowAction[] = [];
|
||||||
|
|
||||||
actions.push({
|
// TODO: Custom row actions for stock table
|
||||||
title: t`Edit`,
|
|
||||||
onClick: () => {
|
|
||||||
notYetImplemented();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return actions;
|
return actions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
import { t } from '@lingui/macro';
|
import { t } from '@lingui/macro';
|
||||||
import { Text } from '@mantine/core';
|
import { Text } from '@mantine/core';
|
||||||
|
|
||||||
import { ApiFormFieldSet } from '../../components/forms/fields/ApiFormField';
|
import { ApiFormFieldSet } from '../components/forms/fields/ApiFormField';
|
||||||
import { ApiPaths } from '../../states/ApiState';
|
|
||||||
import {
|
import {
|
||||||
openCreateApiForm,
|
openCreateApiForm,
|
||||||
openDeleteApiForm,
|
openDeleteApiForm,
|
||||||
openEditApiForm
|
openEditApiForm
|
||||||
} from '../forms';
|
} from '../functions/forms';
|
||||||
|
import { ApiPaths } from '../states/ApiState';
|
||||||
|
|
||||||
export function attachmentFields(editing: boolean): ApiFormFieldSet {
|
export function attachmentFields(editing: boolean): ApiFormFieldSet {
|
||||||
let fields: ApiFormFieldSet = {
|
let fields: ApiFormFieldSet = {
|
||||||
@ -59,7 +59,6 @@ export function addAttachment({
|
|||||||
let message = attachmentType === 'file' ? t`File added` : t`Link added`;
|
let message = attachmentType === 'file' ? t`File added` : t`Link added`;
|
||||||
|
|
||||||
openCreateApiForm({
|
openCreateApiForm({
|
||||||
name: 'attachment-add',
|
|
||||||
title: title,
|
title: title,
|
||||||
url: endpoint,
|
url: endpoint,
|
||||||
successMessage: message,
|
successMessage: message,
|
||||||
@ -102,7 +101,6 @@ export function editAttachment({
|
|||||||
let message = attachmentType === 'file' ? t`File updated` : t`Link updated`;
|
let message = attachmentType === 'file' ? t`File updated` : t`Link updated`;
|
||||||
|
|
||||||
openEditApiForm({
|
openEditApiForm({
|
||||||
name: 'attachment-edit',
|
|
||||||
title: title,
|
title: title,
|
||||||
url: endpoint,
|
url: endpoint,
|
||||||
pk: pk,
|
pk: pk,
|
||||||
@ -124,7 +122,6 @@ export function deleteAttachment({
|
|||||||
openDeleteApiForm({
|
openDeleteApiForm({
|
||||||
url: endpoint,
|
url: endpoint,
|
||||||
pk: pk,
|
pk: pk,
|
||||||
name: 'attachment-edit',
|
|
||||||
title: t`Delete Attachment`,
|
title: t`Delete Attachment`,
|
||||||
successMessage: t`Attachment deleted`,
|
successMessage: t`Attachment deleted`,
|
||||||
onFormSuccess: callback,
|
onFormSuccess: callback,
|
106
src/frontend/src/forms/CompanyForms.tsx
Normal file
106
src/frontend/src/forms/CompanyForms.tsx
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
import { t } from '@lingui/macro';
|
||||||
|
import {
|
||||||
|
IconAt,
|
||||||
|
IconCurrencyDollar,
|
||||||
|
IconGlobe,
|
||||||
|
IconHash,
|
||||||
|
IconLink,
|
||||||
|
IconNote,
|
||||||
|
IconPackage,
|
||||||
|
IconPhone
|
||||||
|
} from '@tabler/icons-react';
|
||||||
|
|
||||||
|
import {
|
||||||
|
ApiFormData,
|
||||||
|
ApiFormFieldSet
|
||||||
|
} from '../components/forms/fields/ApiFormField';
|
||||||
|
import { openEditApiForm } from '../functions/forms';
|
||||||
|
import { ApiPaths } from '../states/ApiState';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Field set for SupplierPart instance
|
||||||
|
*/
|
||||||
|
export function supplierPartFields(): ApiFormFieldSet {
|
||||||
|
return {
|
||||||
|
part: {
|
||||||
|
filters: {
|
||||||
|
purchaseable: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
manufacturer_part: {
|
||||||
|
filters: {
|
||||||
|
part_detail: true,
|
||||||
|
manufacturer_detail: true
|
||||||
|
},
|
||||||
|
adjustFilters: (filters: any, form: ApiFormData) => {
|
||||||
|
let part = form.values.part;
|
||||||
|
|
||||||
|
if (part) {
|
||||||
|
filters.part = part;
|
||||||
|
}
|
||||||
|
|
||||||
|
return filters;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
supplier: {},
|
||||||
|
SKU: {
|
||||||
|
icon: <IconHash />
|
||||||
|
},
|
||||||
|
description: {},
|
||||||
|
link: {
|
||||||
|
icon: <IconLink />
|
||||||
|
},
|
||||||
|
note: {
|
||||||
|
icon: <IconNote />
|
||||||
|
},
|
||||||
|
pack_quantity: {},
|
||||||
|
packaging: {
|
||||||
|
icon: <IconPackage />
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Field set for editing a company instance
|
||||||
|
*/
|
||||||
|
export function companyFields(): ApiFormFieldSet {
|
||||||
|
return {
|
||||||
|
name: {},
|
||||||
|
description: {},
|
||||||
|
website: {
|
||||||
|
icon: <IconGlobe />
|
||||||
|
},
|
||||||
|
currency: {
|
||||||
|
icon: <IconCurrencyDollar />
|
||||||
|
},
|
||||||
|
phone: {
|
||||||
|
icon: <IconPhone />
|
||||||
|
},
|
||||||
|
email: {
|
||||||
|
icon: <IconAt />
|
||||||
|
},
|
||||||
|
is_supplier: {},
|
||||||
|
is_manufacturer: {},
|
||||||
|
is_customer: {}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Edit a company instance
|
||||||
|
*/
|
||||||
|
export function editCompany({
|
||||||
|
pk,
|
||||||
|
callback
|
||||||
|
}: {
|
||||||
|
pk: number;
|
||||||
|
callback?: () => void;
|
||||||
|
}) {
|
||||||
|
openEditApiForm({
|
||||||
|
title: t`Edit Company`,
|
||||||
|
url: ApiPaths.company_list,
|
||||||
|
pk: pk,
|
||||||
|
fields: companyFields(),
|
||||||
|
successMessage: t`Company updated`,
|
||||||
|
onFormSuccess: callback
|
||||||
|
});
|
||||||
|
}
|
@ -1,8 +1,8 @@
|
|||||||
import { t } from '@lingui/macro';
|
import { t } from '@lingui/macro';
|
||||||
|
|
||||||
import { ApiFormFieldSet } from '../../components/forms/fields/ApiFormField';
|
import { ApiFormFieldSet } from '../components/forms/fields/ApiFormField';
|
||||||
import { ApiPaths } from '../../states/ApiState';
|
import { openCreateApiForm, openEditApiForm } from '../functions/forms';
|
||||||
import { openCreateApiForm, openEditApiForm } from '../forms';
|
import { ApiPaths } from '../states/ApiState';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct a set of fields for creating / editing a Part instance
|
* Construct a set of fields for creating / editing a Part instance
|
||||||
@ -70,7 +70,6 @@ export function partFields({
|
|||||||
*/
|
*/
|
||||||
export function createPart() {
|
export function createPart() {
|
||||||
openCreateApiForm({
|
openCreateApiForm({
|
||||||
name: 'part-create',
|
|
||||||
title: t`Create Part`,
|
title: t`Create Part`,
|
||||||
url: ApiPaths.part_list,
|
url: ApiPaths.part_list,
|
||||||
successMessage: t`Part created`,
|
successMessage: t`Part created`,
|
||||||
@ -90,7 +89,6 @@ export function editPart({
|
|||||||
callback?: () => void;
|
callback?: () => void;
|
||||||
}) {
|
}) {
|
||||||
openEditApiForm({
|
openEditApiForm({
|
||||||
name: 'part-edit',
|
|
||||||
title: t`Edit Part`,
|
title: t`Edit Part`,
|
||||||
url: ApiPaths.part_list,
|
url: ApiPaths.part_list,
|
||||||
pk: part_id,
|
pk: part_id,
|
@ -4,9 +4,9 @@ import {
|
|||||||
ApiFormChangeCallback,
|
ApiFormChangeCallback,
|
||||||
ApiFormData,
|
ApiFormData,
|
||||||
ApiFormFieldSet
|
ApiFormFieldSet
|
||||||
} from '../../components/forms/fields/ApiFormField';
|
} from '../components/forms/fields/ApiFormField';
|
||||||
import { ApiPaths } from '../../states/ApiState';
|
import { openCreateApiForm, openEditApiForm } from '../functions/forms';
|
||||||
import { openCreateApiForm, openEditApiForm } from '../forms';
|
import { ApiPaths } from '../states/ApiState';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct a set of fields for creating / editing a StockItem instance
|
* Construct a set of fields for creating / editing a StockItem instance
|
||||||
@ -30,7 +30,6 @@ export function stockFields({
|
|||||||
},
|
},
|
||||||
supplier_part: {
|
supplier_part: {
|
||||||
// TODO: icon
|
// TODO: icon
|
||||||
// TODO: implement adjustFilters
|
|
||||||
filters: {
|
filters: {
|
||||||
part_detail: true,
|
part_detail: true,
|
||||||
supplier_detail: true
|
supplier_detail: true
|
||||||
@ -107,7 +106,6 @@ export function stockFields({
|
|||||||
*/
|
*/
|
||||||
export function createStockItem() {
|
export function createStockItem() {
|
||||||
openCreateApiForm({
|
openCreateApiForm({
|
||||||
name: 'stockitem-create',
|
|
||||||
url: ApiPaths.stock_item_list,
|
url: ApiPaths.stock_item_list,
|
||||||
fields: stockFields({ create: true }),
|
fields: stockFields({ create: true }),
|
||||||
title: t`Create Stock Item`
|
title: t`Create Stock Item`
|
||||||
@ -126,7 +124,6 @@ export function editStockItem({
|
|||||||
callback?: () => void;
|
callback?: () => void;
|
||||||
}) {
|
}) {
|
||||||
openEditApiForm({
|
openEditApiForm({
|
||||||
name: 'stockitem-edit',
|
|
||||||
url: ApiPaths.stock_item_list,
|
url: ApiPaths.stock_item_list,
|
||||||
pk: item_id,
|
pk: item_id,
|
||||||
fields: stockFields({ create: false }),
|
fields: stockFields({ create: false }),
|
@ -115,10 +115,12 @@ export function openModalApiForm(props: ApiFormProps) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Generate a random modal ID for controller
|
// Generate a random modal ID for controller
|
||||||
let modalId: string = `modal-${props.title}-` + generateUniqueId();
|
let modalId: string =
|
||||||
|
`modal-${props.title}-${props.url}-${props.method}` +
|
||||||
|
generateUniqueId();
|
||||||
|
|
||||||
modals.open({
|
modals.open({
|
||||||
title: <StylishText>{props.title}</StylishText>,
|
title: <StylishText size="xl">{props.title}</StylishText>,
|
||||||
modalId: modalId,
|
modalId: modalId,
|
||||||
size: 'xl',
|
size: 'xl',
|
||||||
onClose: () => {
|
onClose: () => {
|
||||||
|
@ -1,57 +0,0 @@
|
|||||||
import { t } from '@lingui/macro';
|
|
||||||
import {
|
|
||||||
IconAt,
|
|
||||||
IconCurrencyDollar,
|
|
||||||
IconGlobe,
|
|
||||||
IconPhone
|
|
||||||
} from '@tabler/icons-react';
|
|
||||||
|
|
||||||
import { ApiFormFieldSet } from '../../components/forms/fields/ApiFormField';
|
|
||||||
import { ApiPaths } from '../../states/ApiState';
|
|
||||||
import { openEditApiForm } from '../forms';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Field set for editing a company instance
|
|
||||||
*/
|
|
||||||
export function companyFields(): ApiFormFieldSet {
|
|
||||||
return {
|
|
||||||
name: {},
|
|
||||||
description: {},
|
|
||||||
website: {
|
|
||||||
icon: <IconGlobe />
|
|
||||||
},
|
|
||||||
currency: {
|
|
||||||
icon: <IconCurrencyDollar />
|
|
||||||
},
|
|
||||||
phone: {
|
|
||||||
icon: <IconPhone />
|
|
||||||
},
|
|
||||||
email: {
|
|
||||||
icon: <IconAt />
|
|
||||||
},
|
|
||||||
is_supplier: {},
|
|
||||||
is_manufacturer: {},
|
|
||||||
is_customer: {}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Edit a company instance
|
|
||||||
*/
|
|
||||||
export function editCompany({
|
|
||||||
pk,
|
|
||||||
callback
|
|
||||||
}: {
|
|
||||||
pk: number;
|
|
||||||
callback?: () => void;
|
|
||||||
}) {
|
|
||||||
openEditApiForm({
|
|
||||||
name: 'company-edit',
|
|
||||||
title: t`Edit Company`,
|
|
||||||
url: ApiPaths.company_list,
|
|
||||||
pk: pk,
|
|
||||||
fields: companyFields(),
|
|
||||||
successMessage: t`Company updated`,
|
|
||||||
onFormSuccess: callback
|
|
||||||
});
|
|
||||||
}
|
|
@ -9,13 +9,13 @@ import { PlaceholderPill } from '../../components/items/Placeholder';
|
|||||||
import { StylishText } from '../../components/items/StylishText';
|
import { StylishText } from '../../components/items/StylishText';
|
||||||
import { ModelType } from '../../components/render/ModelType';
|
import { ModelType } from '../../components/render/ModelType';
|
||||||
import { StatusRenderer } from '../../components/renderers/StatusRenderer';
|
import { StatusRenderer } from '../../components/renderers/StatusRenderer';
|
||||||
import { openCreateApiForm, openEditApiForm } from '../../functions/forms';
|
|
||||||
import {
|
import {
|
||||||
createPart,
|
createPart,
|
||||||
editPart,
|
editPart,
|
||||||
partCategoryFields
|
partCategoryFields
|
||||||
} from '../../functions/forms/PartForms';
|
} from '../../forms/PartForms';
|
||||||
import { createStockItem } from '../../functions/forms/StockForms';
|
import { createStockItem } from '../../forms/StockForms';
|
||||||
|
import { openCreateApiForm, openEditApiForm } from '../../functions/forms';
|
||||||
import { ApiPaths } from '../../states/ApiState';
|
import { ApiPaths } from '../../states/ApiState';
|
||||||
|
|
||||||
// Generate some example forms using the modal API forms interface
|
// Generate some example forms using the modal API forms interface
|
||||||
@ -23,7 +23,6 @@ function ApiFormsPlayground() {
|
|||||||
let fields = partCategoryFields({});
|
let fields = partCategoryFields({});
|
||||||
|
|
||||||
const editCategoryForm: ApiFormProps = {
|
const editCategoryForm: ApiFormProps = {
|
||||||
name: 'partcategory',
|
|
||||||
url: ApiPaths.category_list,
|
url: ApiPaths.category_list,
|
||||||
pk: 2,
|
pk: 2,
|
||||||
title: 'Edit Category',
|
title: 'Edit Category',
|
||||||
@ -31,7 +30,6 @@ function ApiFormsPlayground() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const createAttachmentForm: ApiFormProps = {
|
const createAttachmentForm: ApiFormProps = {
|
||||||
name: 'createattachment',
|
|
||||||
url: ApiPaths.part_attachment_list,
|
url: ApiPaths.part_attachment_list,
|
||||||
title: 'Create Attachment',
|
title: 'Create Attachment',
|
||||||
successMessage: 'Attachment uploaded',
|
successMessage: 'Attachment uploaded',
|
||||||
|
@ -31,7 +31,7 @@ import { ReturnOrderTable } from '../../components/tables/sales/ReturnOrderTable
|
|||||||
import { SalesOrderTable } from '../../components/tables/sales/SalesOrderTable';
|
import { SalesOrderTable } from '../../components/tables/sales/SalesOrderTable';
|
||||||
import { StockItemTable } from '../../components/tables/stock/StockItemTable';
|
import { StockItemTable } from '../../components/tables/stock/StockItemTable';
|
||||||
import { NotesEditor } from '../../components/widgets/MarkdownEditor';
|
import { NotesEditor } from '../../components/widgets/MarkdownEditor';
|
||||||
import { editCompany } from '../../functions/forms/CompanyForms';
|
import { editCompany } from '../../forms/CompanyForms';
|
||||||
import { useInstance } from '../../hooks/UseInstance';
|
import { useInstance } from '../../hooks/UseInstance';
|
||||||
import { ApiPaths, apiUrl } from '../../states/ApiState';
|
import { ApiPaths, apiUrl } from '../../states/ApiState';
|
||||||
import { useUserState } from '../../states/UserState';
|
import { useUserState } from '../../states/UserState';
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
import { t } from '@lingui/macro';
|
import { t } from '@lingui/macro';
|
||||||
import { Group, LoadingOverlay, Stack, Text } from '@mantine/core';
|
import { Group, LoadingOverlay, Stack, Text } from '@mantine/core';
|
||||||
import {
|
import {
|
||||||
|
IconBookmarks,
|
||||||
IconBuilding,
|
IconBuilding,
|
||||||
|
IconBuildingFactory2,
|
||||||
IconCalendarStats,
|
IconCalendarStats,
|
||||||
IconClipboardList,
|
IconClipboardList,
|
||||||
IconCopy,
|
IconCopy,
|
||||||
@ -44,10 +46,11 @@ import { AttachmentTable } from '../../components/tables/general/AttachmentTable
|
|||||||
import { PartParameterTable } from '../../components/tables/part/PartParameterTable';
|
import { PartParameterTable } from '../../components/tables/part/PartParameterTable';
|
||||||
import { PartVariantTable } from '../../components/tables/part/PartVariantTable';
|
import { PartVariantTable } from '../../components/tables/part/PartVariantTable';
|
||||||
import { RelatedPartTable } from '../../components/tables/part/RelatedPartTable';
|
import { RelatedPartTable } from '../../components/tables/part/RelatedPartTable';
|
||||||
|
import { SupplierPartTable } from '../../components/tables/purchasing/SupplierPartTable';
|
||||||
import { SalesOrderTable } from '../../components/tables/sales/SalesOrderTable';
|
import { SalesOrderTable } from '../../components/tables/sales/SalesOrderTable';
|
||||||
import { StockItemTable } from '../../components/tables/stock/StockItemTable';
|
import { StockItemTable } from '../../components/tables/stock/StockItemTable';
|
||||||
import { NotesEditor } from '../../components/widgets/MarkdownEditor';
|
import { NotesEditor } from '../../components/widgets/MarkdownEditor';
|
||||||
import { editPart } from '../../functions/forms/PartForms';
|
import { editPart } from '../../forms/PartForms';
|
||||||
import { useInstance } from '../../hooks/UseInstance';
|
import { useInstance } from '../../hooks/UseInstance';
|
||||||
import { ApiPaths, apiUrl } from '../../states/ApiState';
|
import { ApiPaths, apiUrl } from '../../states/ApiState';
|
||||||
import { useUserState } from '../../states/UserState';
|
import { useUserState } from '../../states/UserState';
|
||||||
@ -108,6 +111,12 @@ export default function PartDetail() {
|
|||||||
hidden: !part.is_template,
|
hidden: !part.is_template,
|
||||||
content: <PartVariantTable partId={String(id)} />
|
content: <PartVariantTable partId={String(id)} />
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'allocations',
|
||||||
|
label: t`Allocations`,
|
||||||
|
icon: <IconBookmarks />,
|
||||||
|
hidden: !part.component && !part.salable
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'bom',
|
name: 'bom',
|
||||||
label: t`Bill of Materials`,
|
label: t`Bill of Materials`,
|
||||||
@ -119,7 +128,7 @@ export default function PartDetail() {
|
|||||||
name: 'builds',
|
name: 'builds',
|
||||||
label: t`Build Orders`,
|
label: t`Build Orders`,
|
||||||
icon: <IconTools />,
|
icon: <IconTools />,
|
||||||
hidden: !part.assembly && !part.component,
|
hidden: !part.assembly,
|
||||||
content: (
|
content: (
|
||||||
<BuildOrderTable
|
<BuildOrderTable
|
||||||
params={{
|
params={{
|
||||||
@ -141,11 +150,24 @@ export default function PartDetail() {
|
|||||||
label: t`Pricing`,
|
label: t`Pricing`,
|
||||||
icon: <IconCurrencyDollar />
|
icon: <IconCurrencyDollar />
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'manufacturers',
|
||||||
|
label: t`Manufacturers`,
|
||||||
|
icon: <IconBuildingFactory2 />,
|
||||||
|
hidden: !part.purchaseable
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'suppliers',
|
name: 'suppliers',
|
||||||
label: t`Suppliers`,
|
label: t`Suppliers`,
|
||||||
icon: <IconBuilding />,
|
icon: <IconBuilding />,
|
||||||
hidden: !part.purchaseable
|
hidden: !part.purchaseable,
|
||||||
|
content: part.pk && (
|
||||||
|
<SupplierPartTable
|
||||||
|
params={{
|
||||||
|
part: part.pk ?? -1
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'purchase_orders',
|
name: 'purchase_orders',
|
||||||
|
@ -35,7 +35,7 @@ import { PanelGroup, PanelType } from '../../components/nav/PanelGroup';
|
|||||||
import { StockLocationTree } from '../../components/nav/StockLocationTree';
|
import { StockLocationTree } from '../../components/nav/StockLocationTree';
|
||||||
import { AttachmentTable } from '../../components/tables/general/AttachmentTable';
|
import { AttachmentTable } from '../../components/tables/general/AttachmentTable';
|
||||||
import { NotesEditor } from '../../components/widgets/MarkdownEditor';
|
import { NotesEditor } from '../../components/widgets/MarkdownEditor';
|
||||||
import { editStockItem } from '../../functions/forms/StockForms';
|
import { editStockItem } from '../../forms/StockForms';
|
||||||
import { useInstance } from '../../hooks/UseInstance';
|
import { useInstance } from '../../hooks/UseInstance';
|
||||||
import { ApiPaths, apiUrl } from '../../states/ApiState';
|
import { ApiPaths, apiUrl } from '../../states/ApiState';
|
||||||
import { useUserState } from '../../states/UserState';
|
import { useUserState } from '../../states/UserState';
|
||||||
|
Loading…
x
Reference in New Issue
Block a user