mirror of
https://github.com/inventree/InvenTree.git
synced 2025-07-02 03:30:54 +00:00
[UI] Query Improvements (#9791)
* Fix for stockOperationModal * Re-check when opened status changes * rename stockOperationModal -> useStockOperationModal * Fix enabled status of query * Add option to specify modalId * Track modal state when open / close * Prevent generators from running until forms are open * Prevent double loading of tables * Fix useQuery * Fix queryKey * Revert API change
This commit is contained in:
@ -174,6 +174,7 @@ export interface ApiFormProps {
|
||||
*/
|
||||
export interface ApiFormModalProps extends ApiFormProps {
|
||||
title: string;
|
||||
modalId?: string;
|
||||
cancelText?: string;
|
||||
cancelColor?: string;
|
||||
onClose?: () => void;
|
||||
|
@ -1,6 +1,7 @@
|
||||
import type { UiSizeType } from './Core';
|
||||
|
||||
export interface UseModalProps {
|
||||
id: string;
|
||||
title: string;
|
||||
children: React.ReactElement;
|
||||
size?: UiSizeType;
|
||||
|
@ -155,7 +155,7 @@ export function TableField({
|
||||
</Table.Thead>
|
||||
|
||||
<Table.Tbody>
|
||||
{value.length > 0 ? (
|
||||
{(value?.length ?? 0) > 0 ? (
|
||||
value.map((item: any, idx: number) => {
|
||||
return (
|
||||
<TableFieldRow
|
||||
|
@ -36,9 +36,11 @@ import { PartColumn } from '../tables/ColumnRenderers';
|
||||
* Field set for BuildOrder forms
|
||||
*/
|
||||
export function useBuildOrderFields({
|
||||
create
|
||||
create,
|
||||
modalId
|
||||
}: {
|
||||
create: boolean;
|
||||
modalId: string;
|
||||
}): ApiFormFieldSet {
|
||||
const [destination, setDestination] = useState<number | null | undefined>(
|
||||
null
|
||||
@ -47,6 +49,7 @@ export function useBuildOrderFields({
|
||||
const [batchCode, setBatchCode] = useState<string>('');
|
||||
|
||||
const batchGenerator = useBatchCodeGenerator({
|
||||
modalId: modalId,
|
||||
onGenerate: (value: any) => {
|
||||
setBatchCode((batch: any) => batch || value);
|
||||
}
|
||||
@ -152,9 +155,11 @@ export function useBuildOrderFields({
|
||||
}
|
||||
|
||||
export function useBuildOrderOutputFields({
|
||||
build
|
||||
build,
|
||||
modalId
|
||||
}: {
|
||||
build: any;
|
||||
modalId: string;
|
||||
}): ApiFormFieldSet {
|
||||
const trackable: boolean = useMemo(() => {
|
||||
return build.part_detail?.trackable ?? false;
|
||||
@ -176,12 +181,14 @@ export function useBuildOrderOutputFields({
|
||||
}, [build]);
|
||||
|
||||
const serialGenerator = useSerialNumberGenerator({
|
||||
modalId: modalId,
|
||||
initialQuery: {
|
||||
part: build.part || build.part_detail?.pk
|
||||
}
|
||||
});
|
||||
|
||||
const batchGenerator = useBatchCodeGenerator({
|
||||
modalId: modalId,
|
||||
initialQuery: {
|
||||
part: build.part || build.part_detail?.pk,
|
||||
quantity: build.quantity
|
||||
|
@ -296,6 +296,7 @@ function LineItemFormRow({
|
||||
|
||||
// Batch code generator
|
||||
const batchCodeGenerator = useBatchCodeGenerator({
|
||||
isEnabled: () => batchOpen,
|
||||
onGenerate: (value: any) => {
|
||||
if (value) {
|
||||
props.changeFn(props.idx, 'batch_code', value);
|
||||
@ -305,6 +306,7 @@ function LineItemFormRow({
|
||||
|
||||
// Serial number generator
|
||||
const serialNumberGenerator = useSerialNumberGenerator({
|
||||
isEnabled: () => batchOpen && trackable,
|
||||
onGenerate: (value: any) => {
|
||||
if (value) {
|
||||
props.changeFn(props.idx, 'serial_numbers', value);
|
||||
|
@ -65,10 +65,12 @@ import { StatusFilterOptions } from '../tables/Filter';
|
||||
export function useStockFields({
|
||||
partId,
|
||||
stockItem,
|
||||
modalId,
|
||||
create = false
|
||||
}: {
|
||||
partId?: number;
|
||||
stockItem?: any;
|
||||
modalId: string;
|
||||
create: boolean;
|
||||
}): ApiFormFieldSet {
|
||||
const globalSettings = useGlobalSettingsState();
|
||||
@ -81,12 +83,14 @@ export function useStockFields({
|
||||
const [expiryDate, setExpiryDate] = useState<string | null>(null);
|
||||
|
||||
const batchGenerator = useBatchCodeGenerator({
|
||||
modalId: modalId,
|
||||
initialQuery: {
|
||||
part: partInstance?.pk || partId
|
||||
}
|
||||
});
|
||||
|
||||
const serialGenerator = useSerialNumberGenerator({
|
||||
modalId: modalId,
|
||||
initialQuery: {
|
||||
part: partInstance?.pk || partId
|
||||
}
|
||||
@ -235,11 +239,15 @@ export function useStockFields({
|
||||
* Launch a form to create a new StockItem instance
|
||||
*/
|
||||
export function useCreateStockItem() {
|
||||
const fields = useStockFields({ create: true });
|
||||
const fields = useStockFields({
|
||||
create: true,
|
||||
modalId: 'create-stock-item'
|
||||
});
|
||||
|
||||
return useCreateApiFormModal({
|
||||
url: ApiEndpoints.stock_item_list,
|
||||
fields: fields,
|
||||
modalId: 'create-stock-item',
|
||||
title: t`Add Stock Item`
|
||||
});
|
||||
}
|
||||
@ -318,12 +326,15 @@ export function useStockItemInstallFields({
|
||||
*/
|
||||
export function useStockItemSerializeFields({
|
||||
partId,
|
||||
trackable
|
||||
trackable,
|
||||
modalId
|
||||
}: {
|
||||
partId: number;
|
||||
trackable: boolean;
|
||||
modalId: string;
|
||||
}) {
|
||||
const serialGenerator = useSerialNumberGenerator({
|
||||
modalId: modalId,
|
||||
initialQuery: {
|
||||
part: partId
|
||||
}
|
||||
@ -1018,7 +1029,7 @@ type apiModalFunc = (props: ApiFormModalProps) => {
|
||||
modal: JSX.Element;
|
||||
};
|
||||
|
||||
function stockOperationModal({
|
||||
function useStockOperationModal({
|
||||
items,
|
||||
pk,
|
||||
model,
|
||||
@ -1061,25 +1072,29 @@ function stockOperationModal({
|
||||
return query_params;
|
||||
}, [baseParams, filters, model, pk]);
|
||||
|
||||
const [opened, setOpened] = useState<boolean>(false);
|
||||
|
||||
const { data } = useQuery({
|
||||
queryKey: ['stockitems', model, pk, items, params],
|
||||
queryKey: ['stockitems', opened, model, pk, items, params],
|
||||
queryFn: async () => {
|
||||
if (items) {
|
||||
// If a list of items is provided, use that directly
|
||||
return Array.isArray(items) ? items : [items];
|
||||
}
|
||||
|
||||
if (!pk || !opened) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const url = apiUrl(ApiEndpoints.stock_item_list);
|
||||
|
||||
return api
|
||||
.get(url, {
|
||||
params: params
|
||||
})
|
||||
.then((response) => {
|
||||
if (response.status === 200) {
|
||||
return response.data;
|
||||
}
|
||||
})
|
||||
.then((response) => response.data ?? [])
|
||||
.catch(() => {
|
||||
return null;
|
||||
return [];
|
||||
});
|
||||
}
|
||||
});
|
||||
@ -1095,7 +1110,9 @@ function stockOperationModal({
|
||||
title: title,
|
||||
size: '80%',
|
||||
successMessage: successMessage,
|
||||
onFormSuccess: () => refresh()
|
||||
onFormSuccess: () => refresh(),
|
||||
onClose: () => setOpened(false),
|
||||
onOpen: () => setOpened(true)
|
||||
});
|
||||
}
|
||||
|
||||
@ -1108,7 +1125,7 @@ export type StockOperationProps = {
|
||||
};
|
||||
|
||||
export function useAddStockItem(props: StockOperationProps) {
|
||||
return stockOperationModal({
|
||||
return useStockOperationModal({
|
||||
...props,
|
||||
fieldGenerator: stockAddFields,
|
||||
endpoint: ApiEndpoints.stock_add,
|
||||
@ -1118,7 +1135,7 @@ export function useAddStockItem(props: StockOperationProps) {
|
||||
}
|
||||
|
||||
export function useRemoveStockItem(props: StockOperationProps) {
|
||||
return stockOperationModal({
|
||||
return useStockOperationModal({
|
||||
...props,
|
||||
fieldGenerator: stockRemoveFields,
|
||||
endpoint: ApiEndpoints.stock_remove,
|
||||
@ -1128,7 +1145,7 @@ export function useRemoveStockItem(props: StockOperationProps) {
|
||||
}
|
||||
|
||||
export function useTransferStockItem(props: StockOperationProps) {
|
||||
return stockOperationModal({
|
||||
return useStockOperationModal({
|
||||
...props,
|
||||
fieldGenerator: stockTransferFields,
|
||||
endpoint: ApiEndpoints.stock_transfer,
|
||||
@ -1138,7 +1155,7 @@ export function useTransferStockItem(props: StockOperationProps) {
|
||||
}
|
||||
|
||||
export function useCountStockItem(props: StockOperationProps) {
|
||||
return stockOperationModal({
|
||||
return useStockOperationModal({
|
||||
...props,
|
||||
fieldGenerator: stockCountFields,
|
||||
endpoint: ApiEndpoints.stock_count,
|
||||
@ -1148,7 +1165,7 @@ export function useCountStockItem(props: StockOperationProps) {
|
||||
}
|
||||
|
||||
export function useChangeStockStatus(props: StockOperationProps) {
|
||||
return stockOperationModal({
|
||||
return useStockOperationModal({
|
||||
...props,
|
||||
fieldGenerator: stockChangeStatusFields,
|
||||
endpoint: ApiEndpoints.stock_change_status,
|
||||
@ -1158,7 +1175,7 @@ export function useChangeStockStatus(props: StockOperationProps) {
|
||||
}
|
||||
|
||||
export function useMergeStockItem(props: StockOperationProps) {
|
||||
return stockOperationModal({
|
||||
return useStockOperationModal({
|
||||
...props,
|
||||
fieldGenerator: stockMergeFields,
|
||||
endpoint: ApiEndpoints.stock_merge,
|
||||
@ -1182,7 +1199,7 @@ export function useAssignStockItem(props: StockOperationProps) {
|
||||
return props.items?.filter((item) => item?.part_detail?.salable);
|
||||
}, [props.items]);
|
||||
|
||||
return stockOperationModal({
|
||||
return useStockOperationModal({
|
||||
...props,
|
||||
items: items,
|
||||
fieldGenerator: stockAssignFields,
|
||||
@ -1193,7 +1210,7 @@ export function useAssignStockItem(props: StockOperationProps) {
|
||||
}
|
||||
|
||||
export function useDeleteStockItem(props: StockOperationProps) {
|
||||
return stockOperationModal({
|
||||
return useStockOperationModal({
|
||||
...props,
|
||||
fieldGenerator: stockDeleteFields,
|
||||
endpoint: ApiEndpoints.stock_item_list,
|
||||
|
@ -8,6 +8,7 @@ import type {
|
||||
BulkEditApiFormModalProps
|
||||
} from '@lib/types/Forms';
|
||||
import { OptionsApiForm } from '../components/forms/ApiForm';
|
||||
import { useModalState } from '../states/ModalState';
|
||||
import { useModal } from './UseModal';
|
||||
|
||||
/**
|
||||
@ -17,6 +18,12 @@ export function useApiFormModal(props: ApiFormModalProps) {
|
||||
const id = useId();
|
||||
const modalClose = useRef(() => {});
|
||||
|
||||
const modalState = useModalState();
|
||||
|
||||
const modalId = useMemo(() => {
|
||||
return props.modalId ?? id;
|
||||
}, [props.modalId, id]);
|
||||
|
||||
const formProps = useMemo<ApiFormModalProps>(
|
||||
() => ({
|
||||
...props,
|
||||
@ -44,15 +51,22 @@ export function useApiFormModal(props: ApiFormModalProps) {
|
||||
);
|
||||
|
||||
const modal = useModal({
|
||||
id: modalId,
|
||||
title: formProps.title,
|
||||
onOpen: formProps.onOpen,
|
||||
onClose: formProps.onClose,
|
||||
onOpen: () => {
|
||||
modalState.setModalOpen(modalId, true);
|
||||
formProps.onOpen?.();
|
||||
},
|
||||
onClose: () => {
|
||||
modalState.setModalOpen(modalId, false);
|
||||
formProps.onClose?.();
|
||||
},
|
||||
closeOnClickOutside: formProps.closeOnClickOutside,
|
||||
size: props.size ?? 'xl',
|
||||
children: (
|
||||
<Stack gap={'xs'}>
|
||||
<Divider />
|
||||
<OptionsApiForm props={formProps} id={id} />
|
||||
<OptionsApiForm props={formProps} id={modalId} />
|
||||
</Stack>
|
||||
)
|
||||
});
|
||||
|
@ -5,12 +5,15 @@ import { useCallback, useState } from 'react';
|
||||
import { ApiEndpoints } from '@lib/enums/ApiEndpoints';
|
||||
import { apiUrl } from '@lib/functions/Api';
|
||||
import { api } from '../App';
|
||||
import { useModalState } from '../states/ModalState';
|
||||
|
||||
export type GeneratorProps = {
|
||||
endpoint: ApiEndpoints;
|
||||
key: string;
|
||||
initialQuery?: Record<string, any>;
|
||||
onGenerate?: (value: any) => void;
|
||||
isEnabled?: () => boolean;
|
||||
modalId?: string;
|
||||
};
|
||||
|
||||
export type GeneratorState = {
|
||||
@ -25,6 +28,8 @@ export type GeneratorState = {
|
||||
* Each update calls a new query to the API, and the result is stored in the state.
|
||||
*/
|
||||
export function useGenerator(props: GeneratorProps): GeneratorState {
|
||||
const modalState = useModalState();
|
||||
|
||||
// Track the result
|
||||
const [result, setResult] = useState<any>(null);
|
||||
|
||||
@ -34,6 +39,24 @@ export function useGenerator(props: GeneratorProps): GeneratorState {
|
||||
// Prevent rapid updates
|
||||
const [debouncedQuery] = useDebouncedValue<Record<string, any>>(query, 100);
|
||||
|
||||
// Callback to determine if the function is enabled
|
||||
const isEnabled = useCallback(() => {
|
||||
if (props.isEnabled?.() == false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (props.modalId && !modalState.isModalOpen(props.modalId)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}, [
|
||||
modalState.isModalOpen,
|
||||
modalState.openModals,
|
||||
props.isEnabled,
|
||||
props.modalId
|
||||
]);
|
||||
|
||||
// Callback to update the generator query
|
||||
const update = useCallback(
|
||||
(params: Record<string, any>, overwrite?: boolean) => {
|
||||
@ -57,6 +80,7 @@ export function useGenerator(props: GeneratorProps): GeneratorState {
|
||||
props.key,
|
||||
props.endpoint,
|
||||
props.initialQuery,
|
||||
modalState.openModals,
|
||||
debouncedQuery
|
||||
],
|
||||
refetchOnMount: false,
|
||||
@ -67,6 +91,11 @@ export function useGenerator(props: GeneratorProps): GeneratorState {
|
||||
...(props.initialQuery ?? {})
|
||||
};
|
||||
|
||||
if (!isEnabled()) {
|
||||
setResult(null);
|
||||
return;
|
||||
}
|
||||
|
||||
return api
|
||||
.post(apiUrl(props.endpoint), generatorQuery)
|
||||
.then((response) => {
|
||||
@ -96,31 +125,43 @@ export function useGenerator(props: GeneratorProps): GeneratorState {
|
||||
// Generate a batch code with provided data
|
||||
export function useBatchCodeGenerator({
|
||||
initialQuery,
|
||||
onGenerate
|
||||
onGenerate,
|
||||
isEnabled,
|
||||
modalId
|
||||
}: {
|
||||
initialQuery?: Record<string, any>;
|
||||
onGenerate?: (value: any) => void;
|
||||
isEnabled?: () => boolean;
|
||||
modalId?: string;
|
||||
}): GeneratorState {
|
||||
return useGenerator({
|
||||
endpoint: ApiEndpoints.generate_batch_code,
|
||||
key: 'batch_code',
|
||||
initialQuery: initialQuery,
|
||||
onGenerate: onGenerate
|
||||
onGenerate: onGenerate,
|
||||
isEnabled: isEnabled,
|
||||
modalId: modalId
|
||||
});
|
||||
}
|
||||
|
||||
// Generate a serial number with provided data
|
||||
export function useSerialNumberGenerator({
|
||||
initialQuery,
|
||||
onGenerate
|
||||
onGenerate,
|
||||
isEnabled,
|
||||
modalId
|
||||
}: {
|
||||
initialQuery?: Record<string, any>;
|
||||
onGenerate?: (value: any) => void;
|
||||
isEnabled?: () => boolean;
|
||||
modalId?: string;
|
||||
}): GeneratorState {
|
||||
return useGenerator({
|
||||
endpoint: ApiEndpoints.generate_serial_number,
|
||||
key: 'serial_number',
|
||||
initialQuery: initialQuery,
|
||||
onGenerate: onGenerate
|
||||
onGenerate: onGenerate,
|
||||
isEnabled: isEnabled,
|
||||
modalId: modalId
|
||||
});
|
||||
}
|
||||
|
@ -25,6 +25,7 @@ export function useModal(props: UseModalProps): UseModalReturn {
|
||||
toggle,
|
||||
modal: (
|
||||
<Modal
|
||||
key={props.id}
|
||||
opened={opened}
|
||||
onClose={close}
|
||||
closeOnClickOutside={props.closeOnClickOutside}
|
||||
|
@ -416,13 +416,17 @@ export default function BuildDetail() {
|
||||
];
|
||||
}, [build, id, user, buildStatus, globalSettings]);
|
||||
|
||||
const buildOrderFields = useBuildOrderFields({ create: false });
|
||||
const editBuildOrderFields = useBuildOrderFields({
|
||||
create: false,
|
||||
modalId: 'edit-build-order'
|
||||
});
|
||||
|
||||
const editBuild = useEditApiFormModal({
|
||||
url: ApiEndpoints.build_order_list,
|
||||
pk: build.pk,
|
||||
title: t`Edit Build Order`,
|
||||
fields: buildOrderFields,
|
||||
modalId: 'edit-build-order',
|
||||
fields: editBuildOrderFields,
|
||||
onFormSuccess: refreshInstance
|
||||
});
|
||||
|
||||
@ -435,10 +439,16 @@ export default function BuildDetail() {
|
||||
return data;
|
||||
}, [build]);
|
||||
|
||||
const duplicateBuildOrderFields = useBuildOrderFields({
|
||||
create: false,
|
||||
modalId: 'duplicate-build-order'
|
||||
});
|
||||
|
||||
const duplicateBuild = useCreateApiFormModal({
|
||||
url: ApiEndpoints.build_order_list,
|
||||
title: t`Add Build Order`,
|
||||
fields: buildOrderFields,
|
||||
modalId: 'duplicate-build-order',
|
||||
fields: duplicateBuildOrderFields,
|
||||
initialData: duplicateBuildOrderInitialData,
|
||||
follow: true,
|
||||
modelType: ModelType.build
|
||||
|
@ -1005,11 +1005,13 @@ export default function PartDetail() {
|
||||
|
||||
return (
|
||||
<>
|
||||
{duplicatePart.modal}
|
||||
{editPart.modal}
|
||||
{deletePart.modal}
|
||||
{findBySerialNumber.modal}
|
||||
{duplicatePart.modal}
|
||||
{countStockItems.modal}
|
||||
{orderPartsWizard.wizard}
|
||||
{findBySerialNumber.modal}
|
||||
{transferStockItems.modal}
|
||||
<InstanceDetail
|
||||
status={requestStatus}
|
||||
loading={instanceQuery.isFetching}
|
||||
@ -1096,8 +1098,6 @@ export default function PartDetail() {
|
||||
model={ModelType.part}
|
||||
id={part.pk}
|
||||
/>
|
||||
{transferStockItems.modal}
|
||||
{countStockItems.modal}
|
||||
</Stack>
|
||||
</InstanceDetail>
|
||||
</>
|
||||
|
@ -640,22 +640,28 @@ export default function StockDetail() {
|
||||
const editStockItemFields = useStockFields({
|
||||
create: false,
|
||||
stockItem: stockitem,
|
||||
partId: stockitem.part
|
||||
partId: stockitem.part,
|
||||
modalId: 'edit-stock-item'
|
||||
});
|
||||
|
||||
const editStockItem = useEditApiFormModal({
|
||||
url: ApiEndpoints.stock_item_list,
|
||||
pk: stockitem.pk,
|
||||
title: t`Edit Stock Item`,
|
||||
modalId: 'edit-stock-item',
|
||||
fields: editStockItemFields,
|
||||
onFormSuccess: refreshInstance
|
||||
});
|
||||
|
||||
const duplicateStockItemFields = useStockFields({ create: true });
|
||||
const duplicateStockItemFields = useStockFields({
|
||||
create: true,
|
||||
modalId: 'duplicate-stock-item'
|
||||
});
|
||||
|
||||
const duplicateStockItem = useCreateApiFormModal({
|
||||
url: ApiEndpoints.stock_item_list,
|
||||
title: t`Add Stock Item`,
|
||||
modalId: 'duplicate-stock-item',
|
||||
fields: duplicateStockItemFields,
|
||||
initialData: {
|
||||
...stockitem
|
||||
@ -700,13 +706,15 @@ export default function StockDetail() {
|
||||
|
||||
const serializeStockFields = useStockItemSerializeFields({
|
||||
partId: stockitem.part,
|
||||
trackable: stockitem.part_detail?.trackable
|
||||
trackable: stockitem.part_detail?.trackable,
|
||||
modalId: 'stock-item-serialize'
|
||||
});
|
||||
|
||||
const serializeStockItem = useCreateApiFormModal({
|
||||
url: ApiEndpoints.stock_serialize,
|
||||
pk: stockitem.pk,
|
||||
title: t`Serialize Stock Item`,
|
||||
modalId: 'stock-item-serialize',
|
||||
fields: serializeStockFields,
|
||||
initialData: {
|
||||
quantity: stockitem.quantity,
|
||||
|
28
src/frontend/src/states/ModalState.tsx
Normal file
28
src/frontend/src/states/ModalState.tsx
Normal file
@ -0,0 +1,28 @@
|
||||
import { create } from 'zustand';
|
||||
|
||||
interface ModalStateProps {
|
||||
openModals: Record<string, boolean>;
|
||||
isModalOpen: (modalKey: string) => boolean;
|
||||
setModalOpen: (modalKey: string, isOpen: boolean) => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Global state manager for determining modal visibility.
|
||||
* Useful to share modal state (open / closed) between components.
|
||||
*/
|
||||
export const useModalState = create<ModalStateProps>()((set, get) => ({
|
||||
openModals: {},
|
||||
|
||||
isModalOpen: (modalKey: string) => {
|
||||
return get().openModals[modalKey] ?? false;
|
||||
},
|
||||
|
||||
setModalOpen: (modalKey: string, isOpen: boolean) => {
|
||||
set((state) => ({
|
||||
openModals: {
|
||||
...state.openModals,
|
||||
[modalKey]: isOpen
|
||||
}
|
||||
}));
|
||||
}
|
||||
}));
|
@ -176,10 +176,24 @@ export function InvenTreeTable<T extends Record<string, any>>({
|
||||
);
|
||||
}, [props.tableFilters, fieldNames]);
|
||||
|
||||
// Build table properties based on provided props (and default props)
|
||||
const tableProps: InvenTreeTableProps<T> = useMemo(() => {
|
||||
return {
|
||||
...defaultInvenTreeTableProps,
|
||||
...props
|
||||
};
|
||||
}, [props]);
|
||||
|
||||
// Request OPTIONS data from the API, before we load the table
|
||||
const tableOptionQuery = useQuery({
|
||||
enabled: !!url && !tableData,
|
||||
queryKey: ['options', url, cacheKey, props.enableColumnCaching],
|
||||
queryKey: [
|
||||
'options',
|
||||
url,
|
||||
cacheKey,
|
||||
tableProps.params,
|
||||
props.enableColumnCaching
|
||||
],
|
||||
retry: 3,
|
||||
refetchOnMount: true,
|
||||
gcTime: 5000,
|
||||
@ -255,14 +269,6 @@ export function InvenTreeTable<T extends Record<string, any>>({
|
||||
tableOptionQuery.refetch();
|
||||
}, [cacheKey, url, props.params, props.enableColumnCaching]);
|
||||
|
||||
// Build table properties based on provided props (and default props)
|
||||
const tableProps: InvenTreeTableProps<T> = useMemo(() => {
|
||||
return {
|
||||
...defaultInvenTreeTableProps,
|
||||
...props
|
||||
};
|
||||
}, [props]);
|
||||
|
||||
const enableSelection: boolean = useMemo(() => {
|
||||
return tableProps.enableSelection || tableProps.enableBulkDelete || false;
|
||||
}, [tableProps]);
|
||||
@ -450,13 +456,17 @@ export function InvenTreeTable<T extends Record<string, any>>({
|
||||
]
|
||||
);
|
||||
|
||||
const [sortingLoaded, setSortingLoaded] = useState<boolean>(false);
|
||||
|
||||
useEffect(() => {
|
||||
const tableKey: string = tableState.tableKey.split('-')[0];
|
||||
const sorting: DataTableSortStatus = getTableSorting(tableKey);
|
||||
|
||||
if (sorting) {
|
||||
if (sorting && !!sorting.columnAccessor && !!sorting.direction) {
|
||||
setSortStatus(sorting);
|
||||
}
|
||||
|
||||
setSortingLoaded(true);
|
||||
}, []);
|
||||
|
||||
// Return the ordering parameter
|
||||
@ -492,6 +502,12 @@ export function InvenTreeTable<T extends Record<string, any>>({
|
||||
const queryParams = getTableFilters(true);
|
||||
|
||||
if (!url) {
|
||||
// No URL supplied - do not load!
|
||||
return [];
|
||||
}
|
||||
|
||||
if (!sortingLoaded) {
|
||||
// Sorting not yet loaded - do not load!
|
||||
return [];
|
||||
}
|
||||
|
||||
@ -560,6 +576,7 @@ export function InvenTreeTable<T extends Record<string, any>>({
|
||||
url,
|
||||
tableState.page,
|
||||
props.params,
|
||||
sortingLoaded,
|
||||
sortStatus.columnAccessor,
|
||||
sortStatus.direction,
|
||||
tableState.tableKey,
|
||||
|
@ -419,7 +419,10 @@ export default function BuildLineTable({
|
||||
];
|
||||
}, [hasOutput, isActive, table, output]);
|
||||
|
||||
const buildOrderFields = useBuildOrderFields({ create: true });
|
||||
const buildOrderFields = useBuildOrderFields({
|
||||
create: true,
|
||||
modalId: 'new-build-order'
|
||||
});
|
||||
|
||||
const [initialData, setInitialData] = useState<any>({});
|
||||
|
||||
@ -431,6 +434,7 @@ export default function BuildLineTable({
|
||||
url: ApiEndpoints.build_order_list,
|
||||
title: t`Create Build Order`,
|
||||
fields: buildOrderFields,
|
||||
modalId: 'new-build-order',
|
||||
initialData: initialData,
|
||||
follow: true,
|
||||
modelType: ModelType.build
|
||||
|
@ -194,11 +194,15 @@ export function BuildOrderTable({
|
||||
|
||||
const user = useUserState();
|
||||
|
||||
const buildOrderFields = useBuildOrderFields({ create: true });
|
||||
const buildOrderFields = useBuildOrderFields({
|
||||
create: true,
|
||||
modalId: 'create-build-order'
|
||||
});
|
||||
|
||||
const newBuild = useCreateApiFormModal({
|
||||
url: ApiEndpoints.build_order_list,
|
||||
title: t`Add Build Order`,
|
||||
modalId: 'create-build-order',
|
||||
fields: buildOrderFields,
|
||||
initialData: {
|
||||
part: partId,
|
||||
|
@ -256,11 +256,15 @@ export default function BuildOutputTable({
|
||||
[partId, buildId, testTemplates, trackedItems]
|
||||
);
|
||||
|
||||
const buildOutputFields = useBuildOrderOutputFields({ build: build });
|
||||
const buildOutputFields = useBuildOrderOutputFields({
|
||||
build: build,
|
||||
modalId: 'add-build-output'
|
||||
});
|
||||
|
||||
const addBuildOutput = useCreateApiFormModal({
|
||||
url: apiUrl(ApiEndpoints.build_output_create, buildId),
|
||||
title: t`Add Build Output`,
|
||||
modalId: 'add-build-output',
|
||||
fields: buildOutputFields,
|
||||
timeout: 10000,
|
||||
initialData: {
|
||||
@ -302,13 +306,15 @@ export default function BuildOutputTable({
|
||||
const editStockItemFields = useStockFields({
|
||||
create: false,
|
||||
partId: partId,
|
||||
stockItem: selectedOutputs[0]
|
||||
stockItem: selectedOutputs[0],
|
||||
modalId: 'edit-build-output'
|
||||
});
|
||||
|
||||
const editBuildOutput = useEditApiFormModal({
|
||||
url: ApiEndpoints.stock_item_list,
|
||||
pk: selectedOutputs[0]?.pk,
|
||||
title: t`Edit Build Output`,
|
||||
modalId: 'edit-build-output',
|
||||
fields: editStockItemFields,
|
||||
table: table
|
||||
});
|
||||
|
@ -276,11 +276,15 @@ export default function SalesOrderLineItemTable({
|
||||
table: table
|
||||
});
|
||||
|
||||
const buildOrderFields = useBuildOrderFields({ create: true });
|
||||
const buildOrderFields = useBuildOrderFields({
|
||||
create: true,
|
||||
modalId: 'build-order-create-from-sales-order'
|
||||
});
|
||||
|
||||
const newBuildOrder = useCreateApiFormModal({
|
||||
url: ApiEndpoints.build_order_list,
|
||||
title: t`Create Build Order`,
|
||||
modalId: 'build-order-create-from-sales-order',
|
||||
fields: buildOrderFields,
|
||||
initialData: initialData,
|
||||
follow: true,
|
||||
|
@ -526,12 +526,14 @@ export function StockItemTable({
|
||||
|
||||
const newStockItemFields = useStockFields({
|
||||
create: true,
|
||||
partId: params.part
|
||||
partId: params.part,
|
||||
modalId: 'add-stock-item'
|
||||
});
|
||||
|
||||
const newStockItem = useCreateApiFormModal({
|
||||
url: ApiEndpoints.stock_item_list,
|
||||
title: t`Add Stock Item`,
|
||||
modalId: 'add-stock-item',
|
||||
fields: newStockItemFields,
|
||||
initialData: {
|
||||
part: params.part,
|
||||
|
Reference in New Issue
Block a user