mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +00:00
Forms initial data (#6365)
* Use PATCH for edit form * Add "localhost:8000" server option * Add initialData property for forms - Allows user to specify an initial dataset to override default values * Override field values when constructing form props * Remove debug messages * Wrap ApiForm in FormProvider - Allows lower elements to access all form data without rebuild - Pass all form data through to adjustFilters routine * Fixes for RelatedModelField - Ensure that the saved data are cleared when filters change * Fix debug message for token creation * Fix address rendering for modals * Refactor "address" forms - Use new "hook" form structure * Update Contacts table * Prevent related model fields from fetching on initial form open - Only fetch when the drop-down is actually opened - Significantly reduces the number of API calls * Fix for ChoiceField - Display label / description / placeholder text * Fix for DateInput - Correct conversion of datatype * Implement "new purchase order" form - Uses modal form hook - Supply initial data - Adjust filters according to selected company * Add new company from company table * Edit company from detail page * More table updates - StockLocation - PartCategory * Update more tables / forms: - PartParameter table - PartParameterTemplate table - Cleanup unused imports * Update ProjectCode table * CustomUnits table * Update RelatedPart table * Update PartTestTemplate table * Cleanup PartParameterTable * Add "IPN" column to PartParameterTable * Update BuildOrder table * Update BuildDetail page * PurchaseOrderLineItem table * Simplify - Move fields which are only used in one location * Create new salesorder with context - Also consolidate translated strings - Also improve consistency of inline rendering (with missing image) * Revert change to RenderInlineModel * Fix for build table - Use apiUrl wrapper around ApiEndpoint * Fix parameter for PurchaseOrderTable * Adjust server selector - Only show localhost:8000 if in dev mode * Tweak URL * Add extra test to playground - Check initial value works for nested field * Cleanup playground * Cleanup unused vars * memoize fields * Fix typo host -> host * Fix part editing * Cleanup unused * update group table
This commit is contained in:
parent
2557383892
commit
7fe8207463
@ -253,6 +253,12 @@ class GetAuthToken(APIView):
|
||||
# User is authenticated, and requesting a token against the provided name.
|
||||
token = ApiToken.objects.create(user=request.user, name=name)
|
||||
|
||||
logger.info(
|
||||
"Created new API token for user '%s' (name='%s')",
|
||||
user.username,
|
||||
name,
|
||||
)
|
||||
|
||||
# Add some metadata about the request
|
||||
token.set_metadata('user_agent', request.META.get('HTTP_USER_AGENT', ''))
|
||||
token.set_metadata('remote_addr', request.META.get('REMOTE_ADDR', ''))
|
||||
@ -263,10 +269,6 @@ class GetAuthToken(APIView):
|
||||
|
||||
data = {'token': token.key, 'name': token.name, 'expiry': token.expiry}
|
||||
|
||||
logger.info(
|
||||
"Created new API token for user '%s' (name='%s')", user.username, name
|
||||
)
|
||||
|
||||
# Ensure that the users session is logged in (PUI -> CUI login)
|
||||
if not get_user(request).is_authenticated:
|
||||
login(request, user)
|
||||
|
@ -14,6 +14,7 @@ import { useCallback, useEffect, useMemo } from 'react';
|
||||
import { useState } from 'react';
|
||||
import {
|
||||
FieldValues,
|
||||
FormProvider,
|
||||
SubmitErrorHandler,
|
||||
SubmitHandler,
|
||||
useForm
|
||||
@ -65,6 +66,7 @@ export interface ApiFormProps {
|
||||
pathParams?: PathParams;
|
||||
method?: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
|
||||
fields?: ApiFormFieldSet;
|
||||
initialData?: FieldValues;
|
||||
submitText?: string;
|
||||
submitColor?: string;
|
||||
fetchInitialData?: boolean;
|
||||
@ -146,6 +148,13 @@ export function OptionsApiForm({
|
||||
field: v,
|
||||
definition: data?.[k]
|
||||
});
|
||||
|
||||
// If the user has specified initial data, use that value here
|
||||
let value = _props?.initialData?.[k];
|
||||
|
||||
if (value) {
|
||||
_props.fields[k].value = value;
|
||||
}
|
||||
}
|
||||
|
||||
return _props;
|
||||
@ -163,13 +172,23 @@ export function OptionsApiForm({
|
||||
* based on an API endpoint.
|
||||
*/
|
||||
export function ApiForm({ id, props }: { id: string; props: ApiFormProps }) {
|
||||
const defaultValues: FieldValues = useMemo(
|
||||
() =>
|
||||
mapFields(props.fields ?? {}, (_path, field) => {
|
||||
return field.default ?? undefined;
|
||||
}),
|
||||
[props.fields]
|
||||
);
|
||||
const defaultValues: FieldValues = useMemo(() => {
|
||||
let defaultValuesMap = mapFields(props.fields ?? {}, (_path, field) => {
|
||||
return field.value ?? field.default ?? undefined;
|
||||
});
|
||||
|
||||
// If the user has specified initial data, use that instead
|
||||
if (props.initialData) {
|
||||
defaultValuesMap = {
|
||||
...defaultValuesMap,
|
||||
...props.initialData
|
||||
};
|
||||
}
|
||||
|
||||
// Update the form values, but only for the fields specified for this form
|
||||
|
||||
return defaultValuesMap;
|
||||
}, [props.fields, props.initialData]);
|
||||
|
||||
// Form errors which are not associated with a specific field
|
||||
const [nonFieldErrors, setNonFieldErrors] = useState<string[]>([]);
|
||||
@ -179,6 +198,7 @@ export function ApiForm({ id, props }: { id: string; props: ApiFormProps }) {
|
||||
criteriaMode: 'all',
|
||||
defaultValues
|
||||
});
|
||||
|
||||
const {
|
||||
isValid,
|
||||
isDirty,
|
||||
@ -390,6 +410,7 @@ export function ApiForm({ id, props }: { id: string; props: ApiFormProps }) {
|
||||
{props.preFormWarning}
|
||||
</Alert>
|
||||
)}
|
||||
<FormProvider {...form}>
|
||||
<Stack spacing="xs">
|
||||
{Object.entries(props.fields ?? {}).map(([fieldName, field]) => (
|
||||
<ApiFormField
|
||||
@ -400,6 +421,7 @@ export function ApiForm({ id, props }: { id: string; props: ApiFormProps }) {
|
||||
/>
|
||||
))}
|
||||
</Stack>
|
||||
</FormProvider>
|
||||
{props.postFormContent}
|
||||
</Stack>
|
||||
<Divider />
|
||||
|
@ -7,7 +7,6 @@ import {
|
||||
Switch,
|
||||
TextInput
|
||||
} from '@mantine/core';
|
||||
import { DateInput } from '@mantine/dates';
|
||||
import { UseFormReturnType } from '@mantine/form';
|
||||
import { useId } from '@mantine/hooks';
|
||||
import { IconX } from '@tabler/icons-react';
|
||||
@ -17,11 +16,17 @@ import { Control, FieldValues, useController } from 'react-hook-form';
|
||||
|
||||
import { ModelType } from '../../../enums/ModelType';
|
||||
import { ChoiceField } from './ChoiceField';
|
||||
import DateField from './DateField';
|
||||
import { NestedObjectField } from './NestedObjectField';
|
||||
import { RelatedModelField } from './RelatedModelField';
|
||||
|
||||
export type ApiFormData = UseFormReturnType<Record<string, unknown>>;
|
||||
|
||||
export type ApiFormAdjustFilterType = {
|
||||
filters: any;
|
||||
data: FieldValues;
|
||||
};
|
||||
|
||||
/** Definition of the ApiForm field component.
|
||||
* - The 'name' attribute *must* be provided
|
||||
* - All other attributes are optional, and may be provided by the API
|
||||
@ -80,7 +85,7 @@ export type ApiFormFieldType = {
|
||||
preFieldContent?: JSX.Element;
|
||||
postFieldContent?: JSX.Element;
|
||||
onValueChange?: (value: any) => void;
|
||||
adjustFilters?: (filters: any) => any;
|
||||
adjustFilters?: (value: ApiFormAdjustFilterType) => any;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -207,20 +212,7 @@ export function ApiFormField({
|
||||
/>
|
||||
);
|
||||
case 'date':
|
||||
return (
|
||||
<DateInput
|
||||
{...reducedDefinition}
|
||||
ref={ref}
|
||||
id={fieldId}
|
||||
radius="sm"
|
||||
type={undefined}
|
||||
error={error?.message}
|
||||
value={value}
|
||||
clearable={!definition.required}
|
||||
onChange={(value) => onChange(value)}
|
||||
valueFormat="YYYY-MM-DD"
|
||||
/>
|
||||
);
|
||||
return <DateField controller={controller} definition={definition} />;
|
||||
case 'integer':
|
||||
case 'decimal':
|
||||
case 'float':
|
||||
|
@ -58,6 +58,10 @@ export function ChoiceField({
|
||||
onChange={onChange}
|
||||
data={choices}
|
||||
value={field.value}
|
||||
label={definition.label}
|
||||
description={definition.description}
|
||||
placeholder={definition.placeholder}
|
||||
icon={definition.icon}
|
||||
withinPortal={true}
|
||||
/>
|
||||
);
|
||||
|
60
src/frontend/src/components/forms/fields/DateField.tsx
Normal file
60
src/frontend/src/components/forms/fields/DateField.tsx
Normal file
@ -0,0 +1,60 @@
|
||||
import { DateInput } from '@mantine/dates';
|
||||
import { useCallback, useId, useMemo } from 'react';
|
||||
import { FieldValues, UseControllerReturn } from 'react-hook-form';
|
||||
|
||||
import { ApiFormFieldType } from './ApiFormField';
|
||||
|
||||
export default function DateField({
|
||||
controller,
|
||||
definition
|
||||
}: {
|
||||
controller: UseControllerReturn<FieldValues, any>;
|
||||
definition: ApiFormFieldType;
|
||||
}) {
|
||||
const fieldId = useId();
|
||||
|
||||
const {
|
||||
field,
|
||||
fieldState: { error }
|
||||
} = controller;
|
||||
|
||||
const onChange = useCallback(
|
||||
(value: any) => {
|
||||
// Convert the returned date object to a string
|
||||
if (value) {
|
||||
value = value.toString();
|
||||
let date = new Date(value);
|
||||
value = date.toISOString().split('T')[0];
|
||||
}
|
||||
|
||||
field.onChange(value);
|
||||
definition.onValueChange?.(value);
|
||||
},
|
||||
[field.onChange, definition]
|
||||
);
|
||||
|
||||
const dateValue = useMemo(() => {
|
||||
if (field.value) {
|
||||
return new Date(field.value);
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
}, [field.value]);
|
||||
|
||||
return (
|
||||
<DateInput
|
||||
id={fieldId}
|
||||
radius="sm"
|
||||
type={undefined}
|
||||
error={error?.message}
|
||||
value={dateValue}
|
||||
clearable={!definition.required}
|
||||
onChange={onChange}
|
||||
valueFormat="YYYY-MM-DD"
|
||||
label={definition.label}
|
||||
description={definition.description}
|
||||
placeholder={definition.placeholder}
|
||||
icon={definition.icon}
|
||||
/>
|
||||
);
|
||||
}
|
@ -4,7 +4,11 @@ import { useDebouncedValue } from '@mantine/hooks';
|
||||
import { useId } from '@mantine/hooks';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { FieldValues, UseControllerReturn } from 'react-hook-form';
|
||||
import {
|
||||
FieldValues,
|
||||
UseControllerReturn,
|
||||
useFormContext
|
||||
} from 'react-hook-form';
|
||||
import Select from 'react-select';
|
||||
|
||||
import { api } from '../../../App';
|
||||
@ -32,6 +36,8 @@ export function RelatedModelField({
|
||||
fieldState: { error }
|
||||
} = controller;
|
||||
|
||||
const form = useFormContext();
|
||||
|
||||
// Keep track of the primary key value for this field
|
||||
const [pk, setPk] = useState<number | null>(null);
|
||||
|
||||
@ -40,6 +46,8 @@ export function RelatedModelField({
|
||||
const [data, setData] = useState<any[]>([]);
|
||||
const dataRef = useRef<any[]>([]);
|
||||
|
||||
const [isOpen, setIsOpen] = useState<boolean>(false);
|
||||
|
||||
// If an initial value is provided, load from the API
|
||||
useEffect(() => {
|
||||
// If the value is unchanged, do nothing
|
||||
@ -71,28 +79,49 @@ export function RelatedModelField({
|
||||
const [value, setValue] = useState<string>('');
|
||||
const [searchText, cancelSearchText] = useDebouncedValue(value, 250);
|
||||
|
||||
const [filters, setFilters] = useState<any>({});
|
||||
|
||||
const resetSearch = useCallback(() => {
|
||||
setOffset(0);
|
||||
setData([]);
|
||||
dataRef.current = [];
|
||||
}, []);
|
||||
|
||||
// reset current data on search value change
|
||||
useEffect(() => {
|
||||
dataRef.current = [];
|
||||
setData([]);
|
||||
}, [searchText]);
|
||||
resetSearch();
|
||||
}, [searchText, filters]);
|
||||
|
||||
const selectQuery = useQuery({
|
||||
enabled: !definition.disabled && !!definition.api_url && !definition.hidden,
|
||||
enabled:
|
||||
isOpen &&
|
||||
!definition.disabled &&
|
||||
!!definition.api_url &&
|
||||
!definition.hidden,
|
||||
queryKey: [`related-field-${fieldName}`, fieldId, offset, searchText],
|
||||
queryFn: async () => {
|
||||
if (!definition.api_url) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let filters = definition.filters ?? {};
|
||||
let _filters = definition.filters ?? {};
|
||||
|
||||
if (definition.adjustFilters) {
|
||||
filters = definition.adjustFilters(filters);
|
||||
_filters =
|
||||
definition.adjustFilters({
|
||||
filters: _filters,
|
||||
data: form.getValues()
|
||||
}) ?? _filters;
|
||||
}
|
||||
|
||||
// If the filters have changed, clear the data
|
||||
if (_filters != filters) {
|
||||
resetSearch();
|
||||
setFilters(_filters);
|
||||
}
|
||||
|
||||
let params = {
|
||||
...filters,
|
||||
..._filters,
|
||||
search: searchText,
|
||||
offset: offset,
|
||||
limit: limit
|
||||
@ -189,16 +218,19 @@ export function RelatedModelField({
|
||||
filterOption={null}
|
||||
onInputChange={(value: any) => {
|
||||
setValue(value);
|
||||
setOffset(0);
|
||||
setData([]);
|
||||
resetSearch();
|
||||
}}
|
||||
onChange={onChange}
|
||||
onMenuScrollToBottom={() => setOffset(offset + limit)}
|
||||
onMenuOpen={() => {
|
||||
setIsOpen(true);
|
||||
setValue('');
|
||||
setOffset(0);
|
||||
resetSearch();
|
||||
selectQuery.refetch();
|
||||
}}
|
||||
onMenuClose={() => {
|
||||
setIsOpen(false);
|
||||
}}
|
||||
isLoading={
|
||||
selectQuery.isFetching ||
|
||||
selectQuery.isLoading ||
|
||||
|
@ -7,7 +7,6 @@ import { RenderInlineModel } from './Instance';
|
||||
*/
|
||||
export function RenderAddress({ instance }: { instance: any }): ReactNode {
|
||||
let text = [
|
||||
instance.title,
|
||||
instance.country,
|
||||
instance.postal_code,
|
||||
instance.postal_city,
|
||||
@ -18,12 +17,7 @@ export function RenderAddress({ instance }: { instance: any }): ReactNode {
|
||||
.filter(Boolean)
|
||||
.join(', ');
|
||||
|
||||
return (
|
||||
<RenderInlineModel
|
||||
primary={instance.description}
|
||||
secondary={instance.address}
|
||||
/>
|
||||
);
|
||||
return <RenderInlineModel primary={instance.title} secondary={text} />;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -5,9 +5,14 @@ import { useNavigate } from 'react-router-dom';
|
||||
import { renderDate } from '../../../defaults/formatters';
|
||||
import { ApiEndpoints } from '../../../enums/ApiEndpoints';
|
||||
import { ModelType } from '../../../enums/ModelType';
|
||||
import { UserRoles } from '../../../enums/Roles';
|
||||
import { buildOrderFields } from '../../../forms/BuildForms';
|
||||
import { getDetailUrl } from '../../../functions/urls';
|
||||
import { useCreateApiFormModal } from '../../../hooks/UseForm';
|
||||
import { useTable } from '../../../hooks/UseTable';
|
||||
import { apiUrl } from '../../../states/ApiState';
|
||||
import { useUserState } from '../../../states/UserState';
|
||||
import { AddItemButton } from '../../buttons/AddItemButton';
|
||||
import { PartHoverCard } from '../../images/Thumbnail';
|
||||
import { ProgressBar } from '../../items/ProgressBar';
|
||||
import { RenderUser } from '../../render/User';
|
||||
@ -88,7 +93,15 @@ function buildOrderTableColumns(): TableColumn[] {
|
||||
/*
|
||||
* Construct a table of build orders, according to the provided parameters
|
||||
*/
|
||||
export function BuildOrderTable({ params = {} }: { params?: any }) {
|
||||
export function BuildOrderTable({
|
||||
partId,
|
||||
parentBuildId,
|
||||
salesOrderId
|
||||
}: {
|
||||
partId?: number;
|
||||
parentBuildId?: number;
|
||||
salesOrderId?: number;
|
||||
}) {
|
||||
const tableColumns = useMemo(() => buildOrderTableColumns(), []);
|
||||
|
||||
const tableFilters: TableFilter[] = useMemo(() => {
|
||||
@ -130,10 +143,39 @@ export function BuildOrderTable({ params = {} }: { params?: any }) {
|
||||
}, []);
|
||||
|
||||
const navigate = useNavigate();
|
||||
const user = useUserState();
|
||||
|
||||
const table = useTable('buildorder');
|
||||
|
||||
const newBuild = useCreateApiFormModal({
|
||||
url: ApiEndpoints.build_order_list,
|
||||
title: t`Add Build Order`,
|
||||
fields: buildOrderFields(),
|
||||
initialData: {
|
||||
part: partId,
|
||||
sales_order: salesOrderId,
|
||||
parent: parentBuildId
|
||||
},
|
||||
onFormSuccess: (data: any) => {
|
||||
if (data.pk) {
|
||||
navigate(getDetailUrl(ModelType.build, data.pk));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const tableActions = useMemo(() => {
|
||||
return [
|
||||
<AddItemButton
|
||||
hidden={!user.hasAddRole(UserRoles.build)}
|
||||
tooltip={t`Add Build Order`}
|
||||
onClick={() => newBuild.open()}
|
||||
/>
|
||||
];
|
||||
}, [user]);
|
||||
|
||||
return (
|
||||
<>
|
||||
{newBuild.modal}
|
||||
<InvenTreeTable
|
||||
url={apiUrl(ApiEndpoints.build_order_list)}
|
||||
tableState={table}
|
||||
@ -141,12 +183,16 @@ export function BuildOrderTable({ params = {} }: { params?: any }) {
|
||||
props={{
|
||||
enableDownload: true,
|
||||
params: {
|
||||
...params,
|
||||
part: partId,
|
||||
sales_order: salesOrderId,
|
||||
parent: parentBuildId,
|
||||
part_detail: true
|
||||
},
|
||||
tableActions: tableActions,
|
||||
tableFilters: tableFilters,
|
||||
onRowClick: (row) => navigate(getDetailUrl(ModelType.build, row.pk))
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
@ -1,18 +1,18 @@
|
||||
import { t } from '@lingui/macro';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import { useCallback, useMemo, useState } from 'react';
|
||||
|
||||
import { ApiEndpoints } from '../../../enums/ApiEndpoints';
|
||||
import { UserRoles } from '../../../enums/Roles';
|
||||
import { addressFields } from '../../../forms/CompanyForms';
|
||||
import {
|
||||
openCreateApiForm,
|
||||
openDeleteApiForm,
|
||||
openEditApiForm
|
||||
} from '../../../functions/forms';
|
||||
useCreateApiFormModal,
|
||||
useDeleteApiFormModal,
|
||||
useEditApiFormModal
|
||||
} from '../../../hooks/UseForm';
|
||||
import { useTable } from '../../../hooks/UseTable';
|
||||
import { apiUrl } from '../../../states/ApiState';
|
||||
import { useUserState } from '../../../states/UserState';
|
||||
import { AddItemButton } from '../../buttons/AddItemButton';
|
||||
import { ApiFormFieldSet } from '../../forms/fields/ApiFormField';
|
||||
import { YesNoButton } from '../../items/YesNoButton';
|
||||
import { TableColumn } from '../Column';
|
||||
import { InvenTreeTable } from '../InvenTreeTable';
|
||||
@ -108,6 +108,54 @@ export function AddressTable({
|
||||
];
|
||||
}, []);
|
||||
|
||||
const addressFields: ApiFormFieldSet = useMemo(() => {
|
||||
return {
|
||||
company: {},
|
||||
title: {},
|
||||
primary: {},
|
||||
line1: {},
|
||||
line2: {},
|
||||
postal_code: {},
|
||||
postal_city: {},
|
||||
province: {},
|
||||
country: {},
|
||||
shipping_notes: {},
|
||||
internal_shipping_notes: {},
|
||||
link: {}
|
||||
};
|
||||
}, []);
|
||||
|
||||
const newAddress = useCreateApiFormModal({
|
||||
url: ApiEndpoints.address_list,
|
||||
title: t`Add Address`,
|
||||
fields: addressFields,
|
||||
initialData: {
|
||||
company: companyId
|
||||
},
|
||||
successMessage: t`Address created`,
|
||||
onFormSuccess: table.refreshTable
|
||||
});
|
||||
|
||||
const [selectedAddress, setSelectedAddress] = useState<number | undefined>(
|
||||
undefined
|
||||
);
|
||||
|
||||
const editAddress = useEditApiFormModal({
|
||||
url: ApiEndpoints.address_list,
|
||||
pk: selectedAddress,
|
||||
title: t`Edit Address`,
|
||||
fields: addressFields,
|
||||
onFormSuccess: table.refreshTable
|
||||
});
|
||||
|
||||
const deleteAddress = useDeleteApiFormModal({
|
||||
url: ApiEndpoints.address_list,
|
||||
pk: selectedAddress,
|
||||
title: t`Delete Address`,
|
||||
onFormSuccess: table.refreshTable,
|
||||
preFormWarning: t`Are you sure you want to delete this address?`
|
||||
});
|
||||
|
||||
const rowActions = useCallback(
|
||||
(record: any) => {
|
||||
let can_edit =
|
||||
@ -122,27 +170,15 @@ export function AddressTable({
|
||||
RowEditAction({
|
||||
hidden: !can_edit,
|
||||
onClick: () => {
|
||||
openEditApiForm({
|
||||
url: ApiEndpoints.address_list,
|
||||
pk: record.pk,
|
||||
title: t`Edit Address`,
|
||||
fields: addressFields(),
|
||||
successMessage: t`Address updated`,
|
||||
onFormSuccess: table.refreshTable
|
||||
});
|
||||
setSelectedAddress(record.pk);
|
||||
editAddress.open();
|
||||
}
|
||||
}),
|
||||
RowDeleteAction({
|
||||
hidden: !can_delete,
|
||||
onClick: () => {
|
||||
openDeleteApiForm({
|
||||
url: ApiEndpoints.address_list,
|
||||
pk: record.pk,
|
||||
title: t`Delete Address`,
|
||||
successMessage: t`Address deleted`,
|
||||
onFormSuccess: table.refreshTable,
|
||||
preFormWarning: t`Are you sure you want to delete this address?`
|
||||
});
|
||||
setSelectedAddress(record.pk);
|
||||
deleteAddress.open();
|
||||
}
|
||||
})
|
||||
];
|
||||
@ -150,20 +186,6 @@ export function AddressTable({
|
||||
[user]
|
||||
);
|
||||
|
||||
const addAddress = useCallback(() => {
|
||||
let fields = addressFields();
|
||||
|
||||
fields['company'].value = companyId;
|
||||
|
||||
openCreateApiForm({
|
||||
url: ApiEndpoints.address_list,
|
||||
title: t`Add Address`,
|
||||
fields: fields,
|
||||
successMessage: t`Address created`,
|
||||
onFormSuccess: table.refreshTable
|
||||
});
|
||||
}, [companyId]);
|
||||
|
||||
const tableActions = useMemo(() => {
|
||||
let can_add =
|
||||
user.hasChangeRole(UserRoles.purchase_order) ||
|
||||
@ -172,13 +194,17 @@ export function AddressTable({
|
||||
return [
|
||||
<AddItemButton
|
||||
tooltip={t`Add Address`}
|
||||
onClick={addAddress}
|
||||
onClick={() => newAddress.open()}
|
||||
disabled={!can_add}
|
||||
/>
|
||||
];
|
||||
}, [user]);
|
||||
|
||||
return (
|
||||
<>
|
||||
{newAddress.modal}
|
||||
{editAddress.modal}
|
||||
{deleteAddress.modal}
|
||||
<InvenTreeTable
|
||||
url={apiUrl(ApiEndpoints.address_list)}
|
||||
tableState={table}
|
||||
@ -192,5 +218,6 @@ export function AddressTable({
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
@ -4,8 +4,13 @@ import { useMemo } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
import { ApiEndpoints } from '../../../enums/ApiEndpoints';
|
||||
import { UserRoles } from '../../../enums/Roles';
|
||||
import { companyFields } from '../../../forms/CompanyForms';
|
||||
import { useCreateApiFormModal } from '../../../hooks/UseForm';
|
||||
import { useTable } from '../../../hooks/UseTable';
|
||||
import { apiUrl } from '../../../states/ApiState';
|
||||
import { useUserState } from '../../../states/UserState';
|
||||
import { AddItemButton } from '../../buttons/AddItemButton';
|
||||
import { Thumbnail } from '../../images/Thumbnail';
|
||||
import { DescriptionColumn } from '../ColumnRenderers';
|
||||
import { InvenTreeTable } from '../InvenTreeTable';
|
||||
@ -24,6 +29,7 @@ export function CompanyTable({
|
||||
const table = useTable('company');
|
||||
|
||||
const navigate = useNavigate();
|
||||
const user = useUserState();
|
||||
|
||||
const columns = useMemo(() => {
|
||||
return [
|
||||
@ -53,7 +59,39 @@ export function CompanyTable({
|
||||
];
|
||||
}, []);
|
||||
|
||||
const newCompany = useCreateApiFormModal({
|
||||
url: ApiEndpoints.company_list,
|
||||
title: t`New Company`,
|
||||
fields: companyFields(),
|
||||
initialData: params,
|
||||
onFormSuccess: (response) => {
|
||||
console.log('onFormSuccess:', response);
|
||||
if (response.pk) {
|
||||
let base = path ?? 'company';
|
||||
navigate(`/${base}/${response.pk}`);
|
||||
} else {
|
||||
table.refreshTable();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const tableActions = useMemo(() => {
|
||||
const can_add =
|
||||
user.hasAddRole(UserRoles.purchase_order) ||
|
||||
user.hasAddRole(UserRoles.sales_order);
|
||||
|
||||
return [
|
||||
<AddItemButton
|
||||
tooltip={t`Add Company`}
|
||||
onClick={() => newCompany.open()}
|
||||
hidden={!can_add}
|
||||
/>
|
||||
];
|
||||
}, [user]);
|
||||
|
||||
return (
|
||||
<>
|
||||
{newCompany.modal}
|
||||
<InvenTreeTable
|
||||
url={apiUrl(ApiEndpoints.company_list)}
|
||||
tableState={table}
|
||||
@ -62,6 +100,7 @@ export function CompanyTable({
|
||||
params: {
|
||||
...params
|
||||
},
|
||||
tableActions: tableActions,
|
||||
onRowClick: (row: any) => {
|
||||
if (row.pk) {
|
||||
let base = path ?? 'company';
|
||||
@ -70,5 +109,6 @@ export function CompanyTable({
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
@ -1,18 +1,18 @@
|
||||
import { t } from '@lingui/macro';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import { useCallback, useMemo, useState } from 'react';
|
||||
|
||||
import { ApiEndpoints } from '../../../enums/ApiEndpoints';
|
||||
import { UserRoles } from '../../../enums/Roles';
|
||||
import { contactFields } from '../../../forms/CompanyForms';
|
||||
import {
|
||||
openCreateApiForm,
|
||||
openDeleteApiForm,
|
||||
openEditApiForm
|
||||
} from '../../../functions/forms';
|
||||
useCreateApiFormModal,
|
||||
useDeleteApiFormModal,
|
||||
useEditApiFormModal
|
||||
} from '../../../hooks/UseForm';
|
||||
import { useTable } from '../../../hooks/UseTable';
|
||||
import { apiUrl } from '../../../states/ApiState';
|
||||
import { useUserState } from '../../../states/UserState';
|
||||
import { AddItemButton } from '../../buttons/AddItemButton';
|
||||
import { ApiFormFieldSet } from '../../forms/fields/ApiFormField';
|
||||
import { TableColumn } from '../Column';
|
||||
import { InvenTreeTable } from '../InvenTreeTable';
|
||||
import { RowDeleteAction, RowEditAction } from '../RowActions';
|
||||
@ -57,6 +57,45 @@ export function ContactTable({
|
||||
];
|
||||
}, []);
|
||||
|
||||
const contactFields: ApiFormFieldSet = useMemo(() => {
|
||||
return {
|
||||
company: {},
|
||||
name: {},
|
||||
phone: {},
|
||||
email: {},
|
||||
role: {}
|
||||
};
|
||||
}, []);
|
||||
|
||||
const [selectedContact, setSelectedContact] = useState<number | undefined>(
|
||||
undefined
|
||||
);
|
||||
|
||||
const editContact = useEditApiFormModal({
|
||||
url: ApiEndpoints.contact_list,
|
||||
pk: selectedContact,
|
||||
title: t`Edit Contact`,
|
||||
fields: contactFields,
|
||||
onFormSuccess: table.refreshTable
|
||||
});
|
||||
|
||||
const newContact = useCreateApiFormModal({
|
||||
url: ApiEndpoints.contact_list,
|
||||
title: t`Add Contact`,
|
||||
initialData: {
|
||||
company: companyId
|
||||
},
|
||||
fields: contactFields,
|
||||
onFormSuccess: table.refreshTable
|
||||
});
|
||||
|
||||
const deleteContact = useDeleteApiFormModal({
|
||||
url: ApiEndpoints.contact_list,
|
||||
pk: selectedContact,
|
||||
title: t`Delete Contact`,
|
||||
onFormSuccess: table.refreshTable
|
||||
});
|
||||
|
||||
const rowActions = useCallback(
|
||||
(record: any) => {
|
||||
let can_edit =
|
||||
@ -70,27 +109,15 @@ export function ContactTable({
|
||||
RowEditAction({
|
||||
hidden: !can_edit,
|
||||
onClick: () => {
|
||||
openEditApiForm({
|
||||
url: ApiEndpoints.contact_list,
|
||||
pk: record.pk,
|
||||
title: t`Edit Contact`,
|
||||
fields: contactFields(),
|
||||
successMessage: t`Contact updated`,
|
||||
onFormSuccess: table.refreshTable
|
||||
});
|
||||
setSelectedContact(record.pk);
|
||||
editContact.open();
|
||||
}
|
||||
}),
|
||||
RowDeleteAction({
|
||||
hidden: !can_delete,
|
||||
onClick: () => {
|
||||
openDeleteApiForm({
|
||||
url: ApiEndpoints.contact_list,
|
||||
pk: record.pk,
|
||||
title: t`Delete Contact`,
|
||||
successMessage: t`Contact deleted`,
|
||||
onFormSuccess: table.refreshTable,
|
||||
preFormWarning: t`Are you sure you want to delete this contact?`
|
||||
});
|
||||
setSelectedContact(record.pk);
|
||||
deleteContact.open();
|
||||
}
|
||||
})
|
||||
];
|
||||
@ -98,20 +125,6 @@ export function ContactTable({
|
||||
[user]
|
||||
);
|
||||
|
||||
const addContact = useCallback(() => {
|
||||
var fields = contactFields();
|
||||
|
||||
fields['company'].value = companyId;
|
||||
|
||||
openCreateApiForm({
|
||||
url: ApiEndpoints.contact_list,
|
||||
title: t`Create Contact`,
|
||||
fields: fields,
|
||||
successMessage: t`Contact created`,
|
||||
onFormSuccess: table.refreshTable
|
||||
});
|
||||
}, [companyId]);
|
||||
|
||||
const tableActions = useMemo(() => {
|
||||
let can_add =
|
||||
user.hasAddRole(UserRoles.purchase_order) ||
|
||||
@ -120,13 +133,17 @@ export function ContactTable({
|
||||
return [
|
||||
<AddItemButton
|
||||
tooltip={t`Add contact`}
|
||||
onClick={addContact}
|
||||
onClick={() => newContact.open()}
|
||||
disabled={!can_add}
|
||||
/>
|
||||
];
|
||||
}, [user]);
|
||||
|
||||
return (
|
||||
<>
|
||||
{newContact.modal}
|
||||
{editContact.modal}
|
||||
{deleteContact.modal}
|
||||
<InvenTreeTable
|
||||
url={apiUrl(ApiEndpoints.contact_list)}
|
||||
tableState={table}
|
||||
@ -140,5 +157,6 @@ export function ContactTable({
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
@ -1,13 +1,16 @@
|
||||
import { t } from '@lingui/macro';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import { useCallback, useMemo, useState } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
import { ApiEndpoints } from '../../../enums/ApiEndpoints';
|
||||
import { ModelType } from '../../../enums/ModelType';
|
||||
import { UserRoles } from '../../../enums/Roles';
|
||||
import { partCategoryFields } from '../../../forms/PartForms';
|
||||
import { openCreateApiForm, openEditApiForm } from '../../../functions/forms';
|
||||
import { getDetailUrl } from '../../../functions/urls';
|
||||
import {
|
||||
useCreateApiFormModal,
|
||||
useEditApiFormModal
|
||||
} from '../../../hooks/UseForm';
|
||||
import { useTable } from '../../../hooks/UseTable';
|
||||
import { apiUrl } from '../../../states/ApiState';
|
||||
import { useUserState } from '../../../states/UserState';
|
||||
@ -73,26 +76,33 @@ export function PartCategoryTable({ parentId }: { parentId?: any }) {
|
||||
];
|
||||
}, []);
|
||||
|
||||
const addCategory = useCallback(() => {
|
||||
let fields = partCategoryFields({});
|
||||
|
||||
if (parentId) {
|
||||
fields['parent'].value = parentId;
|
||||
}
|
||||
|
||||
openCreateApiForm({
|
||||
url: apiUrl(ApiEndpoints.category_list),
|
||||
title: t`Add Part Category`,
|
||||
fields: fields,
|
||||
const newCategory = useCreateApiFormModal({
|
||||
url: ApiEndpoints.category_list,
|
||||
title: t`New Part Category`,
|
||||
fields: partCategoryFields({}),
|
||||
initialData: {
|
||||
parent: parentId
|
||||
},
|
||||
onFormSuccess(data: any) {
|
||||
if (data.pk) {
|
||||
navigate(`/part/category/${data.pk}`);
|
||||
navigate(getDetailUrl(ModelType.partcategory, data.pk));
|
||||
} else {
|
||||
table.refreshTable();
|
||||
}
|
||||
}
|
||||
});
|
||||
}, [parentId]);
|
||||
|
||||
const [selectedCategory, setSelectedCategory] = useState<number | undefined>(
|
||||
undefined
|
||||
);
|
||||
|
||||
const editCategory = useEditApiFormModal({
|
||||
url: ApiEndpoints.category_list,
|
||||
pk: selectedCategory,
|
||||
title: t`Edit Part Category`,
|
||||
fields: partCategoryFields({}),
|
||||
onFormSuccess: table.refreshTable
|
||||
});
|
||||
|
||||
const tableActions = useMemo(() => {
|
||||
let can_add = user.hasAddRole(UserRoles.part_category);
|
||||
@ -100,7 +110,7 @@ export function PartCategoryTable({ parentId }: { parentId?: any }) {
|
||||
return [
|
||||
<AddItemButton
|
||||
tooltip={t`Add Part Category`}
|
||||
onClick={addCategory}
|
||||
onClick={() => newCategory.open()}
|
||||
disabled={!can_add}
|
||||
/>
|
||||
];
|
||||
@ -114,14 +124,8 @@ export function PartCategoryTable({ parentId }: { parentId?: any }) {
|
||||
RowEditAction({
|
||||
hidden: !can_edit,
|
||||
onClick: () => {
|
||||
openEditApiForm({
|
||||
url: ApiEndpoints.category_list,
|
||||
pk: record.pk,
|
||||
title: t`Edit Part Category`,
|
||||
fields: partCategoryFields({}),
|
||||
successMessage: t`Part category updated`,
|
||||
onFormSuccess: table.refreshTable
|
||||
});
|
||||
setSelectedCategory(record.pk);
|
||||
editCategory.open();
|
||||
}
|
||||
})
|
||||
];
|
||||
@ -130,6 +134,9 @@ export function PartCategoryTable({ parentId }: { parentId?: any }) {
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
{newCategory.modal}
|
||||
{editCategory.modal}
|
||||
<InvenTreeTable
|
||||
url={apiUrl(ApiEndpoints.category_list)}
|
||||
tableState={table}
|
||||
@ -142,9 +149,10 @@ export function PartCategoryTable({ parentId }: { parentId?: any }) {
|
||||
tableFilters: tableFilters,
|
||||
tableActions: tableActions,
|
||||
rowActions: rowActions,
|
||||
onRowClick: (record, index, event) =>
|
||||
onRowClick: (record) =>
|
||||
navigate(getDetailUrl(ModelType.partcategory, record.pk))
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
@ -1,18 +1,19 @@
|
||||
import { t } from '@lingui/macro';
|
||||
import { Text } from '@mantine/core';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import { useCallback, useMemo, useState } from 'react';
|
||||
|
||||
import { ApiEndpoints } from '../../../enums/ApiEndpoints';
|
||||
import { UserRoles } from '../../../enums/Roles';
|
||||
import {
|
||||
openCreateApiForm,
|
||||
openDeleteApiForm,
|
||||
openEditApiForm
|
||||
} from '../../../functions/forms';
|
||||
useCreateApiFormModal,
|
||||
useDeleteApiFormModal,
|
||||
useEditApiFormModal
|
||||
} from '../../../hooks/UseForm';
|
||||
import { useTable } from '../../../hooks/UseTable';
|
||||
import { apiUrl } from '../../../states/ApiState';
|
||||
import { useUserState } from '../../../states/UserState';
|
||||
import { AddItemButton } from '../../buttons/AddItemButton';
|
||||
import { ApiFormFieldSet } from '../../forms/fields/ApiFormField';
|
||||
import { YesNoButton } from '../../items/YesNoButton';
|
||||
import { TableColumn } from '../Column';
|
||||
import { PartColumn } from '../ColumnRenderers';
|
||||
@ -36,6 +37,12 @@ export function PartParameterTable({ partId }: { partId: any }) {
|
||||
sortable: true,
|
||||
render: (record: any) => PartColumn(record?.part_detail)
|
||||
},
|
||||
{
|
||||
accessor: 'part_detail.IPN',
|
||||
title: t`IPN`,
|
||||
sortable: false,
|
||||
switchable: true
|
||||
},
|
||||
{
|
||||
accessor: 'name',
|
||||
title: t`Parameter`,
|
||||
@ -85,6 +92,43 @@ export function PartParameterTable({ partId }: { partId: any }) {
|
||||
];
|
||||
}, [partId]);
|
||||
|
||||
const partParameterFields: ApiFormFieldSet = useMemo(() => {
|
||||
return {
|
||||
part: {},
|
||||
template: {},
|
||||
data: {}
|
||||
};
|
||||
}, []);
|
||||
|
||||
const newParameter = useCreateApiFormModal({
|
||||
url: ApiEndpoints.part_parameter_list,
|
||||
title: t`New Part Parameter`,
|
||||
fields: partParameterFields,
|
||||
initialData: {
|
||||
part: partId
|
||||
},
|
||||
onFormSuccess: table.refreshTable
|
||||
});
|
||||
|
||||
const [selectedParameter, setSelectedParameter] = useState<
|
||||
number | undefined
|
||||
>(undefined);
|
||||
|
||||
const editParameter = useEditApiFormModal({
|
||||
url: ApiEndpoints.part_parameter_list,
|
||||
pk: selectedParameter,
|
||||
title: t`Edit Part Parameter`,
|
||||
fields: partParameterFields,
|
||||
onFormSuccess: table.refreshTable
|
||||
});
|
||||
|
||||
const deleteParameter = useDeleteApiFormModal({
|
||||
url: ApiEndpoints.part_parameter_list,
|
||||
pk: selectedParameter,
|
||||
title: t`Delete Part Parameter`,
|
||||
onFormSuccess: table.refreshTable
|
||||
});
|
||||
|
||||
// Callback for row actions
|
||||
const rowActions = useCallback(
|
||||
(record: any) => {
|
||||
@ -93,87 +137,44 @@ export function PartParameterTable({ partId }: { partId: any }) {
|
||||
return [];
|
||||
}
|
||||
|
||||
let actions = [];
|
||||
|
||||
actions.push(
|
||||
return [
|
||||
RowEditAction({
|
||||
tooltip: t`Edit Part Parameter`,
|
||||
hidden: !user.hasChangeRole(UserRoles.part),
|
||||
onClick: () => {
|
||||
openEditApiForm({
|
||||
url: ApiEndpoints.part_parameter_list,
|
||||
pk: record.pk,
|
||||
title: t`Edit Part Parameter`,
|
||||
fields: {
|
||||
part: {
|
||||
hidden: true
|
||||
},
|
||||
template: {},
|
||||
data: {}
|
||||
},
|
||||
successMessage: t`Part parameter updated`,
|
||||
onFormSuccess: table.refreshTable
|
||||
});
|
||||
setSelectedParameter(record.pk);
|
||||
editParameter.open();
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
actions.push(
|
||||
}),
|
||||
RowDeleteAction({
|
||||
tooltip: t`Delete Part Parameter`,
|
||||
hidden: !user.hasDeleteRole(UserRoles.part),
|
||||
onClick: () => {
|
||||
openDeleteApiForm({
|
||||
url: ApiEndpoints.part_parameter_list,
|
||||
pk: record.pk,
|
||||
title: t`Delete Part Parameter`,
|
||||
successMessage: t`Part parameter deleted`,
|
||||
onFormSuccess: table.refreshTable,
|
||||
preFormWarning: t`Are you sure you want to remove this parameter?`
|
||||
});
|
||||
setSelectedParameter(record.pk);
|
||||
deleteParameter.open();
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
return actions;
|
||||
];
|
||||
},
|
||||
[partId, user]
|
||||
);
|
||||
|
||||
const addParameter = useCallback(() => {
|
||||
if (!partId) {
|
||||
return;
|
||||
}
|
||||
|
||||
openCreateApiForm({
|
||||
url: ApiEndpoints.part_parameter_list,
|
||||
title: t`Add Part Parameter`,
|
||||
fields: {
|
||||
part: {
|
||||
hidden: true,
|
||||
value: partId
|
||||
},
|
||||
template: {},
|
||||
data: {}
|
||||
},
|
||||
successMessage: t`Part parameter added`,
|
||||
onFormSuccess: table.refreshTable
|
||||
});
|
||||
}, [partId]);
|
||||
|
||||
// Custom table actions
|
||||
const tableActions = useMemo(() => {
|
||||
let actions = [];
|
||||
|
||||
// TODO: Hide if user does not have permission to edit parts
|
||||
actions.push(
|
||||
<AddItemButton tooltip={t`Add parameter`} onClick={addParameter} />
|
||||
);
|
||||
|
||||
return actions;
|
||||
}, []);
|
||||
return [
|
||||
<AddItemButton
|
||||
hidden={!user.hasAddRole(UserRoles.part)}
|
||||
tooltip={t`Add parameter`}
|
||||
onClick={() => newParameter.open()}
|
||||
/>
|
||||
];
|
||||
}, [user]);
|
||||
|
||||
return (
|
||||
<>
|
||||
{newParameter.modal}
|
||||
{editParameter.modal}
|
||||
{deleteParameter.modal}
|
||||
<InvenTreeTable
|
||||
url={apiUrl(ApiEndpoints.part_parameter_list)}
|
||||
tableState={table}
|
||||
@ -195,5 +196,6 @@ export function PartParameterTable({ partId }: { partId: any }) {
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
@ -1,18 +1,18 @@
|
||||
import { t } from '@lingui/macro';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import { useCallback, useMemo, useState } from 'react';
|
||||
|
||||
import { ApiEndpoints } from '../../../enums/ApiEndpoints';
|
||||
import { UserRoles } from '../../../enums/Roles';
|
||||
import { partParameterTemplateFields } from '../../../forms/PartForms';
|
||||
import {
|
||||
openCreateApiForm,
|
||||
openDeleteApiForm,
|
||||
openEditApiForm
|
||||
} from '../../../functions/forms';
|
||||
useCreateApiFormModal,
|
||||
useDeleteApiFormModal,
|
||||
useEditApiFormModal
|
||||
} from '../../../hooks/UseForm';
|
||||
import { useTable } from '../../../hooks/UseTable';
|
||||
import { apiUrl } from '../../../states/ApiState';
|
||||
import { useUserState } from '../../../states/UserState';
|
||||
import { AddItemButton } from '../../buttons/AddItemButton';
|
||||
import { ApiFormFieldSet } from '../../forms/fields/ApiFormField';
|
||||
import { TableColumn } from '../Column';
|
||||
import { DescriptionColumn } from '../ColumnRenderers';
|
||||
import { TableFilter } from '../Filter';
|
||||
@ -69,6 +69,42 @@ export default function PartParameterTemplateTable() {
|
||||
];
|
||||
}, []);
|
||||
|
||||
const partParameterTemplateFields: ApiFormFieldSet = useMemo(() => {
|
||||
return {
|
||||
name: {},
|
||||
description: {},
|
||||
units: {},
|
||||
choices: {},
|
||||
checkbox: {}
|
||||
};
|
||||
}, []);
|
||||
|
||||
const newTemplate = useCreateApiFormModal({
|
||||
url: ApiEndpoints.part_parameter_template_list,
|
||||
title: t`Add Parameter Template`,
|
||||
fields: partParameterTemplateFields,
|
||||
onFormSuccess: table.refreshTable
|
||||
});
|
||||
|
||||
const [selectedTemplate, setSelectedTemplate] = useState<number | undefined>(
|
||||
undefined
|
||||
);
|
||||
|
||||
const editTemplate = useEditApiFormModal({
|
||||
url: ApiEndpoints.part_parameter_template_list,
|
||||
pk: selectedTemplate,
|
||||
title: t`Edit Parameter Template`,
|
||||
fields: partParameterTemplateFields,
|
||||
onFormSuccess: table.refreshTable
|
||||
});
|
||||
|
||||
const deleteTemplate = useDeleteApiFormModal({
|
||||
url: ApiEndpoints.part_parameter_template_list,
|
||||
pk: selectedTemplate,
|
||||
title: t`Delete Parameter Template`,
|
||||
onFormSuccess: table.refreshTable
|
||||
});
|
||||
|
||||
// Callback for row actions
|
||||
const rowActions = useCallback(
|
||||
(record: any) => {
|
||||
@ -76,27 +112,15 @@ export default function PartParameterTemplateTable() {
|
||||
RowEditAction({
|
||||
hidden: !user.hasChangeRole(UserRoles.part),
|
||||
onClick: () => {
|
||||
openEditApiForm({
|
||||
url: ApiEndpoints.part_parameter_template_list,
|
||||
pk: record.pk,
|
||||
title: t`Edit Parameter Template`,
|
||||
fields: partParameterTemplateFields(),
|
||||
successMessage: t`Parameter template updated`,
|
||||
onFormSuccess: table.refreshTable
|
||||
});
|
||||
setSelectedTemplate(record.pk);
|
||||
editTemplate.open();
|
||||
}
|
||||
}),
|
||||
RowDeleteAction({
|
||||
hidden: !user.hasDeleteRole(UserRoles.part),
|
||||
onClick: () => {
|
||||
openDeleteApiForm({
|
||||
url: ApiEndpoints.part_parameter_template_list,
|
||||
pk: record.pk,
|
||||
title: t`Delete Parameter Template`,
|
||||
successMessage: t`Parameter template deleted`,
|
||||
onFormSuccess: table.refreshTable,
|
||||
preFormWarning: t`Are you sure you want to remove this parameter template?`
|
||||
});
|
||||
setSelectedTemplate(record.pk);
|
||||
deleteTemplate.open();
|
||||
}
|
||||
})
|
||||
];
|
||||
@ -104,27 +128,21 @@ export default function PartParameterTemplateTable() {
|
||||
[user]
|
||||
);
|
||||
|
||||
const addParameterTemplate = useCallback(() => {
|
||||
openCreateApiForm({
|
||||
url: ApiEndpoints.part_parameter_template_list,
|
||||
title: t`Create Parameter Template`,
|
||||
fields: partParameterTemplateFields(),
|
||||
successMessage: t`Parameter template created`,
|
||||
onFormSuccess: table.refreshTable
|
||||
});
|
||||
}, []);
|
||||
|
||||
const tableActions = useMemo(() => {
|
||||
return [
|
||||
<AddItemButton
|
||||
tooltip={t`Add parameter template`}
|
||||
onClick={addParameterTemplate}
|
||||
onClick={() => newTemplate.open()}
|
||||
disabled={!user.hasAddRole(UserRoles.part)}
|
||||
/>
|
||||
];
|
||||
}, [user]);
|
||||
|
||||
return (
|
||||
<>
|
||||
{newTemplate.modal}
|
||||
{editTemplate.modal}
|
||||
{deleteTemplate.modal}
|
||||
<InvenTreeTable
|
||||
url={apiUrl(ApiEndpoints.part_parameter_template_list)}
|
||||
tableState={table}
|
||||
@ -135,5 +153,6 @@ export default function PartParameterTemplateTable() {
|
||||
tableActions: tableActions
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
@ -1,18 +1,18 @@
|
||||
import { t } from '@lingui/macro';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import { useCallback, useMemo, useState } from 'react';
|
||||
|
||||
import { ApiEndpoints } from '../../../enums/ApiEndpoints';
|
||||
import { UserRoles } from '../../../enums/Roles';
|
||||
import { partTestTemplateFields } from '../../../forms/PartForms';
|
||||
import {
|
||||
openCreateApiForm,
|
||||
openDeleteApiForm,
|
||||
openEditApiForm
|
||||
} from '../../../functions/forms';
|
||||
useCreateApiFormModal,
|
||||
useDeleteApiFormModal,
|
||||
useEditApiFormModal
|
||||
} from '../../../hooks/UseForm';
|
||||
import { useTable } from '../../../hooks/UseTable';
|
||||
import { apiUrl } from '../../../states/ApiState';
|
||||
import { useUserState } from '../../../states/UserState';
|
||||
import { AddItemButton } from '../../buttons/AddItemButton';
|
||||
import { ApiFormFieldSet } from '../../forms/fields/ApiFormField';
|
||||
import { TableColumn } from '../Column';
|
||||
import { BooleanColumn, DescriptionColumn } from '../ColumnRenderers';
|
||||
import { TableFilter } from '../Filter';
|
||||
@ -69,6 +69,48 @@ export default function PartTestTemplateTable({ partId }: { partId: number }) {
|
||||
];
|
||||
}, []);
|
||||
|
||||
const partTestTemplateFields: ApiFormFieldSet = useMemo(() => {
|
||||
return {
|
||||
part: {
|
||||
hidden: true
|
||||
},
|
||||
test_name: {},
|
||||
description: {},
|
||||
required: {},
|
||||
requires_value: {},
|
||||
requires_attachment: {}
|
||||
};
|
||||
}, []);
|
||||
|
||||
const newTestTemplate = useCreateApiFormModal({
|
||||
url: ApiEndpoints.part_test_template_list,
|
||||
title: t`Add Test Template`,
|
||||
fields: partTestTemplateFields,
|
||||
initialData: {
|
||||
part: partId
|
||||
},
|
||||
onFormSuccess: table.refreshTable
|
||||
});
|
||||
|
||||
const [selectedTest, setSelectedTest] = useState<number | undefined>(
|
||||
undefined
|
||||
);
|
||||
|
||||
const editTestTemplate = useEditApiFormModal({
|
||||
url: ApiEndpoints.part_test_template_list,
|
||||
pk: selectedTest,
|
||||
title: t`Edit Test Template`,
|
||||
fields: partTestTemplateFields,
|
||||
onFormSuccess: table.refreshTable
|
||||
});
|
||||
|
||||
const deleteTestTemplate = useDeleteApiFormModal({
|
||||
url: ApiEndpoints.part_test_template_list,
|
||||
pk: selectedTest,
|
||||
title: t`Delete Test Template`,
|
||||
onFormSuccess: table.refreshTable
|
||||
});
|
||||
|
||||
const rowActions = useCallback(
|
||||
(record: any) => {
|
||||
let can_edit = user.hasChangeRole(UserRoles.part);
|
||||
@ -78,26 +120,15 @@ export default function PartTestTemplateTable({ partId }: { partId: number }) {
|
||||
RowEditAction({
|
||||
hidden: !can_edit,
|
||||
onClick: () => {
|
||||
openEditApiForm({
|
||||
url: ApiEndpoints.part_test_template_list,
|
||||
pk: record.pk,
|
||||
title: t`Edit Test Template`,
|
||||
fields: partTestTemplateFields(),
|
||||
successMessage: t`Template updated`,
|
||||
onFormSuccess: table.refreshTable
|
||||
});
|
||||
setSelectedTest(record.pk);
|
||||
editTestTemplate.open();
|
||||
}
|
||||
}),
|
||||
RowDeleteAction({
|
||||
hidden: !can_delete,
|
||||
onClick: () => {
|
||||
openDeleteApiForm({
|
||||
url: ApiEndpoints.part_test_template_list,
|
||||
pk: record.pk,
|
||||
title: t`Delete Test Template`,
|
||||
successMessage: t`Test Template deleted`,
|
||||
onFormSuccess: table.refreshTable
|
||||
});
|
||||
setSelectedTest(record.pk);
|
||||
deleteTestTemplate.open();
|
||||
}
|
||||
})
|
||||
];
|
||||
@ -105,33 +136,23 @@ export default function PartTestTemplateTable({ partId }: { partId: number }) {
|
||||
[user]
|
||||
);
|
||||
|
||||
const addTestTemplate = useCallback(() => {
|
||||
let fields = partTestTemplateFields();
|
||||
|
||||
fields['part'].value = partId;
|
||||
|
||||
openCreateApiForm({
|
||||
url: ApiEndpoints.part_test_template_list,
|
||||
title: t`Create Test Template`,
|
||||
fields: fields,
|
||||
successMessage: t`Template created`,
|
||||
onFormSuccess: table.refreshTable
|
||||
});
|
||||
}, [partId]);
|
||||
|
||||
const tableActions = useMemo(() => {
|
||||
let can_add = user.hasAddRole(UserRoles.part);
|
||||
|
||||
return [
|
||||
<AddItemButton
|
||||
tooltip={t`Add Test Template`}
|
||||
onClick={addTestTemplate}
|
||||
onClick={() => newTestTemplate.open()}
|
||||
disabled={!can_add}
|
||||
/>
|
||||
];
|
||||
}, [user]);
|
||||
|
||||
return (
|
||||
<>
|
||||
{newTestTemplate.modal}
|
||||
{editTestTemplate.modal}
|
||||
{deleteTestTemplate.modal}
|
||||
<InvenTreeTable
|
||||
url={apiUrl(ApiEndpoints.part_test_template_list)}
|
||||
tableState={table}
|
||||
@ -145,5 +166,6 @@ export default function PartTestTemplateTable({ partId }: { partId: number }) {
|
||||
rowActions: rowActions
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
@ -1,15 +1,19 @@
|
||||
import { t } from '@lingui/macro';
|
||||
import { ActionIcon, Group, Text, Tooltip } from '@mantine/core';
|
||||
import { IconLayersLinked } from '@tabler/icons-react';
|
||||
import { ReactNode, useCallback, useMemo } from 'react';
|
||||
import { Group, Text } from '@mantine/core';
|
||||
import { ReactNode, useCallback, useMemo, useState } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
import { ApiEndpoints } from '../../../enums/ApiEndpoints';
|
||||
import { UserRoles } from '../../../enums/Roles';
|
||||
import { openCreateApiForm, openDeleteApiForm } from '../../../functions/forms';
|
||||
import {
|
||||
useCreateApiFormModal,
|
||||
useDeleteApiFormModal
|
||||
} from '../../../hooks/UseForm';
|
||||
import { useTable } from '../../../hooks/UseTable';
|
||||
import { apiUrl } from '../../../states/ApiState';
|
||||
import { useUserState } from '../../../states/UserState';
|
||||
import { AddItemButton } from '../../buttons/AddItemButton';
|
||||
import { ApiFormFieldSet } from '../../forms/fields/ApiFormField';
|
||||
import { Thumbnail } from '../../images/Thumbnail';
|
||||
import { TableColumn } from '../Column';
|
||||
import { InvenTreeTable } from '../InvenTreeTable';
|
||||
@ -66,55 +70,54 @@ export function RelatedPartTable({ partId }: { partId: number }): ReactNode {
|
||||
];
|
||||
}, [partId]);
|
||||
|
||||
const addRelatedPart = useCallback(() => {
|
||||
openCreateApiForm({
|
||||
title: t`Add Related Part`,
|
||||
url: ApiEndpoints.related_part_list,
|
||||
fields: {
|
||||
const relatedPartFields: ApiFormFieldSet = useMemo(() => {
|
||||
return {
|
||||
part_1: {
|
||||
hidden: true,
|
||||
value: partId
|
||||
hidden: true
|
||||
},
|
||||
part_2: {
|
||||
label: t`Related Part`
|
||||
}
|
||||
},
|
||||
successMessage: t`Related part added`,
|
||||
onFormSuccess: table.refreshTable
|
||||
});
|
||||
}, [partId]);
|
||||
|
||||
const customActions: ReactNode[] = useMemo(() => {
|
||||
// TODO: Hide if user does not have permission to edit parts
|
||||
let actions = [];
|
||||
|
||||
actions.push(
|
||||
<Tooltip label={t`Add related part`}>
|
||||
<ActionIcon radius="sm" onClick={addRelatedPart}>
|
||||
<IconLayersLinked />
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
);
|
||||
|
||||
return actions;
|
||||
part_2: {}
|
||||
};
|
||||
}, []);
|
||||
|
||||
// Generate row actions
|
||||
// TODO: Hide if user does not have permission to edit parts
|
||||
const newRelatedPart = useCreateApiFormModal({
|
||||
url: ApiEndpoints.related_part_list,
|
||||
title: t`Add Related Part`,
|
||||
fields: relatedPartFields,
|
||||
initialData: {
|
||||
part_1: partId
|
||||
},
|
||||
onFormSuccess: table.refreshTable
|
||||
});
|
||||
|
||||
const [selectedRelatedPart, setSelectedRelatedPart] = useState<
|
||||
number | undefined
|
||||
>(undefined);
|
||||
|
||||
const deleteRelatedPart = useDeleteApiFormModal({
|
||||
url: ApiEndpoints.related_part_list,
|
||||
pk: selectedRelatedPart,
|
||||
title: t`Delete Related Part`,
|
||||
onFormSuccess: table.refreshTable
|
||||
});
|
||||
|
||||
const tableActions: ReactNode[] = useMemo(() => {
|
||||
return [
|
||||
<AddItemButton
|
||||
tooltip={t`Add related part`}
|
||||
hidden={!user.hasAddRole(UserRoles.part)}
|
||||
onClick={() => newRelatedPart.open()}
|
||||
/>
|
||||
];
|
||||
}, [user]);
|
||||
|
||||
const rowActions = useCallback(
|
||||
(record: any) => {
|
||||
return [
|
||||
RowDeleteAction({
|
||||
hidden: !user.hasDeleteRole(UserRoles.part),
|
||||
onClick: () => {
|
||||
openDeleteApiForm({
|
||||
url: ApiEndpoints.related_part_list,
|
||||
pk: record.pk,
|
||||
title: t`Delete Related Part`,
|
||||
successMessage: t`Related part deleted`,
|
||||
preFormWarning: t`Are you sure you want to remove this relationship?`,
|
||||
onFormSuccess: table.refreshTable
|
||||
});
|
||||
setSelectedRelatedPart(record.pk);
|
||||
deleteRelatedPart.open();
|
||||
}
|
||||
})
|
||||
];
|
||||
@ -123,6 +126,9 @@ export function RelatedPartTable({ partId }: { partId: number }): ReactNode {
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
{newRelatedPart.modal}
|
||||
{deleteRelatedPart.modal}
|
||||
<InvenTreeTable
|
||||
url={apiUrl(ApiEndpoints.related_part_list)}
|
||||
tableState={table}
|
||||
@ -133,8 +139,9 @@ export function RelatedPartTable({ partId }: { partId: number }): ReactNode {
|
||||
category_detail: true
|
||||
},
|
||||
rowActions: rowActions,
|
||||
tableActions: customActions
|
||||
tableActions: tableActions
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { t } from '@lingui/macro';
|
||||
import { Text } from '@mantine/core';
|
||||
import { IconSquareArrowRight } from '@tabler/icons-react';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import { useCallback, useMemo, useState } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
import { ProgressBar } from '../../../components/items/ProgressBar';
|
||||
@ -9,8 +9,12 @@ import { ApiEndpoints } from '../../../enums/ApiEndpoints';
|
||||
import { ModelType } from '../../../enums/ModelType';
|
||||
import { UserRoles } from '../../../enums/Roles';
|
||||
import { purchaseOrderLineItemFields } from '../../../forms/PurchaseOrderForms';
|
||||
import { openCreateApiForm, openEditApiForm } from '../../../functions/forms';
|
||||
import { getDetailUrl } from '../../../functions/urls';
|
||||
import {
|
||||
useCreateApiFormModal,
|
||||
useDeleteApiFormModal,
|
||||
useEditApiFormModal
|
||||
} from '../../../hooks/UseForm';
|
||||
import { useTable } from '../../../hooks/UseTable';
|
||||
import { apiUrl } from '../../../states/ApiState';
|
||||
import { useUserState } from '../../../states/UserState';
|
||||
@ -47,52 +51,6 @@ export function PurchaseOrderLineItemTable({
|
||||
const navigate = useNavigate();
|
||||
const user = useUserState();
|
||||
|
||||
const rowActions = useCallback(
|
||||
(record: any) => {
|
||||
let received = (record?.received ?? 0) >= (record?.quantity ?? 0);
|
||||
|
||||
return [
|
||||
{
|
||||
hidden: received,
|
||||
title: t`Receive line item`,
|
||||
icon: <IconSquareArrowRight />,
|
||||
color: 'green'
|
||||
},
|
||||
RowEditAction({
|
||||
hidden: !user.hasAddRole(UserRoles.purchase_order),
|
||||
onClick: () => {
|
||||
let supplier = record?.supplier_part_detail?.supplier;
|
||||
|
||||
if (!supplier) {
|
||||
return;
|
||||
}
|
||||
|
||||
let fields = purchaseOrderLineItemFields({
|
||||
supplierId: supplier,
|
||||
create: false
|
||||
});
|
||||
|
||||
openEditApiForm({
|
||||
url: ApiEndpoints.purchase_order_line_list,
|
||||
pk: record.pk,
|
||||
title: t`Edit Line Item`,
|
||||
fields: fields,
|
||||
onFormSuccess: table.refreshTable,
|
||||
successMessage: t`Line item updated`
|
||||
});
|
||||
}
|
||||
}),
|
||||
RowDuplicateAction({
|
||||
hidden: !user.hasAddRole(UserRoles.purchase_order)
|
||||
}),
|
||||
RowDeleteAction({
|
||||
hidden: !user.hasDeleteRole(UserRoles.purchase_order)
|
||||
})
|
||||
];
|
||||
},
|
||||
[orderId, user]
|
||||
);
|
||||
|
||||
const tableColumns = useMemo(() => {
|
||||
return [
|
||||
{
|
||||
@ -220,18 +178,67 @@ export function PurchaseOrderLineItemTable({
|
||||
];
|
||||
}, [orderId, user]);
|
||||
|
||||
const addLine = useCallback(() => {
|
||||
openCreateApiForm({
|
||||
const newLine = useCreateApiFormModal({
|
||||
url: ApiEndpoints.purchase_order_line_list,
|
||||
title: t`Add Line Item`,
|
||||
fields: purchaseOrderLineItemFields({
|
||||
create: true,
|
||||
orderId: orderId
|
||||
}),
|
||||
onFormSuccess: table.refreshTable,
|
||||
successMessage: t`Line item added`
|
||||
fields: purchaseOrderLineItemFields(),
|
||||
initialData: {
|
||||
order: orderId
|
||||
},
|
||||
onFormSuccess: table.refreshTable
|
||||
});
|
||||
}, [orderId]);
|
||||
|
||||
const [selectedLine, setSelectedLine] = useState<number | undefined>(
|
||||
undefined
|
||||
);
|
||||
|
||||
const editLine = useEditApiFormModal({
|
||||
url: ApiEndpoints.purchase_order_line_list,
|
||||
pk: selectedLine,
|
||||
title: t`Edit Line Item`,
|
||||
fields: purchaseOrderLineItemFields(),
|
||||
onFormSuccess: table.refreshTable
|
||||
});
|
||||
|
||||
const deleteLine = useDeleteApiFormModal({
|
||||
url: ApiEndpoints.purchase_order_line_list,
|
||||
pk: selectedLine,
|
||||
title: t`Delete Line Item`,
|
||||
onFormSuccess: table.refreshTable
|
||||
});
|
||||
|
||||
const rowActions = useCallback(
|
||||
(record: any) => {
|
||||
let received = (record?.received ?? 0) >= (record?.quantity ?? 0);
|
||||
|
||||
return [
|
||||
{
|
||||
hidden: received,
|
||||
title: t`Receive line item`,
|
||||
icon: <IconSquareArrowRight />,
|
||||
color: 'green'
|
||||
},
|
||||
RowEditAction({
|
||||
hidden: !user.hasChangeRole(UserRoles.purchase_order),
|
||||
onClick: () => {
|
||||
setSelectedLine(record.pk);
|
||||
editLine.open();
|
||||
}
|
||||
}),
|
||||
RowDuplicateAction({
|
||||
hidden: !user.hasAddRole(UserRoles.purchase_order)
|
||||
}),
|
||||
RowDeleteAction({
|
||||
hidden: !user.hasDeleteRole(UserRoles.purchase_order),
|
||||
onClick: () => {
|
||||
setSelectedLine(record.pk);
|
||||
deleteLine.open();
|
||||
}
|
||||
})
|
||||
];
|
||||
},
|
||||
[orderId, user]
|
||||
);
|
||||
|
||||
// Custom table actions
|
||||
const tableActions = useMemo(() => {
|
||||
@ -239,7 +246,7 @@ export function PurchaseOrderLineItemTable({
|
||||
<AddItemButton
|
||||
key="add-line-item"
|
||||
tooltip={t`Add line item`}
|
||||
onClick={addLine}
|
||||
onClick={() => newLine.open()}
|
||||
hidden={!user?.hasAddRole(UserRoles.purchase_order)}
|
||||
/>,
|
||||
<ActionButton
|
||||
@ -251,6 +258,10 @@ export function PurchaseOrderLineItemTable({
|
||||
}, [orderId, user]);
|
||||
|
||||
return (
|
||||
<>
|
||||
{newLine.modal}
|
||||
{editLine.modal}
|
||||
{deleteLine.modal}
|
||||
<InvenTreeTable
|
||||
url={apiUrl(ApiEndpoints.purchase_order_line_list)}
|
||||
tableState={table}
|
||||
@ -272,5 +283,6 @@ export function PurchaseOrderLineItemTable({
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
@ -1,12 +1,13 @@
|
||||
import { t } from '@lingui/macro';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import { useMemo } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
import { ApiEndpoints } from '../../../enums/ApiEndpoints';
|
||||
import { ModelType } from '../../../enums/ModelType';
|
||||
import { UserRoles } from '../../../enums/Roles';
|
||||
import { notYetImplemented } from '../../../functions/notifications';
|
||||
import { purchaseOrderFields } from '../../../forms/PurchaseOrderForms';
|
||||
import { getDetailUrl } from '../../../functions/urls';
|
||||
import { useCreateApiFormModal } from '../../../hooks/UseForm';
|
||||
import { useTable } from '../../../hooks/UseTable';
|
||||
import { apiUrl } from '../../../states/ApiState';
|
||||
import { useUserState } from '../../../states/UserState';
|
||||
@ -34,7 +35,13 @@ import { InvenTreeTable } from '../InvenTreeTable';
|
||||
/**
|
||||
* Display a table of purchase orders
|
||||
*/
|
||||
export function PurchaseOrderTable({ params }: { params?: any }) {
|
||||
export function PurchaseOrderTable({
|
||||
supplierId,
|
||||
supplierPartId
|
||||
}: {
|
||||
supplierId?: number;
|
||||
supplierPartId?: number;
|
||||
}) {
|
||||
const navigate = useNavigate();
|
||||
|
||||
const table = useTable('purchase-order');
|
||||
@ -56,10 +63,6 @@ export function PurchaseOrderTable({ params }: { params?: any }) {
|
||||
];
|
||||
}, []);
|
||||
|
||||
// TODO: Row actions
|
||||
|
||||
// TODO: Table actions (e.g. create new purchase order)
|
||||
|
||||
const tableColumns = useMemo(() => {
|
||||
return [
|
||||
{
|
||||
@ -100,29 +103,44 @@ export function PurchaseOrderTable({ params }: { params?: any }) {
|
||||
];
|
||||
}, []);
|
||||
|
||||
const addPurchaseOrder = useCallback(() => {
|
||||
notYetImplemented();
|
||||
}, []);
|
||||
const newPurchaseOrder = useCreateApiFormModal({
|
||||
url: ApiEndpoints.purchase_order_list,
|
||||
title: t`Add Purchase Order`,
|
||||
fields: purchaseOrderFields(),
|
||||
initialData: {
|
||||
supplier: supplierId
|
||||
},
|
||||
onFormSuccess: (response) => {
|
||||
if (response.pk) {
|
||||
navigate(getDetailUrl(ModelType.purchaseorder, response.pk));
|
||||
} else {
|
||||
table.refreshTable();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const tableActions = useMemo(() => {
|
||||
return [
|
||||
<AddItemButton
|
||||
tooltip={t`Add Purchase Order`}
|
||||
onClick={addPurchaseOrder}
|
||||
onClick={() => newPurchaseOrder.open()}
|
||||
hidden={!user.hasAddRole(UserRoles.purchase_order)}
|
||||
/>
|
||||
];
|
||||
}, [user]);
|
||||
|
||||
return (
|
||||
<>
|
||||
{newPurchaseOrder.modal}
|
||||
<InvenTreeTable
|
||||
url={apiUrl(ApiEndpoints.purchase_order_list)}
|
||||
tableState={table}
|
||||
columns={tableColumns}
|
||||
props={{
|
||||
params: {
|
||||
...params,
|
||||
supplier_detail: true
|
||||
supplier_detail: true,
|
||||
supplier: supplierId,
|
||||
supplier_part: supplierPartId
|
||||
},
|
||||
tableFilters: tableFilters,
|
||||
tableActions: tableActions,
|
||||
@ -133,5 +151,6 @@ export function PurchaseOrderTable({ params }: { params?: any }) {
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
@ -1,12 +1,13 @@
|
||||
import { t } from '@lingui/macro';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import { useMemo } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
import { ApiEndpoints } from '../../../enums/ApiEndpoints';
|
||||
import { ModelType } from '../../../enums/ModelType';
|
||||
import { UserRoles } from '../../../enums/Roles';
|
||||
import { notYetImplemented } from '../../../functions/notifications';
|
||||
import { salesOrderFields } from '../../../forms/SalesOrderForms';
|
||||
import { getDetailUrl } from '../../../functions/urls';
|
||||
import { useCreateApiFormModal } from '../../../hooks/UseForm';
|
||||
import { useTable } from '../../../hooks/UseTable';
|
||||
import { apiUrl } from '../../../states/ApiState';
|
||||
import { useUserState } from '../../../states/UserState';
|
||||
@ -31,7 +32,13 @@ import {
|
||||
} from '../Filter';
|
||||
import { InvenTreeTable } from '../InvenTreeTable';
|
||||
|
||||
export function SalesOrderTable({ params }: { params?: any }) {
|
||||
export function SalesOrderTable({
|
||||
partId,
|
||||
customerId
|
||||
}: {
|
||||
partId?: number;
|
||||
customerId?: number;
|
||||
}) {
|
||||
const table = useTable('sales-order');
|
||||
const user = useUserState();
|
||||
|
||||
@ -53,9 +60,31 @@ export function SalesOrderTable({ params }: { params?: any }) {
|
||||
];
|
||||
}, []);
|
||||
|
||||
// TODO: Row actions
|
||||
const newSalesOrder = useCreateApiFormModal({
|
||||
url: ApiEndpoints.sales_order_list,
|
||||
title: t`Add Sales Order`,
|
||||
fields: salesOrderFields(),
|
||||
initialData: {
|
||||
customer: customerId
|
||||
},
|
||||
onFormSuccess: (response) => {
|
||||
if (response.pk) {
|
||||
navigate(getDetailUrl(ModelType.salesorder, response.pk));
|
||||
} else {
|
||||
table.refreshTable();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// TODO: Table actions (e.g. create new sales order)
|
||||
const tableActions = useMemo(() => {
|
||||
return [
|
||||
<AddItemButton
|
||||
tooltip={t`Add Sales Order`}
|
||||
onClick={() => newSalesOrder.open()}
|
||||
hidden={!user.hasAddRole(UserRoles.sales_order)}
|
||||
/>
|
||||
];
|
||||
}, [user]);
|
||||
|
||||
const tableColumns = useMemo(() => {
|
||||
return [
|
||||
@ -97,29 +126,18 @@ export function SalesOrderTable({ params }: { params?: any }) {
|
||||
];
|
||||
}, []);
|
||||
|
||||
const addSalesOrder = useCallback(() => {
|
||||
notYetImplemented();
|
||||
}, []);
|
||||
|
||||
const tableActions = useMemo(() => {
|
||||
return [
|
||||
<AddItemButton
|
||||
tooltip={t`Add Sales Order`}
|
||||
onClick={addSalesOrder}
|
||||
hidden={!user.hasAddRole(UserRoles.sales_order)}
|
||||
/>
|
||||
];
|
||||
}, [user]);
|
||||
|
||||
return (
|
||||
<>
|
||||
{newSalesOrder.modal}
|
||||
<InvenTreeTable
|
||||
url={apiUrl(ApiEndpoints.sales_order_list)}
|
||||
tableState={table}
|
||||
columns={tableColumns}
|
||||
props={{
|
||||
params: {
|
||||
...params,
|
||||
customer_detail: true
|
||||
customer_detail: true,
|
||||
part: partId,
|
||||
customer: customerId
|
||||
},
|
||||
tableFilters: tableFilters,
|
||||
tableActions: tableActions,
|
||||
@ -130,5 +148,6 @@ export function SalesOrderTable({ params }: { params?: any }) {
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
@ -1,13 +1,14 @@
|
||||
import { t } from '@lingui/macro';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import { useCallback, useMemo, useState } from 'react';
|
||||
|
||||
import { ApiEndpoints } from '../../../enums/ApiEndpoints';
|
||||
import { UserRoles } from '../../../enums/Roles';
|
||||
import { customUnitsFields } from '../../../forms/CommonForms';
|
||||
import {
|
||||
openCreateApiForm,
|
||||
openDeleteApiForm,
|
||||
openEditApiForm
|
||||
} from '../../../functions/forms';
|
||||
useCreateApiFormModal,
|
||||
useDeleteApiFormModal,
|
||||
useEditApiFormModal
|
||||
} from '../../../hooks/UseForm';
|
||||
import { useTable } from '../../../hooks/UseTable';
|
||||
import { apiUrl } from '../../../states/ApiState';
|
||||
import { useUserState } from '../../../states/UserState';
|
||||
@ -47,37 +48,47 @@ export default function CustomUnitsTable() {
|
||||
];
|
||||
}, []);
|
||||
|
||||
const newUnit = useCreateApiFormModal({
|
||||
url: ApiEndpoints.custom_unit_list,
|
||||
title: t`Add Custom Unit`,
|
||||
fields: customUnitsFields(),
|
||||
onFormSuccess: table.refreshTable
|
||||
});
|
||||
|
||||
const [selectedUnit, setSelectedUnit] = useState<number | undefined>(
|
||||
undefined
|
||||
);
|
||||
|
||||
const editUnit = useEditApiFormModal({
|
||||
url: ApiEndpoints.custom_unit_list,
|
||||
pk: selectedUnit,
|
||||
title: t`Edit Custom Unit`,
|
||||
fields: customUnitsFields(),
|
||||
onFormSuccess: table.refreshTable
|
||||
});
|
||||
|
||||
const deleteUnit = useDeleteApiFormModal({
|
||||
url: ApiEndpoints.custom_unit_list,
|
||||
pk: selectedUnit,
|
||||
title: t`Delete Custom Unit`,
|
||||
onFormSuccess: table.refreshTable
|
||||
});
|
||||
|
||||
const rowActions = useCallback(
|
||||
(record: any): RowAction[] => {
|
||||
return [
|
||||
RowEditAction({
|
||||
hidden: !user.hasChangeRole(UserRoles.admin),
|
||||
onClick: () => {
|
||||
openEditApiForm({
|
||||
url: ApiEndpoints.custom_unit_list,
|
||||
pk: record.pk,
|
||||
title: t`Edit custom unit`,
|
||||
fields: {
|
||||
name: {},
|
||||
definition: {},
|
||||
symbol: {}
|
||||
},
|
||||
onFormSuccess: table.refreshTable,
|
||||
successMessage: t`Custom unit updated`
|
||||
});
|
||||
setSelectedUnit(record.pk);
|
||||
editUnit.open();
|
||||
}
|
||||
}),
|
||||
RowDeleteAction({
|
||||
hidden: !user.hasDeleteRole(UserRoles.admin),
|
||||
onClick: () => {
|
||||
openDeleteApiForm({
|
||||
url: ApiEndpoints.custom_unit_list,
|
||||
pk: record.pk,
|
||||
title: t`Delete custom unit`,
|
||||
successMessage: t`Custom unit deleted`,
|
||||
onFormSuccess: table.refreshTable,
|
||||
preFormWarning: t`Are you sure you want to remove this custom unit?`
|
||||
});
|
||||
setSelectedUnit(record.pk);
|
||||
deleteUnit.open();
|
||||
}
|
||||
})
|
||||
];
|
||||
@ -85,32 +96,25 @@ export default function CustomUnitsTable() {
|
||||
[user]
|
||||
);
|
||||
|
||||
const addCustomUnit = useCallback(() => {
|
||||
openCreateApiForm({
|
||||
url: ApiEndpoints.custom_unit_list,
|
||||
title: t`Add custom unit`,
|
||||
fields: {
|
||||
name: {},
|
||||
definition: {},
|
||||
symbol: {}
|
||||
},
|
||||
successMessage: t`Custom unit created`,
|
||||
onFormSuccess: table.refreshTable
|
||||
});
|
||||
}, []);
|
||||
|
||||
const tableActions = useMemo(() => {
|
||||
let actions = [];
|
||||
|
||||
actions.push(
|
||||
// TODO: Adjust actions based on user permissions
|
||||
<AddItemButton tooltip={t`Add custom unit`} onClick={addCustomUnit} />
|
||||
<AddItemButton
|
||||
tooltip={t`Add custom unit`}
|
||||
onClick={() => newUnit.open()}
|
||||
/>
|
||||
);
|
||||
|
||||
return actions;
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
{newUnit.modal}
|
||||
{editUnit.modal}
|
||||
{deleteUnit.modal}
|
||||
<InvenTreeTable
|
||||
url={apiUrl(ApiEndpoints.custom_unit_list)}
|
||||
tableState={table}
|
||||
@ -120,5 +124,6 @@ export default function CustomUnitsTable() {
|
||||
tableActions: tableActions
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
@ -1,10 +1,13 @@
|
||||
import { Trans, t } from '@lingui/macro';
|
||||
import { Group, LoadingOverlay, Stack, Text, Title } from '@mantine/core';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import { useCallback, useMemo, useState } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
import { ApiEndpoints } from '../../../enums/ApiEndpoints';
|
||||
import { openCreateApiForm, openDeleteApiForm } from '../../../functions/forms';
|
||||
import {
|
||||
useCreateApiFormModal,
|
||||
useDeleteApiFormModal
|
||||
} from '../../../hooks/UseForm';
|
||||
import { useInstance } from '../../../hooks/UseInstance';
|
||||
import { useTable } from '../../../hooks/UseTable';
|
||||
import { apiUrl } from '../../../states/ApiState';
|
||||
@ -109,28 +112,31 @@ export function GroupTable() {
|
||||
}),
|
||||
RowDeleteAction({
|
||||
onClick: () => {
|
||||
openDeleteApiForm({
|
||||
url: ApiEndpoints.group_list,
|
||||
pk: record.pk,
|
||||
title: t`Delete group`,
|
||||
successMessage: t`Group deleted`,
|
||||
onFormSuccess: table.refreshTable,
|
||||
preFormWarning: t`Are you sure you want to delete this group?`
|
||||
});
|
||||
setSelectedGroup(record.pk), deleteGroup.open();
|
||||
}
|
||||
})
|
||||
];
|
||||
}, []);
|
||||
|
||||
const addGroup = useCallback(() => {
|
||||
openCreateApiForm({
|
||||
const [selectedGroup, setSelectedGroup] = useState<number | undefined>(
|
||||
undefined
|
||||
);
|
||||
|
||||
const deleteGroup = useDeleteApiFormModal({
|
||||
url: ApiEndpoints.group_list,
|
||||
pk: selectedGroup,
|
||||
title: t`Delete group`,
|
||||
successMessage: t`Group deleted`,
|
||||
onFormSuccess: table.refreshTable,
|
||||
preFormWarning: t`Are you sure you want to delete this group?`
|
||||
});
|
||||
|
||||
const newGroup = useCreateApiFormModal({
|
||||
url: ApiEndpoints.group_list,
|
||||
title: t`Add group`,
|
||||
fields: { name: {} },
|
||||
onFormSuccess: table.refreshTable,
|
||||
successMessage: t`Added group`
|
||||
onFormSuccess: table.refreshTable
|
||||
});
|
||||
}, []);
|
||||
|
||||
const tableActions = useMemo(() => {
|
||||
let actions = [];
|
||||
@ -138,7 +144,7 @@ export function GroupTable() {
|
||||
actions.push(
|
||||
<AddItemButton
|
||||
key={'add-group'}
|
||||
onClick={addGroup}
|
||||
onClick={() => newGroup.open()}
|
||||
tooltip={t`Add group`}
|
||||
/>
|
||||
);
|
||||
@ -148,6 +154,8 @@ export function GroupTable() {
|
||||
|
||||
return (
|
||||
<>
|
||||
{newGroup.modal}
|
||||
{deleteGroup.modal}
|
||||
<DetailDrawer
|
||||
title={t`Edit group`}
|
||||
renderContent={(id) => {
|
||||
|
@ -1,13 +1,14 @@
|
||||
import { t } from '@lingui/macro';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import { useCallback, useMemo, useState } from 'react';
|
||||
|
||||
import { ApiEndpoints } from '../../../enums/ApiEndpoints';
|
||||
import { UserRoles } from '../../../enums/Roles';
|
||||
import { projectCodeFields } from '../../../forms/CommonForms';
|
||||
import {
|
||||
openCreateApiForm,
|
||||
openDeleteApiForm,
|
||||
openEditApiForm
|
||||
} from '../../../functions/forms';
|
||||
useCreateApiFormModal,
|
||||
useDeleteApiFormModal,
|
||||
useEditApiFormModal
|
||||
} from '../../../hooks/UseForm';
|
||||
import { useTable } from '../../../hooks/UseTable';
|
||||
import { apiUrl } from '../../../states/ApiState';
|
||||
import { useUserState } from '../../../states/UserState';
|
||||
@ -37,37 +38,47 @@ export default function ProjectCodeTable() {
|
||||
];
|
||||
}, []);
|
||||
|
||||
const newProjectCode = useCreateApiFormModal({
|
||||
url: ApiEndpoints.project_code_list,
|
||||
title: t`Add Project Code`,
|
||||
fields: projectCodeFields(),
|
||||
onFormSuccess: table.refreshTable
|
||||
});
|
||||
|
||||
const [selectedProjectCode, setSelectedProjectCode] = useState<
|
||||
number | undefined
|
||||
>(undefined);
|
||||
|
||||
const editProjectCode = useEditApiFormModal({
|
||||
url: ApiEndpoints.project_code_list,
|
||||
pk: selectedProjectCode,
|
||||
title: t`Edit Project Code`,
|
||||
fields: projectCodeFields(),
|
||||
onFormSuccess: table.refreshTable
|
||||
});
|
||||
|
||||
const deleteProjectCode = useDeleteApiFormModal({
|
||||
url: ApiEndpoints.project_code_list,
|
||||
pk: selectedProjectCode,
|
||||
title: t`Delete Project Code`,
|
||||
onFormSuccess: table.refreshTable
|
||||
});
|
||||
|
||||
const rowActions = useCallback(
|
||||
(record: any): RowAction[] => {
|
||||
return [
|
||||
RowEditAction({
|
||||
hidden: !user.hasChangeRole(UserRoles.admin),
|
||||
onClick: () => {
|
||||
openEditApiForm({
|
||||
url: ApiEndpoints.project_code_list,
|
||||
pk: record.pk,
|
||||
title: t`Edit project code`,
|
||||
fields: {
|
||||
code: {},
|
||||
description: {},
|
||||
responsible: {}
|
||||
},
|
||||
onFormSuccess: table.refreshTable,
|
||||
successMessage: t`Project code updated`
|
||||
});
|
||||
setSelectedProjectCode(record.pk);
|
||||
editProjectCode.open();
|
||||
}
|
||||
}),
|
||||
RowDeleteAction({
|
||||
hidden: !user.hasDeleteRole(UserRoles.admin),
|
||||
onClick: () => {
|
||||
openDeleteApiForm({
|
||||
url: ApiEndpoints.project_code_list,
|
||||
pk: record.pk,
|
||||
title: t`Delete project code`,
|
||||
successMessage: t`Project code deleted`,
|
||||
onFormSuccess: table.refreshTable,
|
||||
preFormWarning: t`Are you sure you want to remove this project code?`
|
||||
});
|
||||
setSelectedProjectCode(record.pk);
|
||||
deleteProjectCode.open();
|
||||
}
|
||||
})
|
||||
];
|
||||
@ -75,31 +86,24 @@ export default function ProjectCodeTable() {
|
||||
[user]
|
||||
);
|
||||
|
||||
const addProjectCode = useCallback(() => {
|
||||
openCreateApiForm({
|
||||
url: ApiEndpoints.project_code_list,
|
||||
title: t`Add project code`,
|
||||
fields: {
|
||||
code: {},
|
||||
description: {},
|
||||
responsible: {}
|
||||
},
|
||||
onFormSuccess: table.refreshTable,
|
||||
successMessage: t`Added project code`
|
||||
});
|
||||
}, []);
|
||||
|
||||
const tableActions = useMemo(() => {
|
||||
let actions = [];
|
||||
|
||||
actions.push(
|
||||
<AddItemButton onClick={addProjectCode} tooltip={t`Add project code`} />
|
||||
<AddItemButton
|
||||
onClick={() => newProjectCode.open()}
|
||||
tooltip={t`Add project code`}
|
||||
/>
|
||||
);
|
||||
|
||||
return actions;
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
{newProjectCode.modal}
|
||||
{editProjectCode.modal}
|
||||
{deleteProjectCode.modal}
|
||||
<InvenTreeTable
|
||||
url={apiUrl(ApiEndpoints.project_code_list)}
|
||||
tableState={table}
|
||||
@ -109,5 +113,6 @@ export default function ProjectCodeTable() {
|
||||
tableActions: tableActions
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
@ -1,13 +1,16 @@
|
||||
import { t } from '@lingui/macro';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import { useCallback, useMemo, useState } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
import { ApiEndpoints } from '../../../enums/ApiEndpoints';
|
||||
import { ModelType } from '../../../enums/ModelType';
|
||||
import { UserRoles } from '../../../enums/Roles';
|
||||
import { stockLocationFields } from '../../../forms/StockForms';
|
||||
import { openCreateApiForm, openEditApiForm } from '../../../functions/forms';
|
||||
import { getDetailUrl } from '../../../functions/urls';
|
||||
import {
|
||||
useCreateApiFormModal,
|
||||
useEditApiFormModal
|
||||
} from '../../../hooks/UseForm';
|
||||
import { useTable } from '../../../hooks/UseTable';
|
||||
import { apiUrl } from '../../../states/ApiState';
|
||||
import { useUserState } from '../../../states/UserState';
|
||||
@ -96,26 +99,33 @@ export function StockLocationTable({ parentId }: { parentId?: any }) {
|
||||
];
|
||||
}, []);
|
||||
|
||||
const addLocation = useCallback(() => {
|
||||
let fields = stockLocationFields({});
|
||||
|
||||
if (parentId) {
|
||||
fields['parent'].value = parentId;
|
||||
}
|
||||
|
||||
openCreateApiForm({
|
||||
url: apiUrl(ApiEndpoints.stock_location_list),
|
||||
const newLocation = useCreateApiFormModal({
|
||||
url: ApiEndpoints.stock_location_list,
|
||||
title: t`Add Stock Location`,
|
||||
fields: fields,
|
||||
fields: stockLocationFields({}),
|
||||
initialData: {
|
||||
parent: parentId
|
||||
},
|
||||
onFormSuccess(data: any) {
|
||||
if (data.pk) {
|
||||
navigate(`/stock/location/${data.pk}`);
|
||||
navigate(getDetailUrl(ModelType.stocklocation, data.pk));
|
||||
} else {
|
||||
table.refreshTable();
|
||||
}
|
||||
}
|
||||
});
|
||||
}, [parentId]);
|
||||
|
||||
const [selectedLocation, setSelectedLocation] = useState<number | undefined>(
|
||||
undefined
|
||||
);
|
||||
|
||||
const editLocation = useEditApiFormModal({
|
||||
url: ApiEndpoints.stock_location_list,
|
||||
pk: selectedLocation,
|
||||
title: t`Edit Stock Location`,
|
||||
fields: stockLocationFields({}),
|
||||
onFormSuccess: table.refreshTable
|
||||
});
|
||||
|
||||
const tableActions = useMemo(() => {
|
||||
let can_add = user.hasAddRole(UserRoles.stock_location);
|
||||
@ -123,7 +133,7 @@ export function StockLocationTable({ parentId }: { parentId?: any }) {
|
||||
return [
|
||||
<AddItemButton
|
||||
tooltip={t`Add Stock Location`}
|
||||
onClick={addLocation}
|
||||
onClick={() => newLocation.open()}
|
||||
disabled={!can_add}
|
||||
/>
|
||||
];
|
||||
@ -137,14 +147,8 @@ export function StockLocationTable({ parentId }: { parentId?: any }) {
|
||||
RowEditAction({
|
||||
hidden: !can_edit,
|
||||
onClick: () => {
|
||||
openEditApiForm({
|
||||
url: ApiEndpoints.stock_location_list,
|
||||
pk: record.pk,
|
||||
title: t`Edit Stock Location`,
|
||||
fields: stockLocationFields({}),
|
||||
successMessage: t`Stock location updated`,
|
||||
onFormSuccess: table.refreshTable
|
||||
});
|
||||
setSelectedLocation(record.pk);
|
||||
editLocation.open();
|
||||
}
|
||||
})
|
||||
];
|
||||
@ -153,6 +157,9 @@ export function StockLocationTable({ parentId }: { parentId?: any }) {
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
{newLocation.modal}
|
||||
{editLocation.modal}
|
||||
<InvenTreeTable
|
||||
url={apiUrl(ApiEndpoints.stock_location_list)}
|
||||
tableState={table}
|
||||
@ -168,8 +175,8 @@ export function StockLocationTable({ parentId }: { parentId?: any }) {
|
||||
onRowClick: (record) => {
|
||||
navigate(getDetailUrl(ModelType.stocklocation, record.pk));
|
||||
}
|
||||
// TODO: allow for "tree view" with cascade
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
21
src/frontend/src/forms/CommonForms.tsx
Normal file
21
src/frontend/src/forms/CommonForms.tsx
Normal file
@ -0,0 +1,21 @@
|
||||
import { IconUsers } from '@tabler/icons-react';
|
||||
|
||||
import { ApiFormFieldSet } from '../components/forms/fields/ApiFormField';
|
||||
|
||||
export function projectCodeFields(): ApiFormFieldSet {
|
||||
return {
|
||||
code: {},
|
||||
description: {},
|
||||
responsible: {
|
||||
icon: <IconUsers />
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export function customUnitsFields(): ApiFormFieldSet {
|
||||
return {
|
||||
name: {},
|
||||
definition: {},
|
||||
symbol: {}
|
||||
};
|
||||
}
|
@ -1,4 +1,3 @@
|
||||
import { t } from '@lingui/macro';
|
||||
import {
|
||||
IconAt,
|
||||
IconCurrencyDollar,
|
||||
@ -12,8 +11,6 @@ import {
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
|
||||
import { ApiFormFieldSet } from '../components/forms/fields/ApiFormField';
|
||||
import { ApiEndpoints } from '../enums/ApiEndpoints';
|
||||
import { openEditApiForm } from '../functions/forms';
|
||||
|
||||
/**
|
||||
* Field set for SupplierPart instance
|
||||
@ -131,54 +128,3 @@ export function companyFields(): ApiFormFieldSet {
|
||||
is_customer: {}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Edit a company instance
|
||||
*/
|
||||
export function editCompany({
|
||||
pk,
|
||||
callback
|
||||
}: {
|
||||
pk: number;
|
||||
callback?: () => void;
|
||||
}) {
|
||||
openEditApiForm({
|
||||
title: t`Edit Company`,
|
||||
url: ApiEndpoints.company_list,
|
||||
pk: pk,
|
||||
fields: companyFields(),
|
||||
successMessage: t`Company updated`,
|
||||
onFormSuccess: callback
|
||||
});
|
||||
}
|
||||
|
||||
export function contactFields(): ApiFormFieldSet {
|
||||
return {
|
||||
company: {
|
||||
hidden: true
|
||||
},
|
||||
name: {},
|
||||
phone: {},
|
||||
email: {},
|
||||
role: {}
|
||||
};
|
||||
}
|
||||
|
||||
export function addressFields(): ApiFormFieldSet {
|
||||
return {
|
||||
company: {
|
||||
hidden: true
|
||||
},
|
||||
title: {},
|
||||
primary: {},
|
||||
line1: {},
|
||||
line2: {},
|
||||
postal_code: {},
|
||||
postal_city: {},
|
||||
province: {},
|
||||
country: {},
|
||||
shipping_notes: {},
|
||||
internal_shipping_notes: {},
|
||||
link: {}
|
||||
};
|
||||
}
|
||||
|
@ -2,8 +2,6 @@ import { t } from '@lingui/macro';
|
||||
import { IconPackages } from '@tabler/icons-react';
|
||||
|
||||
import { ApiFormFieldSet } from '../components/forms/fields/ApiFormField';
|
||||
import { ApiEndpoints } from '../enums/ApiEndpoints';
|
||||
import { openCreateApiForm, openEditApiForm } from '../functions/forms';
|
||||
|
||||
/**
|
||||
* Construct a set of fields for creating / editing a Part instance
|
||||
@ -98,39 +96,6 @@ export function partFields({
|
||||
return fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* Launch a dialog to create a new Part instance
|
||||
*/
|
||||
export function createPart() {
|
||||
openCreateApiForm({
|
||||
title: t`Create Part`,
|
||||
url: ApiEndpoints.part_list,
|
||||
successMessage: t`Part created`,
|
||||
fields: partFields({})
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Launch a dialog to edit an existing Part instance
|
||||
* @param part The ID of the part to edit
|
||||
*/
|
||||
export function editPart({
|
||||
part_id,
|
||||
callback
|
||||
}: {
|
||||
part_id: number;
|
||||
callback?: () => void;
|
||||
}) {
|
||||
openEditApiForm({
|
||||
title: t`Edit Part`,
|
||||
url: ApiEndpoints.part_list,
|
||||
pk: part_id,
|
||||
fields: partFields({ editing: true }),
|
||||
successMessage: t`Part updated`,
|
||||
onFormSuccess: callback
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a set of fields for creating / editing a PartCategory instance
|
||||
*/
|
||||
@ -154,26 +119,3 @@ export function partCategoryFields({}: {}): ApiFormFieldSet {
|
||||
|
||||
return fields;
|
||||
}
|
||||
|
||||
export function partParameterTemplateFields(): ApiFormFieldSet {
|
||||
return {
|
||||
name: {},
|
||||
description: {},
|
||||
units: {},
|
||||
choices: {},
|
||||
checkbox: {}
|
||||
};
|
||||
}
|
||||
|
||||
export function partTestTemplateFields(): ApiFormFieldSet {
|
||||
return {
|
||||
part: {
|
||||
hidden: true
|
||||
},
|
||||
test_name: {},
|
||||
description: {},
|
||||
required: {},
|
||||
requires_value: {},
|
||||
requires_attachment: {}
|
||||
};
|
||||
}
|
||||
|
@ -1,46 +1,42 @@
|
||||
import {
|
||||
IconAddressBook,
|
||||
IconCalendar,
|
||||
IconCoins,
|
||||
IconCurrencyDollar,
|
||||
IconHash,
|
||||
IconLink,
|
||||
IconList,
|
||||
IconNotes,
|
||||
IconSitemap
|
||||
IconSitemap,
|
||||
IconUser,
|
||||
IconUsers
|
||||
} from '@tabler/icons-react';
|
||||
|
||||
import { ApiFormFieldSet } from '../components/forms/fields/ApiFormField';
|
||||
import {
|
||||
ApiFormAdjustFilterType,
|
||||
ApiFormFieldSet
|
||||
} from '../components/forms/fields/ApiFormField';
|
||||
|
||||
/*
|
||||
* Construct a set of fields for creating / editing a PurchaseOrderLineItem instance
|
||||
*/
|
||||
export function purchaseOrderLineItemFields({
|
||||
supplierId,
|
||||
orderId,
|
||||
create = false
|
||||
}: {
|
||||
supplierId?: number;
|
||||
orderId?: number;
|
||||
create?: boolean;
|
||||
}) {
|
||||
export function purchaseOrderLineItemFields() {
|
||||
let fields: ApiFormFieldSet = {
|
||||
order: {
|
||||
filters: {
|
||||
supplier_detail: true
|
||||
},
|
||||
value: orderId,
|
||||
hidden: create != true || orderId != undefined
|
||||
hidden: true
|
||||
},
|
||||
part: {
|
||||
filters: {
|
||||
part_detail: true,
|
||||
supplier_detail: true,
|
||||
supplier: supplierId
|
||||
supplier_detail: true
|
||||
},
|
||||
adjustFilters: (filters: any) => {
|
||||
// TODO: Filter by the supplier associated with the order
|
||||
return filters;
|
||||
adjustFilters: (value: ApiFormAdjustFilterType) => {
|
||||
// TODO: Adjust part based on the supplier associated with the supplier
|
||||
return value.filters;
|
||||
}
|
||||
// TODO: Custom onEdit callback (see purchase_order.js)
|
||||
// TODO: secondary modal (see purchase_order.js)
|
||||
},
|
||||
quantity: {},
|
||||
reference: {},
|
||||
@ -66,3 +62,52 @@ export function purchaseOrderLineItemFields({
|
||||
|
||||
return fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a set of fields for creating / editing a PurchaseOrder instance
|
||||
*/
|
||||
export function purchaseOrderFields(): ApiFormFieldSet {
|
||||
return {
|
||||
reference: {
|
||||
icon: <IconHash />
|
||||
},
|
||||
description: {},
|
||||
supplier: {
|
||||
filters: {
|
||||
is_supplier: true
|
||||
}
|
||||
},
|
||||
supplier_reference: {},
|
||||
project_code: {
|
||||
icon: <IconList />
|
||||
},
|
||||
order_currency: {
|
||||
icon: <IconCoins />
|
||||
},
|
||||
target_date: {
|
||||
icon: <IconCalendar />
|
||||
},
|
||||
link: {},
|
||||
contact: {
|
||||
icon: <IconUser />,
|
||||
adjustFilters: (value: ApiFormAdjustFilterType) => {
|
||||
return {
|
||||
...value.filters,
|
||||
company: value.data.supplier
|
||||
};
|
||||
}
|
||||
},
|
||||
address: {
|
||||
icon: <IconAddressBook />,
|
||||
adjustFilters: (value: ApiFormAdjustFilterType) => {
|
||||
return {
|
||||
...value.filters,
|
||||
company: value.data.supplier
|
||||
};
|
||||
}
|
||||
},
|
||||
responsible: {
|
||||
icon: <IconUsers />
|
||||
}
|
||||
};
|
||||
}
|
||||
|
44
src/frontend/src/forms/SalesOrderForms.tsx
Normal file
44
src/frontend/src/forms/SalesOrderForms.tsx
Normal file
@ -0,0 +1,44 @@
|
||||
import { IconAddressBook, IconUser, IconUsers } from '@tabler/icons-react';
|
||||
|
||||
import {
|
||||
ApiFormAdjustFilterType,
|
||||
ApiFormFieldSet
|
||||
} from '../components/forms/fields/ApiFormField';
|
||||
|
||||
export function salesOrderFields(): ApiFormFieldSet {
|
||||
return {
|
||||
reference: {},
|
||||
description: {},
|
||||
customer: {
|
||||
filters: {
|
||||
is_customer: true
|
||||
}
|
||||
},
|
||||
customer_reference: {},
|
||||
project_code: {},
|
||||
order_currency: {},
|
||||
target_date: {},
|
||||
link: {},
|
||||
contact: {
|
||||
icon: <IconUser />,
|
||||
adjustFilters: (value: ApiFormAdjustFilterType) => {
|
||||
return {
|
||||
...value.filters,
|
||||
company: value.data.customer
|
||||
};
|
||||
}
|
||||
},
|
||||
address: {
|
||||
icon: <IconAddressBook />,
|
||||
adjustFilters: (value: ApiFormAdjustFilterType) => {
|
||||
return {
|
||||
...value.filters,
|
||||
company: value.data.customer
|
||||
};
|
||||
}
|
||||
},
|
||||
responsible: {
|
||||
icon: <IconUsers />
|
||||
}
|
||||
};
|
||||
}
|
@ -107,7 +107,7 @@ export function useCreateStockItem() {
|
||||
return useCreateApiFormModal({
|
||||
url: ApiEndpoints.stock_item_list,
|
||||
fields: fields,
|
||||
title: t`Create Stock Item`
|
||||
title: t`Add Stock Item`
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { t } from '@lingui/macro';
|
||||
import { Divider, Stack } from '@mantine/core';
|
||||
import { Alert, Divider, Stack } from '@mantine/core';
|
||||
import { useId } from '@mantine/hooks';
|
||||
import { useEffect, useMemo, useRef } from 'react';
|
||||
|
||||
@ -82,6 +82,8 @@ export function useCreateApiFormModal(props: ApiFormModalProps) {
|
||||
const createProps = useMemo<ApiFormModalProps>(
|
||||
() => ({
|
||||
...props,
|
||||
fetchInitialData: props.fetchInitialData ?? false,
|
||||
successMessage: props.successMessage ?? t`Item Created`,
|
||||
method: 'POST'
|
||||
}),
|
||||
[props]
|
||||
@ -98,7 +100,8 @@ export function useEditApiFormModal(props: ApiFormModalProps) {
|
||||
() => ({
|
||||
...props,
|
||||
fetchInitialData: props.fetchInitialData ?? true,
|
||||
method: 'PUT'
|
||||
successMessage: props.successMessage ?? t`Item Updated`,
|
||||
method: 'PATCH'
|
||||
}),
|
||||
[props]
|
||||
);
|
||||
@ -116,6 +119,12 @@ export function useDeleteApiFormModal(props: ApiFormModalProps) {
|
||||
method: 'DELETE',
|
||||
submitText: t`Delete`,
|
||||
submitColor: 'red',
|
||||
successMessage: props.successMessage ?? t`Item Deleted`,
|
||||
preFormContent: props.preFormContent ?? (
|
||||
<Alert
|
||||
color={'red'}
|
||||
>{t`Are you sure you want to delete this item?`}</Alert>
|
||||
),
|
||||
fields: {}
|
||||
}),
|
||||
[props]
|
||||
|
@ -42,6 +42,14 @@ window.INVENTREE_SETTINGS = {
|
||||
host: `${window.location.origin}/`,
|
||||
name: 'Current Server'
|
||||
},
|
||||
...(IS_DEV
|
||||
? {
|
||||
'mantine-2j5j5j5j5': {
|
||||
host: 'http://localhost:8000',
|
||||
name: 'Localhost'
|
||||
}
|
||||
}
|
||||
: {}),
|
||||
...(IS_DEV_OR_DEMO
|
||||
? {
|
||||
'mantine-u56l5jt85': {
|
||||
@ -51,7 +59,11 @@ window.INVENTREE_SETTINGS = {
|
||||
}
|
||||
: {})
|
||||
},
|
||||
default_server: IS_DEMO ? 'mantine-u56l5jt85' : 'mantine-cqj63coxn',
|
||||
default_server: IS_DEV
|
||||
? 'mantine-2j5j5j5j5'
|
||||
: IS_DEMO
|
||||
? 'mantine-u56l5jt85'
|
||||
: 'mantine-cqj63coxn',
|
||||
show_server_selector: IS_DEV_OR_DEMO,
|
||||
|
||||
// merge in settings that are already set via django's spa_view or for development
|
||||
|
@ -10,42 +10,54 @@ import { StylishText } from '../../components/items/StylishText';
|
||||
import { StatusRenderer } from '../../components/render/StatusRenderer';
|
||||
import { ApiEndpoints } from '../../enums/ApiEndpoints';
|
||||
import { ModelType } from '../../enums/ModelType';
|
||||
import {
|
||||
createPart,
|
||||
editPart,
|
||||
partCategoryFields,
|
||||
partFields
|
||||
} from '../../forms/PartForms';
|
||||
import { partCategoryFields, partFields } from '../../forms/PartForms';
|
||||
import { useCreateStockItem } from '../../forms/StockForms';
|
||||
import {
|
||||
OpenApiFormProps,
|
||||
openCreateApiForm,
|
||||
openEditApiForm
|
||||
} from '../../functions/forms';
|
||||
import { useCreateApiFormModal } from '../../hooks/UseForm';
|
||||
useCreateApiFormModal,
|
||||
useEditApiFormModal
|
||||
} from '../../hooks/UseForm';
|
||||
|
||||
// Generate some example forms using the modal API forms interface
|
||||
const fields = partCategoryFields({});
|
||||
|
||||
function ApiFormsPlayground() {
|
||||
const editCategoryForm: OpenApiFormProps = {
|
||||
const editCategory = useEditApiFormModal({
|
||||
url: ApiEndpoints.category_list,
|
||||
pk: 2,
|
||||
title: 'Edit Category',
|
||||
fields: fields
|
||||
};
|
||||
});
|
||||
|
||||
const createAttachmentForm: OpenApiFormProps = {
|
||||
const newPart = useCreateApiFormModal({
|
||||
url: ApiEndpoints.part_list,
|
||||
title: 'Create Part',
|
||||
fields: partFields({}),
|
||||
initialData: {
|
||||
description: 'A part created via the API'
|
||||
}
|
||||
});
|
||||
|
||||
const editPart = useEditApiFormModal({
|
||||
url: ApiEndpoints.part_list,
|
||||
pk: 1,
|
||||
title: 'Edit Part',
|
||||
fields: partFields({ editing: true })
|
||||
});
|
||||
|
||||
const newAttachment = useCreateApiFormModal({
|
||||
url: ApiEndpoints.part_attachment_list,
|
||||
title: 'Create Attachment',
|
||||
successMessage: 'Attachment uploaded',
|
||||
fields: {
|
||||
part: {
|
||||
value: 1
|
||||
},
|
||||
part: {},
|
||||
attachment: {},
|
||||
comment: {}
|
||||
}
|
||||
};
|
||||
},
|
||||
initialData: {
|
||||
part: 1
|
||||
},
|
||||
successMessage: 'Attachment uploaded'
|
||||
});
|
||||
|
||||
const [active, setActive] = useState(true);
|
||||
const [name, setName] = useState('Hello');
|
||||
|
||||
@ -73,6 +85,14 @@ function ApiFormsPlayground() {
|
||||
url: ApiEndpoints.part_list,
|
||||
title: 'Create part',
|
||||
fields: partFieldsState,
|
||||
initialData: {
|
||||
is_template: true,
|
||||
virtual: true,
|
||||
minimum_stock: 10,
|
||||
description: 'An example part description',
|
||||
keywords: 'apple, banana, carrottt',
|
||||
'initial_supplier.sku': 'SKU-123'
|
||||
},
|
||||
preFormContent: (
|
||||
<Button onClick={() => setName('Hello world')}>
|
||||
Set name="Hello world"
|
||||
@ -86,19 +106,20 @@ function ApiFormsPlayground() {
|
||||
return (
|
||||
<Stack>
|
||||
<Group>
|
||||
<Button onClick={() => createPart()}>Create New Part</Button>
|
||||
<Button onClick={() => editPart({ part_id: 1 })}>Edit Part</Button>
|
||||
<Button onClick={() => newPart.open()}>Create New Part</Button>
|
||||
{newPart.modal}
|
||||
|
||||
<Button onClick={() => editPart.open()}>Edit Part</Button>
|
||||
{editPart.modal}
|
||||
|
||||
<Button onClick={() => openCreateStockItem()}>Create Stock Item</Button>
|
||||
{createStockItemModal}
|
||||
|
||||
<Button onClick={() => openEditApiForm(editCategoryForm)}>
|
||||
Edit Category
|
||||
</Button>
|
||||
<Button onClick={() => editCategory.open()}>Edit Category</Button>
|
||||
{editCategory.modal}
|
||||
|
||||
<Button onClick={() => openCreateApiForm(createAttachmentForm)}>
|
||||
Create Attachment
|
||||
</Button>
|
||||
<Button onClick={() => newAttachment.open()}>Create Attachment</Button>
|
||||
{newAttachment.modal}
|
||||
|
||||
<Button onClick={() => openCreatePart()}>Create Part new Modal</Button>
|
||||
{createPartModal}
|
||||
|
@ -14,12 +14,11 @@ import {
|
||||
IconQrcode,
|
||||
IconSitemap
|
||||
} from '@tabler/icons-react';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import { useMemo } from 'react';
|
||||
import { useParams } from 'react-router-dom';
|
||||
|
||||
import {
|
||||
ActionDropdown,
|
||||
DeleteItemAction,
|
||||
DuplicateItemAction,
|
||||
EditItemAction,
|
||||
LinkBarcodeAction,
|
||||
@ -36,8 +35,9 @@ import { StockItemTable } from '../../components/tables/stock/StockItemTable';
|
||||
import { NotesEditor } from '../../components/widgets/MarkdownEditor';
|
||||
import { ApiEndpoints } from '../../enums/ApiEndpoints';
|
||||
import { ModelType } from '../../enums/ModelType';
|
||||
import { UserRoles } from '../../enums/Roles';
|
||||
import { buildOrderFields } from '../../forms/BuildForms';
|
||||
import { openEditApiForm } from '../../functions/forms';
|
||||
import { useEditApiFormModal } from '../../hooks/UseForm';
|
||||
import { useInstance } from '../../hooks/UseInstance';
|
||||
import { apiUrl } from '../../states/ApiState';
|
||||
import { useUserState } from '../../states/UserState';
|
||||
@ -152,12 +152,10 @@ export default function BuildDetail() {
|
||||
name: 'child-orders',
|
||||
label: t`Child Build Orders`,
|
||||
icon: <IconSitemap />,
|
||||
content: (
|
||||
<BuildOrderTable
|
||||
params={{
|
||||
parent: id
|
||||
}}
|
||||
/>
|
||||
content: build.pk ? (
|
||||
<BuildOrderTable parentBuildId={build.pk} />
|
||||
) : (
|
||||
<Skeleton />
|
||||
)
|
||||
},
|
||||
{
|
||||
@ -187,24 +185,15 @@ export default function BuildDetail() {
|
||||
];
|
||||
}, [build, id]);
|
||||
|
||||
const editBuildOrder = useCallback(() => {
|
||||
let fields = buildOrderFields();
|
||||
|
||||
// Cannot edit part field after creation
|
||||
fields['part'].hidden = true;
|
||||
|
||||
build.pk &&
|
||||
openEditApiForm({
|
||||
const editBuild = useEditApiFormModal({
|
||||
url: ApiEndpoints.build_order_list,
|
||||
pk: build.pk,
|
||||
title: t`Edit Build Order`,
|
||||
fields: fields,
|
||||
successMessage: t`Build Order updated`,
|
||||
fields: buildOrderFields(),
|
||||
onFormSuccess: () => {
|
||||
refreshInstance();
|
||||
}
|
||||
});
|
||||
}, [build]);
|
||||
|
||||
const buildActions = useMemo(() => {
|
||||
// TODO: Disable certain actions based on user permissions
|
||||
@ -241,10 +230,10 @@ export default function BuildDetail() {
|
||||
icon={<IconDots />}
|
||||
actions={[
|
||||
EditItemAction({
|
||||
onClick: editBuildOrder
|
||||
onClick: () => editBuild.open(),
|
||||
disabled: !user.hasChangeRole(UserRoles.build)
|
||||
}),
|
||||
DuplicateItemAction({}),
|
||||
DeleteItemAction({})
|
||||
DuplicateItemAction({})
|
||||
]}
|
||||
/>
|
||||
];
|
||||
@ -263,6 +252,7 @@ export default function BuildDetail() {
|
||||
|
||||
return (
|
||||
<>
|
||||
{editBuild.modal}
|
||||
<Stack spacing="xs">
|
||||
<LoadingOverlay visible={instanceQuery.isFetching} />
|
||||
<PageDetail
|
||||
|
@ -1,45 +1,17 @@
|
||||
import { t } from '@lingui/macro';
|
||||
import { Button, Stack } from '@mantine/core';
|
||||
import { useCallback } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { Stack } from '@mantine/core';
|
||||
|
||||
import { PageDetail } from '../../components/nav/PageDetail';
|
||||
import { BuildOrderTable } from '../../components/tables/build/BuildOrderTable';
|
||||
import { ApiEndpoints } from '../../enums/ApiEndpoints';
|
||||
import { buildOrderFields } from '../../forms/BuildForms';
|
||||
import { openCreateApiForm } from '../../functions/forms';
|
||||
|
||||
/**
|
||||
* Build Order index page
|
||||
*/
|
||||
export default function BuildIndex() {
|
||||
const navigate = useNavigate();
|
||||
|
||||
const newBuildOrder = useCallback(() => {
|
||||
openCreateApiForm({
|
||||
url: ApiEndpoints.build_order_list,
|
||||
title: t`Add Build Order`,
|
||||
fields: buildOrderFields(),
|
||||
successMessage: t`Build order created`,
|
||||
onFormSuccess: (data: any) => {
|
||||
if (data.pk) {
|
||||
navigate(`/build/${data.pk}`);
|
||||
}
|
||||
}
|
||||
});
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Stack>
|
||||
<PageDetail
|
||||
title={t`Build Orders`}
|
||||
actions={[
|
||||
<Button key="new-build" color="green" onClick={newBuildOrder}>
|
||||
{t`New Build Order`}
|
||||
</Button>
|
||||
]}
|
||||
/>
|
||||
<PageDetail title={t`Build Orders`} actions={[]} />
|
||||
<BuildOrderTable />
|
||||
</Stack>
|
||||
</>
|
||||
|
@ -39,7 +39,8 @@ import { StockItemTable } from '../../components/tables/stock/StockItemTable';
|
||||
import { NotesEditor } from '../../components/widgets/MarkdownEditor';
|
||||
import { ApiEndpoints } from '../../enums/ApiEndpoints';
|
||||
import { UserRoles } from '../../enums/Roles';
|
||||
import { editCompany } from '../../forms/CompanyForms';
|
||||
import { companyFields } from '../../forms/CompanyForms';
|
||||
import { useEditApiFormModal } from '../../hooks/UseForm';
|
||||
import { useInstance } from '../../hooks/UseInstance';
|
||||
import { apiUrl } from '../../states/ApiState';
|
||||
import { useUserState } from '../../states/UserState';
|
||||
@ -98,9 +99,7 @@ export default function CompanyDetail(props: CompanyDetailProps) {
|
||||
label: t`Purchase Orders`,
|
||||
icon: <IconShoppingCart />,
|
||||
hidden: !company?.is_supplier,
|
||||
content: company?.pk && (
|
||||
<PurchaseOrderTable params={{ supplier: company.pk }} />
|
||||
)
|
||||
content: company?.pk && <PurchaseOrderTable supplierId={company.pk} />
|
||||
},
|
||||
{
|
||||
name: 'stock-items',
|
||||
@ -116,9 +115,7 @@ export default function CompanyDetail(props: CompanyDetailProps) {
|
||||
label: t`Sales Orders`,
|
||||
icon: <IconTruckDelivery />,
|
||||
hidden: !company?.is_customer,
|
||||
content: company?.pk && (
|
||||
<SalesOrderTable params={{ customer: company.pk }} />
|
||||
)
|
||||
content: company?.pk && <SalesOrderTable customerId={company.pk} />
|
||||
},
|
||||
{
|
||||
name: 'return-orders',
|
||||
@ -179,6 +176,14 @@ export default function CompanyDetail(props: CompanyDetailProps) {
|
||||
];
|
||||
}, [id, company]);
|
||||
|
||||
const editCompany = useEditApiFormModal({
|
||||
url: ApiEndpoints.company_list,
|
||||
pk: company?.pk,
|
||||
title: t`Edit Company`,
|
||||
fields: companyFields(),
|
||||
onFormSuccess: refreshInstance
|
||||
});
|
||||
|
||||
const companyActions = useMemo(() => {
|
||||
return [
|
||||
<ActionDropdown
|
||||
@ -188,14 +193,7 @@ export default function CompanyDetail(props: CompanyDetailProps) {
|
||||
actions={[
|
||||
EditItemAction({
|
||||
disabled: !user.hasChangeRole(UserRoles.purchase_order),
|
||||
onClick: () => {
|
||||
if (company?.pk) {
|
||||
editCompany({
|
||||
pk: company?.pk,
|
||||
callback: refreshInstance
|
||||
});
|
||||
}
|
||||
}
|
||||
onClick: () => editCompany.open()
|
||||
}),
|
||||
DeleteItemAction({
|
||||
disabled: !user.hasDeleteRole(UserRoles.purchase_order)
|
||||
@ -206,6 +204,8 @@ export default function CompanyDetail(props: CompanyDetailProps) {
|
||||
}, [id, company, user]);
|
||||
|
||||
return (
|
||||
<>
|
||||
{editCompany.modal}
|
||||
<Stack spacing="xs">
|
||||
<LoadingOverlay visible={instanceQuery.isFetching} />
|
||||
<PageDetail
|
||||
@ -217,5 +217,6 @@ export default function CompanyDetail(props: CompanyDetailProps) {
|
||||
/>
|
||||
<PanelGroup pageKey="company" panels={companyPanels} />
|
||||
</Stack>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
@ -45,7 +45,7 @@ export default function SupplierPartDetail() {
|
||||
label: t`Purchase Orders`,
|
||||
icon: <IconShoppingCart />,
|
||||
content: supplierPart?.pk ? (
|
||||
<PurchaseOrderTable params={{ supplier_part: supplierPart.pk }} />
|
||||
<PurchaseOrderTable supplierPartId={supplierPart.pk} />
|
||||
) : (
|
||||
<Skeleton />
|
||||
)
|
||||
|
@ -63,7 +63,8 @@ import { NotesEditor } from '../../components/widgets/MarkdownEditor';
|
||||
import { formatPriceRange } from '../../defaults/formatters';
|
||||
import { ApiEndpoints } from '../../enums/ApiEndpoints';
|
||||
import { UserRoles } from '../../enums/Roles';
|
||||
import { editPart } from '../../forms/PartForms';
|
||||
import { partFields } from '../../forms/PartForms';
|
||||
import { useEditApiFormModal } from '../../hooks/UseForm';
|
||||
import { useInstance } from '../../hooks/UseInstance';
|
||||
import { apiUrl } from '../../states/ApiState';
|
||||
import { useUserState } from '../../states/UserState';
|
||||
@ -504,14 +505,7 @@ export default function PartDetail() {
|
||||
label: t`Build Orders`,
|
||||
icon: <IconTools />,
|
||||
hidden: !part.assembly,
|
||||
content: (
|
||||
<BuildOrderTable
|
||||
params={{
|
||||
part_detail: true,
|
||||
part: part.pk ?? -1
|
||||
}}
|
||||
/>
|
||||
)
|
||||
content: part?.pk ? <BuildOrderTable partId={part.pk} /> : <Skeleton />
|
||||
},
|
||||
{
|
||||
name: 'used_in',
|
||||
@ -562,15 +556,7 @@ export default function PartDetail() {
|
||||
label: t`Sales Orders`,
|
||||
icon: <IconTruckDelivery />,
|
||||
hidden: !part.salable,
|
||||
content: part.pk ? (
|
||||
<SalesOrderTable
|
||||
params={{
|
||||
part: part.pk ?? -1
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<Skeleton />
|
||||
)
|
||||
content: part.pk ? <SalesOrderTable partId={part.pk} /> : <Skeleton />
|
||||
},
|
||||
{
|
||||
name: 'scheduling',
|
||||
@ -647,6 +633,14 @@ export default function PartDetail() {
|
||||
);
|
||||
}, [part, id]);
|
||||
|
||||
const editPart = useEditApiFormModal({
|
||||
url: ApiEndpoints.part_list,
|
||||
pk: part.pk,
|
||||
title: t`Edit Part`,
|
||||
fields: partFields({ editing: true }),
|
||||
onFormSuccess: refreshInstance
|
||||
});
|
||||
|
||||
const partActions = useMemo(() => {
|
||||
// TODO: Disable actions based on user permissions
|
||||
return [
|
||||
@ -685,13 +679,8 @@ export default function PartDetail() {
|
||||
actions={[
|
||||
DuplicateItemAction({}),
|
||||
EditItemAction({
|
||||
onClick: () => {
|
||||
part.pk &&
|
||||
editPart({
|
||||
part_id: part.pk,
|
||||
callback: refreshInstance
|
||||
});
|
||||
}
|
||||
disabled: !user.hasChangeRole(UserRoles.part),
|
||||
onClick: () => editPart.open()
|
||||
}),
|
||||
DeleteItemAction({
|
||||
disabled: part?.active
|
||||
@ -703,6 +692,7 @@ export default function PartDetail() {
|
||||
|
||||
return (
|
||||
<>
|
||||
{editPart.modal}
|
||||
<Stack spacing="xs">
|
||||
<LoadingOverlay visible={instanceQuery.isFetching} />
|
||||
<PartCategoryTree
|
||||
|
@ -20,7 +20,6 @@ export default function PurchasingIndex() {
|
||||
label: t`Purchase Orders`,
|
||||
icon: <IconShoppingCart />,
|
||||
content: <PurchaseOrderTable />
|
||||
// TODO: Add optional "calendar" display here...
|
||||
},
|
||||
{
|
||||
name: 'suppliers',
|
||||
|
@ -62,11 +62,7 @@ export default function SalesOrderDetail() {
|
||||
label: t`Build Orders`,
|
||||
icon: <IconTools />,
|
||||
content: order?.pk ? (
|
||||
<BuildOrderTable
|
||||
params={{
|
||||
sales_order: order.pk
|
||||
}}
|
||||
/>
|
||||
<BuildOrderTable salesOrderId={order.pk} />
|
||||
) : (
|
||||
<Skeleton />
|
||||
)
|
||||
|
Loading…
Reference in New Issue
Block a user