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.
|
# User is authenticated, and requesting a token against the provided name.
|
||||||
token = ApiToken.objects.create(user=request.user, name=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
|
# Add some metadata about the request
|
||||||
token.set_metadata('user_agent', request.META.get('HTTP_USER_AGENT', ''))
|
token.set_metadata('user_agent', request.META.get('HTTP_USER_AGENT', ''))
|
||||||
token.set_metadata('remote_addr', request.META.get('REMOTE_ADDR', ''))
|
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}
|
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)
|
# Ensure that the users session is logged in (PUI -> CUI login)
|
||||||
if not get_user(request).is_authenticated:
|
if not get_user(request).is_authenticated:
|
||||||
login(request, user)
|
login(request, user)
|
||||||
|
@ -14,6 +14,7 @@ import { useCallback, useEffect, useMemo } from 'react';
|
|||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import {
|
import {
|
||||||
FieldValues,
|
FieldValues,
|
||||||
|
FormProvider,
|
||||||
SubmitErrorHandler,
|
SubmitErrorHandler,
|
||||||
SubmitHandler,
|
SubmitHandler,
|
||||||
useForm
|
useForm
|
||||||
@ -65,6 +66,7 @@ export interface ApiFormProps {
|
|||||||
pathParams?: PathParams;
|
pathParams?: PathParams;
|
||||||
method?: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
|
method?: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
|
||||||
fields?: ApiFormFieldSet;
|
fields?: ApiFormFieldSet;
|
||||||
|
initialData?: FieldValues;
|
||||||
submitText?: string;
|
submitText?: string;
|
||||||
submitColor?: string;
|
submitColor?: string;
|
||||||
fetchInitialData?: boolean;
|
fetchInitialData?: boolean;
|
||||||
@ -146,6 +148,13 @@ export function OptionsApiForm({
|
|||||||
field: v,
|
field: v,
|
||||||
definition: data?.[k]
|
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;
|
return _props;
|
||||||
@ -163,13 +172,23 @@ export function OptionsApiForm({
|
|||||||
* based on an API endpoint.
|
* based on an API endpoint.
|
||||||
*/
|
*/
|
||||||
export function ApiForm({ id, props }: { id: string; props: ApiFormProps }) {
|
export function ApiForm({ id, props }: { id: string; props: ApiFormProps }) {
|
||||||
const defaultValues: FieldValues = useMemo(
|
const defaultValues: FieldValues = useMemo(() => {
|
||||||
() =>
|
let defaultValuesMap = mapFields(props.fields ?? {}, (_path, field) => {
|
||||||
mapFields(props.fields ?? {}, (_path, field) => {
|
return field.value ?? field.default ?? undefined;
|
||||||
return field.default ?? undefined;
|
});
|
||||||
}),
|
|
||||||
[props.fields]
|
// 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
|
// Form errors which are not associated with a specific field
|
||||||
const [nonFieldErrors, setNonFieldErrors] = useState<string[]>([]);
|
const [nonFieldErrors, setNonFieldErrors] = useState<string[]>([]);
|
||||||
@ -179,6 +198,7 @@ export function ApiForm({ id, props }: { id: string; props: ApiFormProps }) {
|
|||||||
criteriaMode: 'all',
|
criteriaMode: 'all',
|
||||||
defaultValues
|
defaultValues
|
||||||
});
|
});
|
||||||
|
|
||||||
const {
|
const {
|
||||||
isValid,
|
isValid,
|
||||||
isDirty,
|
isDirty,
|
||||||
@ -390,16 +410,18 @@ export function ApiForm({ id, props }: { id: string; props: ApiFormProps }) {
|
|||||||
{props.preFormWarning}
|
{props.preFormWarning}
|
||||||
</Alert>
|
</Alert>
|
||||||
)}
|
)}
|
||||||
<Stack spacing="xs">
|
<FormProvider {...form}>
|
||||||
{Object.entries(props.fields ?? {}).map(([fieldName, field]) => (
|
<Stack spacing="xs">
|
||||||
<ApiFormField
|
{Object.entries(props.fields ?? {}).map(([fieldName, field]) => (
|
||||||
key={fieldName}
|
<ApiFormField
|
||||||
fieldName={fieldName}
|
key={fieldName}
|
||||||
definition={field}
|
fieldName={fieldName}
|
||||||
control={form.control}
|
definition={field}
|
||||||
/>
|
control={form.control}
|
||||||
))}
|
/>
|
||||||
</Stack>
|
))}
|
||||||
|
</Stack>
|
||||||
|
</FormProvider>
|
||||||
{props.postFormContent}
|
{props.postFormContent}
|
||||||
</Stack>
|
</Stack>
|
||||||
<Divider />
|
<Divider />
|
||||||
|
@ -7,7 +7,6 @@ import {
|
|||||||
Switch,
|
Switch,
|
||||||
TextInput
|
TextInput
|
||||||
} from '@mantine/core';
|
} from '@mantine/core';
|
||||||
import { DateInput } from '@mantine/dates';
|
|
||||||
import { UseFormReturnType } from '@mantine/form';
|
import { UseFormReturnType } from '@mantine/form';
|
||||||
import { useId } from '@mantine/hooks';
|
import { useId } from '@mantine/hooks';
|
||||||
import { IconX } from '@tabler/icons-react';
|
import { IconX } from '@tabler/icons-react';
|
||||||
@ -17,11 +16,17 @@ import { Control, FieldValues, useController } from 'react-hook-form';
|
|||||||
|
|
||||||
import { ModelType } from '../../../enums/ModelType';
|
import { ModelType } from '../../../enums/ModelType';
|
||||||
import { ChoiceField } from './ChoiceField';
|
import { ChoiceField } from './ChoiceField';
|
||||||
|
import DateField from './DateField';
|
||||||
import { NestedObjectField } from './NestedObjectField';
|
import { NestedObjectField } from './NestedObjectField';
|
||||||
import { RelatedModelField } from './RelatedModelField';
|
import { RelatedModelField } from './RelatedModelField';
|
||||||
|
|
||||||
export type ApiFormData = UseFormReturnType<Record<string, unknown>>;
|
export type ApiFormData = UseFormReturnType<Record<string, unknown>>;
|
||||||
|
|
||||||
|
export type ApiFormAdjustFilterType = {
|
||||||
|
filters: any;
|
||||||
|
data: FieldValues;
|
||||||
|
};
|
||||||
|
|
||||||
/** Definition of the ApiForm field component.
|
/** Definition of the ApiForm field component.
|
||||||
* - The 'name' attribute *must* be provided
|
* - The 'name' attribute *must* be provided
|
||||||
* - All other attributes are optional, and may be provided by the API
|
* - All other attributes are optional, and may be provided by the API
|
||||||
@ -80,7 +85,7 @@ export type ApiFormFieldType = {
|
|||||||
preFieldContent?: JSX.Element;
|
preFieldContent?: JSX.Element;
|
||||||
postFieldContent?: JSX.Element;
|
postFieldContent?: JSX.Element;
|
||||||
onValueChange?: (value: any) => void;
|
onValueChange?: (value: any) => void;
|
||||||
adjustFilters?: (filters: any) => any;
|
adjustFilters?: (value: ApiFormAdjustFilterType) => any;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -207,20 +212,7 @@ export function ApiFormField({
|
|||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
case 'date':
|
case 'date':
|
||||||
return (
|
return <DateField controller={controller} definition={definition} />;
|
||||||
<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"
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
case 'integer':
|
case 'integer':
|
||||||
case 'decimal':
|
case 'decimal':
|
||||||
case 'float':
|
case 'float':
|
||||||
|
@ -58,6 +58,10 @@ export function ChoiceField({
|
|||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
data={choices}
|
data={choices}
|
||||||
value={field.value}
|
value={field.value}
|
||||||
|
label={definition.label}
|
||||||
|
description={definition.description}
|
||||||
|
placeholder={definition.placeholder}
|
||||||
|
icon={definition.icon}
|
||||||
withinPortal={true}
|
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 { useId } from '@mantine/hooks';
|
||||||
import { useQuery } from '@tanstack/react-query';
|
import { useQuery } from '@tanstack/react-query';
|
||||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
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 Select from 'react-select';
|
||||||
|
|
||||||
import { api } from '../../../App';
|
import { api } from '../../../App';
|
||||||
@ -32,6 +36,8 @@ export function RelatedModelField({
|
|||||||
fieldState: { error }
|
fieldState: { error }
|
||||||
} = controller;
|
} = controller;
|
||||||
|
|
||||||
|
const form = useFormContext();
|
||||||
|
|
||||||
// Keep track of the primary key value for this field
|
// Keep track of the primary key value for this field
|
||||||
const [pk, setPk] = useState<number | null>(null);
|
const [pk, setPk] = useState<number | null>(null);
|
||||||
|
|
||||||
@ -40,6 +46,8 @@ export function RelatedModelField({
|
|||||||
const [data, setData] = useState<any[]>([]);
|
const [data, setData] = useState<any[]>([]);
|
||||||
const dataRef = useRef<any[]>([]);
|
const dataRef = useRef<any[]>([]);
|
||||||
|
|
||||||
|
const [isOpen, setIsOpen] = useState<boolean>(false);
|
||||||
|
|
||||||
// If an initial value is provided, load from the API
|
// If an initial value is provided, load from the API
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// If the value is unchanged, do nothing
|
// If the value is unchanged, do nothing
|
||||||
@ -71,28 +79,49 @@ export function RelatedModelField({
|
|||||||
const [value, setValue] = useState<string>('');
|
const [value, setValue] = useState<string>('');
|
||||||
const [searchText, cancelSearchText] = useDebouncedValue(value, 250);
|
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
|
// reset current data on search value change
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
dataRef.current = [];
|
resetSearch();
|
||||||
setData([]);
|
}, [searchText, filters]);
|
||||||
}, [searchText]);
|
|
||||||
|
|
||||||
const selectQuery = useQuery({
|
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],
|
queryKey: [`related-field-${fieldName}`, fieldId, offset, searchText],
|
||||||
queryFn: async () => {
|
queryFn: async () => {
|
||||||
if (!definition.api_url) {
|
if (!definition.api_url) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
let filters = definition.filters ?? {};
|
let _filters = definition.filters ?? {};
|
||||||
|
|
||||||
if (definition.adjustFilters) {
|
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 = {
|
let params = {
|
||||||
...filters,
|
..._filters,
|
||||||
search: searchText,
|
search: searchText,
|
||||||
offset: offset,
|
offset: offset,
|
||||||
limit: limit
|
limit: limit
|
||||||
@ -189,16 +218,19 @@ export function RelatedModelField({
|
|||||||
filterOption={null}
|
filterOption={null}
|
||||||
onInputChange={(value: any) => {
|
onInputChange={(value: any) => {
|
||||||
setValue(value);
|
setValue(value);
|
||||||
setOffset(0);
|
resetSearch();
|
||||||
setData([]);
|
|
||||||
}}
|
}}
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
onMenuScrollToBottom={() => setOffset(offset + limit)}
|
onMenuScrollToBottom={() => setOffset(offset + limit)}
|
||||||
onMenuOpen={() => {
|
onMenuOpen={() => {
|
||||||
|
setIsOpen(true);
|
||||||
setValue('');
|
setValue('');
|
||||||
setOffset(0);
|
resetSearch();
|
||||||
selectQuery.refetch();
|
selectQuery.refetch();
|
||||||
}}
|
}}
|
||||||
|
onMenuClose={() => {
|
||||||
|
setIsOpen(false);
|
||||||
|
}}
|
||||||
isLoading={
|
isLoading={
|
||||||
selectQuery.isFetching ||
|
selectQuery.isFetching ||
|
||||||
selectQuery.isLoading ||
|
selectQuery.isLoading ||
|
||||||
|
@ -7,7 +7,6 @@ import { RenderInlineModel } from './Instance';
|
|||||||
*/
|
*/
|
||||||
export function RenderAddress({ instance }: { instance: any }): ReactNode {
|
export function RenderAddress({ instance }: { instance: any }): ReactNode {
|
||||||
let text = [
|
let text = [
|
||||||
instance.title,
|
|
||||||
instance.country,
|
instance.country,
|
||||||
instance.postal_code,
|
instance.postal_code,
|
||||||
instance.postal_city,
|
instance.postal_city,
|
||||||
@ -18,12 +17,7 @@ export function RenderAddress({ instance }: { instance: any }): ReactNode {
|
|||||||
.filter(Boolean)
|
.filter(Boolean)
|
||||||
.join(', ');
|
.join(', ');
|
||||||
|
|
||||||
return (
|
return <RenderInlineModel primary={instance.title} secondary={text} />;
|
||||||
<RenderInlineModel
|
|
||||||
primary={instance.description}
|
|
||||||
secondary={instance.address}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -5,9 +5,14 @@ import { useNavigate } from 'react-router-dom';
|
|||||||
import { renderDate } from '../../../defaults/formatters';
|
import { renderDate } from '../../../defaults/formatters';
|
||||||
import { ApiEndpoints } from '../../../enums/ApiEndpoints';
|
import { ApiEndpoints } from '../../../enums/ApiEndpoints';
|
||||||
import { ModelType } from '../../../enums/ModelType';
|
import { ModelType } from '../../../enums/ModelType';
|
||||||
|
import { UserRoles } from '../../../enums/Roles';
|
||||||
|
import { buildOrderFields } from '../../../forms/BuildForms';
|
||||||
import { getDetailUrl } from '../../../functions/urls';
|
import { getDetailUrl } from '../../../functions/urls';
|
||||||
|
import { useCreateApiFormModal } from '../../../hooks/UseForm';
|
||||||
import { useTable } from '../../../hooks/UseTable';
|
import { useTable } from '../../../hooks/UseTable';
|
||||||
import { apiUrl } from '../../../states/ApiState';
|
import { apiUrl } from '../../../states/ApiState';
|
||||||
|
import { useUserState } from '../../../states/UserState';
|
||||||
|
import { AddItemButton } from '../../buttons/AddItemButton';
|
||||||
import { PartHoverCard } from '../../images/Thumbnail';
|
import { PartHoverCard } from '../../images/Thumbnail';
|
||||||
import { ProgressBar } from '../../items/ProgressBar';
|
import { ProgressBar } from '../../items/ProgressBar';
|
||||||
import { RenderUser } from '../../render/User';
|
import { RenderUser } from '../../render/User';
|
||||||
@ -88,7 +93,15 @@ function buildOrderTableColumns(): TableColumn[] {
|
|||||||
/*
|
/*
|
||||||
* Construct a table of build orders, according to the provided parameters
|
* 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 tableColumns = useMemo(() => buildOrderTableColumns(), []);
|
||||||
|
|
||||||
const tableFilters: TableFilter[] = useMemo(() => {
|
const tableFilters: TableFilter[] = useMemo(() => {
|
||||||
@ -130,23 +143,56 @@ export function BuildOrderTable({ params = {} }: { params?: any }) {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
const user = useUserState();
|
||||||
|
|
||||||
const table = useTable('buildorder');
|
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 (
|
return (
|
||||||
<InvenTreeTable
|
<>
|
||||||
url={apiUrl(ApiEndpoints.build_order_list)}
|
{newBuild.modal}
|
||||||
tableState={table}
|
<InvenTreeTable
|
||||||
columns={tableColumns}
|
url={apiUrl(ApiEndpoints.build_order_list)}
|
||||||
props={{
|
tableState={table}
|
||||||
enableDownload: true,
|
columns={tableColumns}
|
||||||
params: {
|
props={{
|
||||||
...params,
|
enableDownload: true,
|
||||||
part_detail: true
|
params: {
|
||||||
},
|
part: partId,
|
||||||
tableFilters: tableFilters,
|
sales_order: salesOrderId,
|
||||||
onRowClick: (row) => navigate(getDetailUrl(ModelType.build, row.pk))
|
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 { t } from '@lingui/macro';
|
||||||
import { useCallback, useMemo } from 'react';
|
import { useCallback, useMemo, useState } from 'react';
|
||||||
|
|
||||||
import { ApiEndpoints } from '../../../enums/ApiEndpoints';
|
import { ApiEndpoints } from '../../../enums/ApiEndpoints';
|
||||||
import { UserRoles } from '../../../enums/Roles';
|
import { UserRoles } from '../../../enums/Roles';
|
||||||
import { addressFields } from '../../../forms/CompanyForms';
|
|
||||||
import {
|
import {
|
||||||
openCreateApiForm,
|
useCreateApiFormModal,
|
||||||
openDeleteApiForm,
|
useDeleteApiFormModal,
|
||||||
openEditApiForm
|
useEditApiFormModal
|
||||||
} from '../../../functions/forms';
|
} from '../../../hooks/UseForm';
|
||||||
import { useTable } from '../../../hooks/UseTable';
|
import { useTable } from '../../../hooks/UseTable';
|
||||||
import { apiUrl } from '../../../states/ApiState';
|
import { apiUrl } from '../../../states/ApiState';
|
||||||
import { useUserState } from '../../../states/UserState';
|
import { useUserState } from '../../../states/UserState';
|
||||||
import { AddItemButton } from '../../buttons/AddItemButton';
|
import { AddItemButton } from '../../buttons/AddItemButton';
|
||||||
|
import { ApiFormFieldSet } from '../../forms/fields/ApiFormField';
|
||||||
import { YesNoButton } from '../../items/YesNoButton';
|
import { YesNoButton } from '../../items/YesNoButton';
|
||||||
import { TableColumn } from '../Column';
|
import { TableColumn } from '../Column';
|
||||||
import { InvenTreeTable } from '../InvenTreeTable';
|
import { InvenTreeTable } from '../InvenTreeTable';
|
||||||
@ -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(
|
const rowActions = useCallback(
|
||||||
(record: any) => {
|
(record: any) => {
|
||||||
let can_edit =
|
let can_edit =
|
||||||
@ -122,27 +170,15 @@ export function AddressTable({
|
|||||||
RowEditAction({
|
RowEditAction({
|
||||||
hidden: !can_edit,
|
hidden: !can_edit,
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
openEditApiForm({
|
setSelectedAddress(record.pk);
|
||||||
url: ApiEndpoints.address_list,
|
editAddress.open();
|
||||||
pk: record.pk,
|
|
||||||
title: t`Edit Address`,
|
|
||||||
fields: addressFields(),
|
|
||||||
successMessage: t`Address updated`,
|
|
||||||
onFormSuccess: table.refreshTable
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
RowDeleteAction({
|
RowDeleteAction({
|
||||||
hidden: !can_delete,
|
hidden: !can_delete,
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
openDeleteApiForm({
|
setSelectedAddress(record.pk);
|
||||||
url: ApiEndpoints.address_list,
|
deleteAddress.open();
|
||||||
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?`
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
];
|
];
|
||||||
@ -150,20 +186,6 @@ export function AddressTable({
|
|||||||
[user]
|
[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(() => {
|
const tableActions = useMemo(() => {
|
||||||
let can_add =
|
let can_add =
|
||||||
user.hasChangeRole(UserRoles.purchase_order) ||
|
user.hasChangeRole(UserRoles.purchase_order) ||
|
||||||
@ -172,25 +194,30 @@ export function AddressTable({
|
|||||||
return [
|
return [
|
||||||
<AddItemButton
|
<AddItemButton
|
||||||
tooltip={t`Add Address`}
|
tooltip={t`Add Address`}
|
||||||
onClick={addAddress}
|
onClick={() => newAddress.open()}
|
||||||
disabled={!can_add}
|
disabled={!can_add}
|
||||||
/>
|
/>
|
||||||
];
|
];
|
||||||
}, [user]);
|
}, [user]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<InvenTreeTable
|
<>
|
||||||
url={apiUrl(ApiEndpoints.address_list)}
|
{newAddress.modal}
|
||||||
tableState={table}
|
{editAddress.modal}
|
||||||
columns={columns}
|
{deleteAddress.modal}
|
||||||
props={{
|
<InvenTreeTable
|
||||||
rowActions: rowActions,
|
url={apiUrl(ApiEndpoints.address_list)}
|
||||||
tableActions: tableActions,
|
tableState={table}
|
||||||
params: {
|
columns={columns}
|
||||||
...params,
|
props={{
|
||||||
company: companyId
|
rowActions: rowActions,
|
||||||
}
|
tableActions: tableActions,
|
||||||
}}
|
params: {
|
||||||
/>
|
...params,
|
||||||
|
company: companyId
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -4,8 +4,13 @@ import { useMemo } from 'react';
|
|||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
|
||||||
import { ApiEndpoints } from '../../../enums/ApiEndpoints';
|
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 { useTable } from '../../../hooks/UseTable';
|
||||||
import { apiUrl } from '../../../states/ApiState';
|
import { apiUrl } from '../../../states/ApiState';
|
||||||
|
import { useUserState } from '../../../states/UserState';
|
||||||
|
import { AddItemButton } from '../../buttons/AddItemButton';
|
||||||
import { Thumbnail } from '../../images/Thumbnail';
|
import { Thumbnail } from '../../images/Thumbnail';
|
||||||
import { DescriptionColumn } from '../ColumnRenderers';
|
import { DescriptionColumn } from '../ColumnRenderers';
|
||||||
import { InvenTreeTable } from '../InvenTreeTable';
|
import { InvenTreeTable } from '../InvenTreeTable';
|
||||||
@ -24,6 +29,7 @@ export function CompanyTable({
|
|||||||
const table = useTable('company');
|
const table = useTable('company');
|
||||||
|
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
const user = useUserState();
|
||||||
|
|
||||||
const columns = useMemo(() => {
|
const columns = useMemo(() => {
|
||||||
return [
|
return [
|
||||||
@ -53,22 +59,56 @@ 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 (
|
return (
|
||||||
<InvenTreeTable
|
<>
|
||||||
url={apiUrl(ApiEndpoints.company_list)}
|
{newCompany.modal}
|
||||||
tableState={table}
|
<InvenTreeTable
|
||||||
columns={columns}
|
url={apiUrl(ApiEndpoints.company_list)}
|
||||||
props={{
|
tableState={table}
|
||||||
params: {
|
columns={columns}
|
||||||
...params
|
props={{
|
||||||
},
|
params: {
|
||||||
onRowClick: (row: any) => {
|
...params
|
||||||
if (row.pk) {
|
},
|
||||||
let base = path ?? 'company';
|
tableActions: tableActions,
|
||||||
navigate(`/${base}/${row.pk}`);
|
onRowClick: (row: any) => {
|
||||||
|
if (row.pk) {
|
||||||
|
let base = path ?? 'company';
|
||||||
|
navigate(`/${base}/${row.pk}`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}}
|
||||||
}}
|
/>
|
||||||
/>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,18 +1,18 @@
|
|||||||
import { t } from '@lingui/macro';
|
import { t } from '@lingui/macro';
|
||||||
import { useCallback, useMemo } from 'react';
|
import { useCallback, useMemo, useState } from 'react';
|
||||||
|
|
||||||
import { ApiEndpoints } from '../../../enums/ApiEndpoints';
|
import { ApiEndpoints } from '../../../enums/ApiEndpoints';
|
||||||
import { UserRoles } from '../../../enums/Roles';
|
import { UserRoles } from '../../../enums/Roles';
|
||||||
import { contactFields } from '../../../forms/CompanyForms';
|
|
||||||
import {
|
import {
|
||||||
openCreateApiForm,
|
useCreateApiFormModal,
|
||||||
openDeleteApiForm,
|
useDeleteApiFormModal,
|
||||||
openEditApiForm
|
useEditApiFormModal
|
||||||
} from '../../../functions/forms';
|
} from '../../../hooks/UseForm';
|
||||||
import { useTable } from '../../../hooks/UseTable';
|
import { useTable } from '../../../hooks/UseTable';
|
||||||
import { apiUrl } from '../../../states/ApiState';
|
import { apiUrl } from '../../../states/ApiState';
|
||||||
import { useUserState } from '../../../states/UserState';
|
import { useUserState } from '../../../states/UserState';
|
||||||
import { AddItemButton } from '../../buttons/AddItemButton';
|
import { AddItemButton } from '../../buttons/AddItemButton';
|
||||||
|
import { ApiFormFieldSet } from '../../forms/fields/ApiFormField';
|
||||||
import { TableColumn } from '../Column';
|
import { TableColumn } from '../Column';
|
||||||
import { InvenTreeTable } from '../InvenTreeTable';
|
import { InvenTreeTable } from '../InvenTreeTable';
|
||||||
import { RowDeleteAction, RowEditAction } from '../RowActions';
|
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(
|
const rowActions = useCallback(
|
||||||
(record: any) => {
|
(record: any) => {
|
||||||
let can_edit =
|
let can_edit =
|
||||||
@ -70,27 +109,15 @@ export function ContactTable({
|
|||||||
RowEditAction({
|
RowEditAction({
|
||||||
hidden: !can_edit,
|
hidden: !can_edit,
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
openEditApiForm({
|
setSelectedContact(record.pk);
|
||||||
url: ApiEndpoints.contact_list,
|
editContact.open();
|
||||||
pk: record.pk,
|
|
||||||
title: t`Edit Contact`,
|
|
||||||
fields: contactFields(),
|
|
||||||
successMessage: t`Contact updated`,
|
|
||||||
onFormSuccess: table.refreshTable
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
RowDeleteAction({
|
RowDeleteAction({
|
||||||
hidden: !can_delete,
|
hidden: !can_delete,
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
openDeleteApiForm({
|
setSelectedContact(record.pk);
|
||||||
url: ApiEndpoints.contact_list,
|
deleteContact.open();
|
||||||
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?`
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
];
|
];
|
||||||
@ -98,20 +125,6 @@ export function ContactTable({
|
|||||||
[user]
|
[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(() => {
|
const tableActions = useMemo(() => {
|
||||||
let can_add =
|
let can_add =
|
||||||
user.hasAddRole(UserRoles.purchase_order) ||
|
user.hasAddRole(UserRoles.purchase_order) ||
|
||||||
@ -120,25 +133,30 @@ export function ContactTable({
|
|||||||
return [
|
return [
|
||||||
<AddItemButton
|
<AddItemButton
|
||||||
tooltip={t`Add contact`}
|
tooltip={t`Add contact`}
|
||||||
onClick={addContact}
|
onClick={() => newContact.open()}
|
||||||
disabled={!can_add}
|
disabled={!can_add}
|
||||||
/>
|
/>
|
||||||
];
|
];
|
||||||
}, [user]);
|
}, [user]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<InvenTreeTable
|
<>
|
||||||
url={apiUrl(ApiEndpoints.contact_list)}
|
{newContact.modal}
|
||||||
tableState={table}
|
{editContact.modal}
|
||||||
columns={columns}
|
{deleteContact.modal}
|
||||||
props={{
|
<InvenTreeTable
|
||||||
rowActions: rowActions,
|
url={apiUrl(ApiEndpoints.contact_list)}
|
||||||
tableActions: tableActions,
|
tableState={table}
|
||||||
params: {
|
columns={columns}
|
||||||
...params,
|
props={{
|
||||||
company: companyId
|
rowActions: rowActions,
|
||||||
}
|
tableActions: tableActions,
|
||||||
}}
|
params: {
|
||||||
/>
|
...params,
|
||||||
|
company: companyId
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,16 @@
|
|||||||
import { t } from '@lingui/macro';
|
import { t } from '@lingui/macro';
|
||||||
import { useCallback, useMemo } from 'react';
|
import { useCallback, useMemo, useState } from 'react';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
|
||||||
import { ApiEndpoints } from '../../../enums/ApiEndpoints';
|
import { ApiEndpoints } from '../../../enums/ApiEndpoints';
|
||||||
import { ModelType } from '../../../enums/ModelType';
|
import { ModelType } from '../../../enums/ModelType';
|
||||||
import { UserRoles } from '../../../enums/Roles';
|
import { UserRoles } from '../../../enums/Roles';
|
||||||
import { partCategoryFields } from '../../../forms/PartForms';
|
import { partCategoryFields } from '../../../forms/PartForms';
|
||||||
import { openCreateApiForm, openEditApiForm } from '../../../functions/forms';
|
|
||||||
import { getDetailUrl } from '../../../functions/urls';
|
import { getDetailUrl } from '../../../functions/urls';
|
||||||
|
import {
|
||||||
|
useCreateApiFormModal,
|
||||||
|
useEditApiFormModal
|
||||||
|
} from '../../../hooks/UseForm';
|
||||||
import { useTable } from '../../../hooks/UseTable';
|
import { useTable } from '../../../hooks/UseTable';
|
||||||
import { apiUrl } from '../../../states/ApiState';
|
import { apiUrl } from '../../../states/ApiState';
|
||||||
import { useUserState } from '../../../states/UserState';
|
import { useUserState } from '../../../states/UserState';
|
||||||
@ -73,26 +76,33 @@ export function PartCategoryTable({ parentId }: { parentId?: any }) {
|
|||||||
];
|
];
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const addCategory = useCallback(() => {
|
const newCategory = useCreateApiFormModal({
|
||||||
let fields = partCategoryFields({});
|
url: ApiEndpoints.category_list,
|
||||||
|
title: t`New Part Category`,
|
||||||
if (parentId) {
|
fields: partCategoryFields({}),
|
||||||
fields['parent'].value = parentId;
|
initialData: {
|
||||||
}
|
parent: parentId
|
||||||
|
},
|
||||||
openCreateApiForm({
|
onFormSuccess(data: any) {
|
||||||
url: apiUrl(ApiEndpoints.category_list),
|
if (data.pk) {
|
||||||
title: t`Add Part Category`,
|
navigate(getDetailUrl(ModelType.partcategory, data.pk));
|
||||||
fields: fields,
|
} else {
|
||||||
onFormSuccess(data: any) {
|
table.refreshTable();
|
||||||
if (data.pk) {
|
|
||||||
navigate(`/part/category/${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(() => {
|
const tableActions = useMemo(() => {
|
||||||
let can_add = user.hasAddRole(UserRoles.part_category);
|
let can_add = user.hasAddRole(UserRoles.part_category);
|
||||||
@ -100,7 +110,7 @@ export function PartCategoryTable({ parentId }: { parentId?: any }) {
|
|||||||
return [
|
return [
|
||||||
<AddItemButton
|
<AddItemButton
|
||||||
tooltip={t`Add Part Category`}
|
tooltip={t`Add Part Category`}
|
||||||
onClick={addCategory}
|
onClick={() => newCategory.open()}
|
||||||
disabled={!can_add}
|
disabled={!can_add}
|
||||||
/>
|
/>
|
||||||
];
|
];
|
||||||
@ -114,14 +124,8 @@ export function PartCategoryTable({ parentId }: { parentId?: any }) {
|
|||||||
RowEditAction({
|
RowEditAction({
|
||||||
hidden: !can_edit,
|
hidden: !can_edit,
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
openEditApiForm({
|
setSelectedCategory(record.pk);
|
||||||
url: ApiEndpoints.category_list,
|
editCategory.open();
|
||||||
pk: record.pk,
|
|
||||||
title: t`Edit Part Category`,
|
|
||||||
fields: partCategoryFields({}),
|
|
||||||
successMessage: t`Part category updated`,
|
|
||||||
onFormSuccess: table.refreshTable
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
];
|
];
|
||||||
@ -130,21 +134,25 @@ export function PartCategoryTable({ parentId }: { parentId?: any }) {
|
|||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<InvenTreeTable
|
<>
|
||||||
url={apiUrl(ApiEndpoints.category_list)}
|
{newCategory.modal}
|
||||||
tableState={table}
|
{editCategory.modal}
|
||||||
columns={tableColumns}
|
<InvenTreeTable
|
||||||
props={{
|
url={apiUrl(ApiEndpoints.category_list)}
|
||||||
enableDownload: true,
|
tableState={table}
|
||||||
params: {
|
columns={tableColumns}
|
||||||
parent: parentId ?? 'null'
|
props={{
|
||||||
},
|
enableDownload: true,
|
||||||
tableFilters: tableFilters,
|
params: {
|
||||||
tableActions: tableActions,
|
parent: parentId ?? 'null'
|
||||||
rowActions: rowActions,
|
},
|
||||||
onRowClick: (record, index, event) =>
|
tableFilters: tableFilters,
|
||||||
navigate(getDetailUrl(ModelType.partcategory, record.pk))
|
tableActions: tableActions,
|
||||||
}}
|
rowActions: rowActions,
|
||||||
/>
|
onRowClick: (record) =>
|
||||||
|
navigate(getDetailUrl(ModelType.partcategory, record.pk))
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,18 +1,19 @@
|
|||||||
import { t } from '@lingui/macro';
|
import { t } from '@lingui/macro';
|
||||||
import { Text } from '@mantine/core';
|
import { Text } from '@mantine/core';
|
||||||
import { useCallback, useMemo } from 'react';
|
import { useCallback, useMemo, useState } from 'react';
|
||||||
|
|
||||||
import { ApiEndpoints } from '../../../enums/ApiEndpoints';
|
import { ApiEndpoints } from '../../../enums/ApiEndpoints';
|
||||||
import { UserRoles } from '../../../enums/Roles';
|
import { UserRoles } from '../../../enums/Roles';
|
||||||
import {
|
import {
|
||||||
openCreateApiForm,
|
useCreateApiFormModal,
|
||||||
openDeleteApiForm,
|
useDeleteApiFormModal,
|
||||||
openEditApiForm
|
useEditApiFormModal
|
||||||
} from '../../../functions/forms';
|
} from '../../../hooks/UseForm';
|
||||||
import { useTable } from '../../../hooks/UseTable';
|
import { useTable } from '../../../hooks/UseTable';
|
||||||
import { apiUrl } from '../../../states/ApiState';
|
import { apiUrl } from '../../../states/ApiState';
|
||||||
import { useUserState } from '../../../states/UserState';
|
import { useUserState } from '../../../states/UserState';
|
||||||
import { AddItemButton } from '../../buttons/AddItemButton';
|
import { AddItemButton } from '../../buttons/AddItemButton';
|
||||||
|
import { ApiFormFieldSet } from '../../forms/fields/ApiFormField';
|
||||||
import { YesNoButton } from '../../items/YesNoButton';
|
import { YesNoButton } from '../../items/YesNoButton';
|
||||||
import { TableColumn } from '../Column';
|
import { TableColumn } from '../Column';
|
||||||
import { PartColumn } from '../ColumnRenderers';
|
import { PartColumn } from '../ColumnRenderers';
|
||||||
@ -36,6 +37,12 @@ export function PartParameterTable({ partId }: { partId: any }) {
|
|||||||
sortable: true,
|
sortable: true,
|
||||||
render: (record: any) => PartColumn(record?.part_detail)
|
render: (record: any) => PartColumn(record?.part_detail)
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
accessor: 'part_detail.IPN',
|
||||||
|
title: t`IPN`,
|
||||||
|
sortable: false,
|
||||||
|
switchable: true
|
||||||
|
},
|
||||||
{
|
{
|
||||||
accessor: 'name',
|
accessor: 'name',
|
||||||
title: t`Parameter`,
|
title: t`Parameter`,
|
||||||
@ -85,6 +92,43 @@ export function PartParameterTable({ partId }: { partId: any }) {
|
|||||||
];
|
];
|
||||||
}, [partId]);
|
}, [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
|
// Callback for row actions
|
||||||
const rowActions = useCallback(
|
const rowActions = useCallback(
|
||||||
(record: any) => {
|
(record: any) => {
|
||||||
@ -93,107 +137,65 @@ export function PartParameterTable({ partId }: { partId: any }) {
|
|||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
let actions = [];
|
return [
|
||||||
|
|
||||||
actions.push(
|
|
||||||
RowEditAction({
|
RowEditAction({
|
||||||
tooltip: t`Edit Part Parameter`,
|
tooltip: t`Edit Part Parameter`,
|
||||||
hidden: !user.hasChangeRole(UserRoles.part),
|
hidden: !user.hasChangeRole(UserRoles.part),
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
openEditApiForm({
|
setSelectedParameter(record.pk);
|
||||||
url: ApiEndpoints.part_parameter_list,
|
editParameter.open();
|
||||||
pk: record.pk,
|
|
||||||
title: t`Edit Part Parameter`,
|
|
||||||
fields: {
|
|
||||||
part: {
|
|
||||||
hidden: true
|
|
||||||
},
|
|
||||||
template: {},
|
|
||||||
data: {}
|
|
||||||
},
|
|
||||||
successMessage: t`Part parameter updated`,
|
|
||||||
onFormSuccess: table.refreshTable
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
})
|
}),
|
||||||
);
|
|
||||||
|
|
||||||
actions.push(
|
|
||||||
RowDeleteAction({
|
RowDeleteAction({
|
||||||
tooltip: t`Delete Part Parameter`,
|
tooltip: t`Delete Part Parameter`,
|
||||||
hidden: !user.hasDeleteRole(UserRoles.part),
|
hidden: !user.hasDeleteRole(UserRoles.part),
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
openDeleteApiForm({
|
setSelectedParameter(record.pk);
|
||||||
url: ApiEndpoints.part_parameter_list,
|
deleteParameter.open();
|
||||||
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?`
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
);
|
];
|
||||||
|
|
||||||
return actions;
|
|
||||||
},
|
},
|
||||||
[partId, user]
|
[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
|
// Custom table actions
|
||||||
const tableActions = useMemo(() => {
|
const tableActions = useMemo(() => {
|
||||||
let actions = [];
|
return [
|
||||||
|
<AddItemButton
|
||||||
// TODO: Hide if user does not have permission to edit parts
|
hidden={!user.hasAddRole(UserRoles.part)}
|
||||||
actions.push(
|
tooltip={t`Add parameter`}
|
||||||
<AddItemButton tooltip={t`Add parameter`} onClick={addParameter} />
|
onClick={() => newParameter.open()}
|
||||||
);
|
/>
|
||||||
|
];
|
||||||
return actions;
|
}, [user]);
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<InvenTreeTable
|
<>
|
||||||
url={apiUrl(ApiEndpoints.part_parameter_list)}
|
{newParameter.modal}
|
||||||
tableState={table}
|
{editParameter.modal}
|
||||||
columns={tableColumns}
|
{deleteParameter.modal}
|
||||||
props={{
|
<InvenTreeTable
|
||||||
rowActions: rowActions,
|
url={apiUrl(ApiEndpoints.part_parameter_list)}
|
||||||
tableActions: tableActions,
|
tableState={table}
|
||||||
tableFilters: [
|
columns={tableColumns}
|
||||||
{
|
props={{
|
||||||
name: 'include_variants',
|
rowActions: rowActions,
|
||||||
label: t`Include Variants`,
|
tableActions: tableActions,
|
||||||
type: 'boolean'
|
tableFilters: [
|
||||||
|
{
|
||||||
|
name: 'include_variants',
|
||||||
|
label: t`Include Variants`,
|
||||||
|
type: 'boolean'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
params: {
|
||||||
|
part: partId,
|
||||||
|
template_detail: true,
|
||||||
|
part_detail: true
|
||||||
}
|
}
|
||||||
],
|
}}
|
||||||
params: {
|
/>
|
||||||
part: partId,
|
</>
|
||||||
template_detail: true,
|
|
||||||
part_detail: true
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,18 +1,18 @@
|
|||||||
import { t } from '@lingui/macro';
|
import { t } from '@lingui/macro';
|
||||||
import { useCallback, useMemo } from 'react';
|
import { useCallback, useMemo, useState } from 'react';
|
||||||
|
|
||||||
import { ApiEndpoints } from '../../../enums/ApiEndpoints';
|
import { ApiEndpoints } from '../../../enums/ApiEndpoints';
|
||||||
import { UserRoles } from '../../../enums/Roles';
|
import { UserRoles } from '../../../enums/Roles';
|
||||||
import { partParameterTemplateFields } from '../../../forms/PartForms';
|
|
||||||
import {
|
import {
|
||||||
openCreateApiForm,
|
useCreateApiFormModal,
|
||||||
openDeleteApiForm,
|
useDeleteApiFormModal,
|
||||||
openEditApiForm
|
useEditApiFormModal
|
||||||
} from '../../../functions/forms';
|
} from '../../../hooks/UseForm';
|
||||||
import { useTable } from '../../../hooks/UseTable';
|
import { useTable } from '../../../hooks/UseTable';
|
||||||
import { apiUrl } from '../../../states/ApiState';
|
import { apiUrl } from '../../../states/ApiState';
|
||||||
import { useUserState } from '../../../states/UserState';
|
import { useUserState } from '../../../states/UserState';
|
||||||
import { AddItemButton } from '../../buttons/AddItemButton';
|
import { AddItemButton } from '../../buttons/AddItemButton';
|
||||||
|
import { ApiFormFieldSet } from '../../forms/fields/ApiFormField';
|
||||||
import { TableColumn } from '../Column';
|
import { TableColumn } from '../Column';
|
||||||
import { DescriptionColumn } from '../ColumnRenderers';
|
import { DescriptionColumn } from '../ColumnRenderers';
|
||||||
import { TableFilter } from '../Filter';
|
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
|
// Callback for row actions
|
||||||
const rowActions = useCallback(
|
const rowActions = useCallback(
|
||||||
(record: any) => {
|
(record: any) => {
|
||||||
@ -76,27 +112,15 @@ export default function PartParameterTemplateTable() {
|
|||||||
RowEditAction({
|
RowEditAction({
|
||||||
hidden: !user.hasChangeRole(UserRoles.part),
|
hidden: !user.hasChangeRole(UserRoles.part),
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
openEditApiForm({
|
setSelectedTemplate(record.pk);
|
||||||
url: ApiEndpoints.part_parameter_template_list,
|
editTemplate.open();
|
||||||
pk: record.pk,
|
|
||||||
title: t`Edit Parameter Template`,
|
|
||||||
fields: partParameterTemplateFields(),
|
|
||||||
successMessage: t`Parameter template updated`,
|
|
||||||
onFormSuccess: table.refreshTable
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
RowDeleteAction({
|
RowDeleteAction({
|
||||||
hidden: !user.hasDeleteRole(UserRoles.part),
|
hidden: !user.hasDeleteRole(UserRoles.part),
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
openDeleteApiForm({
|
setSelectedTemplate(record.pk);
|
||||||
url: ApiEndpoints.part_parameter_template_list,
|
deleteTemplate.open();
|
||||||
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?`
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
];
|
];
|
||||||
@ -104,36 +128,31 @@ export default function PartParameterTemplateTable() {
|
|||||||
[user]
|
[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(() => {
|
const tableActions = useMemo(() => {
|
||||||
return [
|
return [
|
||||||
<AddItemButton
|
<AddItemButton
|
||||||
tooltip={t`Add parameter template`}
|
tooltip={t`Add parameter template`}
|
||||||
onClick={addParameterTemplate}
|
onClick={() => newTemplate.open()}
|
||||||
disabled={!user.hasAddRole(UserRoles.part)}
|
disabled={!user.hasAddRole(UserRoles.part)}
|
||||||
/>
|
/>
|
||||||
];
|
];
|
||||||
}, [user]);
|
}, [user]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<InvenTreeTable
|
<>
|
||||||
url={apiUrl(ApiEndpoints.part_parameter_template_list)}
|
{newTemplate.modal}
|
||||||
tableState={table}
|
{editTemplate.modal}
|
||||||
columns={tableColumns}
|
{deleteTemplate.modal}
|
||||||
props={{
|
<InvenTreeTable
|
||||||
rowActions: rowActions,
|
url={apiUrl(ApiEndpoints.part_parameter_template_list)}
|
||||||
tableFilters: tableFilters,
|
tableState={table}
|
||||||
tableActions: tableActions
|
columns={tableColumns}
|
||||||
}}
|
props={{
|
||||||
/>
|
rowActions: rowActions,
|
||||||
|
tableFilters: tableFilters,
|
||||||
|
tableActions: tableActions
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,18 +1,18 @@
|
|||||||
import { t } from '@lingui/macro';
|
import { t } from '@lingui/macro';
|
||||||
import { useCallback, useMemo } from 'react';
|
import { useCallback, useMemo, useState } from 'react';
|
||||||
|
|
||||||
import { ApiEndpoints } from '../../../enums/ApiEndpoints';
|
import { ApiEndpoints } from '../../../enums/ApiEndpoints';
|
||||||
import { UserRoles } from '../../../enums/Roles';
|
import { UserRoles } from '../../../enums/Roles';
|
||||||
import { partTestTemplateFields } from '../../../forms/PartForms';
|
|
||||||
import {
|
import {
|
||||||
openCreateApiForm,
|
useCreateApiFormModal,
|
||||||
openDeleteApiForm,
|
useDeleteApiFormModal,
|
||||||
openEditApiForm
|
useEditApiFormModal
|
||||||
} from '../../../functions/forms';
|
} from '../../../hooks/UseForm';
|
||||||
import { useTable } from '../../../hooks/UseTable';
|
import { useTable } from '../../../hooks/UseTable';
|
||||||
import { apiUrl } from '../../../states/ApiState';
|
import { apiUrl } from '../../../states/ApiState';
|
||||||
import { useUserState } from '../../../states/UserState';
|
import { useUserState } from '../../../states/UserState';
|
||||||
import { AddItemButton } from '../../buttons/AddItemButton';
|
import { AddItemButton } from '../../buttons/AddItemButton';
|
||||||
|
import { ApiFormFieldSet } from '../../forms/fields/ApiFormField';
|
||||||
import { TableColumn } from '../Column';
|
import { TableColumn } from '../Column';
|
||||||
import { BooleanColumn, DescriptionColumn } from '../ColumnRenderers';
|
import { BooleanColumn, DescriptionColumn } from '../ColumnRenderers';
|
||||||
import { TableFilter } from '../Filter';
|
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(
|
const rowActions = useCallback(
|
||||||
(record: any) => {
|
(record: any) => {
|
||||||
let can_edit = user.hasChangeRole(UserRoles.part);
|
let can_edit = user.hasChangeRole(UserRoles.part);
|
||||||
@ -78,26 +120,15 @@ export default function PartTestTemplateTable({ partId }: { partId: number }) {
|
|||||||
RowEditAction({
|
RowEditAction({
|
||||||
hidden: !can_edit,
|
hidden: !can_edit,
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
openEditApiForm({
|
setSelectedTest(record.pk);
|
||||||
url: ApiEndpoints.part_test_template_list,
|
editTestTemplate.open();
|
||||||
pk: record.pk,
|
|
||||||
title: t`Edit Test Template`,
|
|
||||||
fields: partTestTemplateFields(),
|
|
||||||
successMessage: t`Template updated`,
|
|
||||||
onFormSuccess: table.refreshTable
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
RowDeleteAction({
|
RowDeleteAction({
|
||||||
hidden: !can_delete,
|
hidden: !can_delete,
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
openDeleteApiForm({
|
setSelectedTest(record.pk);
|
||||||
url: ApiEndpoints.part_test_template_list,
|
deleteTestTemplate.open();
|
||||||
pk: record.pk,
|
|
||||||
title: t`Delete Test Template`,
|
|
||||||
successMessage: t`Test Template deleted`,
|
|
||||||
onFormSuccess: table.refreshTable
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
];
|
];
|
||||||
@ -105,45 +136,36 @@ export default function PartTestTemplateTable({ partId }: { partId: number }) {
|
|||||||
[user]
|
[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(() => {
|
const tableActions = useMemo(() => {
|
||||||
let can_add = user.hasAddRole(UserRoles.part);
|
let can_add = user.hasAddRole(UserRoles.part);
|
||||||
|
|
||||||
return [
|
return [
|
||||||
<AddItemButton
|
<AddItemButton
|
||||||
tooltip={t`Add Test Template`}
|
tooltip={t`Add Test Template`}
|
||||||
onClick={addTestTemplate}
|
onClick={() => newTestTemplate.open()}
|
||||||
disabled={!can_add}
|
disabled={!can_add}
|
||||||
/>
|
/>
|
||||||
];
|
];
|
||||||
}, [user]);
|
}, [user]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<InvenTreeTable
|
<>
|
||||||
url={apiUrl(ApiEndpoints.part_test_template_list)}
|
{newTestTemplate.modal}
|
||||||
tableState={table}
|
{editTestTemplate.modal}
|
||||||
columns={tableColumns}
|
{deleteTestTemplate.modal}
|
||||||
props={{
|
<InvenTreeTable
|
||||||
params: {
|
url={apiUrl(ApiEndpoints.part_test_template_list)}
|
||||||
part: partId
|
tableState={table}
|
||||||
},
|
columns={tableColumns}
|
||||||
tableFilters: tableFilters,
|
props={{
|
||||||
tableActions: tableActions,
|
params: {
|
||||||
rowActions: rowActions
|
part: partId
|
||||||
}}
|
},
|
||||||
/>
|
tableFilters: tableFilters,
|
||||||
|
tableActions: tableActions,
|
||||||
|
rowActions: rowActions
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,15 +1,19 @@
|
|||||||
import { t } from '@lingui/macro';
|
import { t } from '@lingui/macro';
|
||||||
import { ActionIcon, Group, Text, Tooltip } from '@mantine/core';
|
import { Group, Text } from '@mantine/core';
|
||||||
import { IconLayersLinked } from '@tabler/icons-react';
|
import { ReactNode, useCallback, useMemo, useState } from 'react';
|
||||||
import { ReactNode, useCallback, useMemo } from 'react';
|
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
|
||||||
import { ApiEndpoints } from '../../../enums/ApiEndpoints';
|
import { ApiEndpoints } from '../../../enums/ApiEndpoints';
|
||||||
import { UserRoles } from '../../../enums/Roles';
|
import { UserRoles } from '../../../enums/Roles';
|
||||||
import { openCreateApiForm, openDeleteApiForm } from '../../../functions/forms';
|
import {
|
||||||
|
useCreateApiFormModal,
|
||||||
|
useDeleteApiFormModal
|
||||||
|
} from '../../../hooks/UseForm';
|
||||||
import { useTable } from '../../../hooks/UseTable';
|
import { useTable } from '../../../hooks/UseTable';
|
||||||
import { apiUrl } from '../../../states/ApiState';
|
import { apiUrl } from '../../../states/ApiState';
|
||||||
import { useUserState } from '../../../states/UserState';
|
import { useUserState } from '../../../states/UserState';
|
||||||
|
import { AddItemButton } from '../../buttons/AddItemButton';
|
||||||
|
import { ApiFormFieldSet } from '../../forms/fields/ApiFormField';
|
||||||
import { Thumbnail } from '../../images/Thumbnail';
|
import { Thumbnail } from '../../images/Thumbnail';
|
||||||
import { TableColumn } from '../Column';
|
import { TableColumn } from '../Column';
|
||||||
import { InvenTreeTable } from '../InvenTreeTable';
|
import { InvenTreeTable } from '../InvenTreeTable';
|
||||||
@ -66,55 +70,54 @@ export function RelatedPartTable({ partId }: { partId: number }): ReactNode {
|
|||||||
];
|
];
|
||||||
}, [partId]);
|
}, [partId]);
|
||||||
|
|
||||||
const addRelatedPart = useCallback(() => {
|
const relatedPartFields: ApiFormFieldSet = useMemo(() => {
|
||||||
openCreateApiForm({
|
return {
|
||||||
title: t`Add Related Part`,
|
part_1: {
|
||||||
url: ApiEndpoints.related_part_list,
|
hidden: true
|
||||||
fields: {
|
|
||||||
part_1: {
|
|
||||||
hidden: true,
|
|
||||||
value: partId
|
|
||||||
},
|
|
||||||
part_2: {
|
|
||||||
label: t`Related Part`
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
successMessage: t`Related part added`,
|
part_2: {}
|
||||||
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;
|
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
// Generate row actions
|
const newRelatedPart = useCreateApiFormModal({
|
||||||
// TODO: Hide if user does not have permission to edit parts
|
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(
|
const rowActions = useCallback(
|
||||||
(record: any) => {
|
(record: any) => {
|
||||||
return [
|
return [
|
||||||
RowDeleteAction({
|
RowDeleteAction({
|
||||||
hidden: !user.hasDeleteRole(UserRoles.part),
|
hidden: !user.hasDeleteRole(UserRoles.part),
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
openDeleteApiForm({
|
setSelectedRelatedPart(record.pk);
|
||||||
url: ApiEndpoints.related_part_list,
|
deleteRelatedPart.open();
|
||||||
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
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
];
|
];
|
||||||
@ -123,18 +126,22 @@ export function RelatedPartTable({ partId }: { partId: number }): ReactNode {
|
|||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<InvenTreeTable
|
<>
|
||||||
url={apiUrl(ApiEndpoints.related_part_list)}
|
{newRelatedPart.modal}
|
||||||
tableState={table}
|
{deleteRelatedPart.modal}
|
||||||
columns={tableColumns}
|
<InvenTreeTable
|
||||||
props={{
|
url={apiUrl(ApiEndpoints.related_part_list)}
|
||||||
params: {
|
tableState={table}
|
||||||
part: partId,
|
columns={tableColumns}
|
||||||
category_detail: true
|
props={{
|
||||||
},
|
params: {
|
||||||
rowActions: rowActions,
|
part: partId,
|
||||||
tableActions: customActions
|
category_detail: true
|
||||||
}}
|
},
|
||||||
/>
|
rowActions: rowActions,
|
||||||
|
tableActions: tableActions
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { t } from '@lingui/macro';
|
import { t } from '@lingui/macro';
|
||||||
import { Text } from '@mantine/core';
|
import { Text } from '@mantine/core';
|
||||||
import { IconSquareArrowRight } from '@tabler/icons-react';
|
import { IconSquareArrowRight } from '@tabler/icons-react';
|
||||||
import { useCallback, useMemo } from 'react';
|
import { useCallback, useMemo, useState } from 'react';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
|
||||||
import { ProgressBar } from '../../../components/items/ProgressBar';
|
import { ProgressBar } from '../../../components/items/ProgressBar';
|
||||||
@ -9,8 +9,12 @@ import { ApiEndpoints } from '../../../enums/ApiEndpoints';
|
|||||||
import { ModelType } from '../../../enums/ModelType';
|
import { ModelType } from '../../../enums/ModelType';
|
||||||
import { UserRoles } from '../../../enums/Roles';
|
import { UserRoles } from '../../../enums/Roles';
|
||||||
import { purchaseOrderLineItemFields } from '../../../forms/PurchaseOrderForms';
|
import { purchaseOrderLineItemFields } from '../../../forms/PurchaseOrderForms';
|
||||||
import { openCreateApiForm, openEditApiForm } from '../../../functions/forms';
|
|
||||||
import { getDetailUrl } from '../../../functions/urls';
|
import { getDetailUrl } from '../../../functions/urls';
|
||||||
|
import {
|
||||||
|
useCreateApiFormModal,
|
||||||
|
useDeleteApiFormModal,
|
||||||
|
useEditApiFormModal
|
||||||
|
} from '../../../hooks/UseForm';
|
||||||
import { useTable } from '../../../hooks/UseTable';
|
import { useTable } from '../../../hooks/UseTable';
|
||||||
import { apiUrl } from '../../../states/ApiState';
|
import { apiUrl } from '../../../states/ApiState';
|
||||||
import { useUserState } from '../../../states/UserState';
|
import { useUserState } from '../../../states/UserState';
|
||||||
@ -47,52 +51,6 @@ export function PurchaseOrderLineItemTable({
|
|||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const user = useUserState();
|
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(() => {
|
const tableColumns = useMemo(() => {
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
@ -220,18 +178,67 @@ export function PurchaseOrderLineItemTable({
|
|||||||
];
|
];
|
||||||
}, [orderId, user]);
|
}, [orderId, user]);
|
||||||
|
|
||||||
const addLine = useCallback(() => {
|
const newLine = useCreateApiFormModal({
|
||||||
openCreateApiForm({
|
url: ApiEndpoints.purchase_order_line_list,
|
||||||
url: ApiEndpoints.purchase_order_line_list,
|
title: t`Add Line Item`,
|
||||||
title: t`Add Line Item`,
|
fields: purchaseOrderLineItemFields(),
|
||||||
fields: purchaseOrderLineItemFields({
|
initialData: {
|
||||||
create: true,
|
order: orderId
|
||||||
orderId: orderId
|
},
|
||||||
}),
|
onFormSuccess: table.refreshTable
|
||||||
onFormSuccess: table.refreshTable,
|
});
|
||||||
successMessage: t`Line item added`
|
|
||||||
});
|
const [selectedLine, setSelectedLine] = useState<number | undefined>(
|
||||||
}, [orderId]);
|
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
|
// Custom table actions
|
||||||
const tableActions = useMemo(() => {
|
const tableActions = useMemo(() => {
|
||||||
@ -239,7 +246,7 @@ export function PurchaseOrderLineItemTable({
|
|||||||
<AddItemButton
|
<AddItemButton
|
||||||
key="add-line-item"
|
key="add-line-item"
|
||||||
tooltip={t`Add line item`}
|
tooltip={t`Add line item`}
|
||||||
onClick={addLine}
|
onClick={() => newLine.open()}
|
||||||
hidden={!user?.hasAddRole(UserRoles.purchase_order)}
|
hidden={!user?.hasAddRole(UserRoles.purchase_order)}
|
||||||
/>,
|
/>,
|
||||||
<ActionButton
|
<ActionButton
|
||||||
@ -251,26 +258,31 @@ export function PurchaseOrderLineItemTable({
|
|||||||
}, [orderId, user]);
|
}, [orderId, user]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<InvenTreeTable
|
<>
|
||||||
url={apiUrl(ApiEndpoints.purchase_order_line_list)}
|
{newLine.modal}
|
||||||
tableState={table}
|
{editLine.modal}
|
||||||
columns={tableColumns}
|
{deleteLine.modal}
|
||||||
props={{
|
<InvenTreeTable
|
||||||
enableSelection: true,
|
url={apiUrl(ApiEndpoints.purchase_order_line_list)}
|
||||||
enableDownload: true,
|
tableState={table}
|
||||||
params: {
|
columns={tableColumns}
|
||||||
...params,
|
props={{
|
||||||
order: orderId,
|
enableSelection: true,
|
||||||
part_detail: true
|
enableDownload: true,
|
||||||
},
|
params: {
|
||||||
rowActions: rowActions,
|
...params,
|
||||||
tableActions: tableActions,
|
order: orderId,
|
||||||
onRowClick: (row: any) => {
|
part_detail: true
|
||||||
if (row.part) {
|
},
|
||||||
navigate(getDetailUrl(ModelType.supplierpart, row.part));
|
rowActions: rowActions,
|
||||||
|
tableActions: tableActions,
|
||||||
|
onRowClick: (row: any) => {
|
||||||
|
if (row.part) {
|
||||||
|
navigate(getDetailUrl(ModelType.supplierpart, row.part));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}}
|
||||||
}}
|
/>
|
||||||
/>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
import { t } from '@lingui/macro';
|
import { t } from '@lingui/macro';
|
||||||
import { useCallback, useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
|
||||||
import { ApiEndpoints } from '../../../enums/ApiEndpoints';
|
import { ApiEndpoints } from '../../../enums/ApiEndpoints';
|
||||||
import { ModelType } from '../../../enums/ModelType';
|
import { ModelType } from '../../../enums/ModelType';
|
||||||
import { UserRoles } from '../../../enums/Roles';
|
import { UserRoles } from '../../../enums/Roles';
|
||||||
import { notYetImplemented } from '../../../functions/notifications';
|
import { purchaseOrderFields } from '../../../forms/PurchaseOrderForms';
|
||||||
import { getDetailUrl } from '../../../functions/urls';
|
import { getDetailUrl } from '../../../functions/urls';
|
||||||
|
import { useCreateApiFormModal } from '../../../hooks/UseForm';
|
||||||
import { useTable } from '../../../hooks/UseTable';
|
import { useTable } from '../../../hooks/UseTable';
|
||||||
import { apiUrl } from '../../../states/ApiState';
|
import { apiUrl } from '../../../states/ApiState';
|
||||||
import { useUserState } from '../../../states/UserState';
|
import { useUserState } from '../../../states/UserState';
|
||||||
@ -34,7 +35,13 @@ import { InvenTreeTable } from '../InvenTreeTable';
|
|||||||
/**
|
/**
|
||||||
* Display a table of purchase orders
|
* 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 navigate = useNavigate();
|
||||||
|
|
||||||
const table = useTable('purchase-order');
|
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(() => {
|
const tableColumns = useMemo(() => {
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
@ -100,38 +103,54 @@ export function PurchaseOrderTable({ params }: { params?: any }) {
|
|||||||
];
|
];
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const addPurchaseOrder = useCallback(() => {
|
const newPurchaseOrder = useCreateApiFormModal({
|
||||||
notYetImplemented();
|
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(() => {
|
const tableActions = useMemo(() => {
|
||||||
return [
|
return [
|
||||||
<AddItemButton
|
<AddItemButton
|
||||||
tooltip={t`Add Purchase Order`}
|
tooltip={t`Add Purchase Order`}
|
||||||
onClick={addPurchaseOrder}
|
onClick={() => newPurchaseOrder.open()}
|
||||||
hidden={!user.hasAddRole(UserRoles.purchase_order)}
|
hidden={!user.hasAddRole(UserRoles.purchase_order)}
|
||||||
/>
|
/>
|
||||||
];
|
];
|
||||||
}, [user]);
|
}, [user]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<InvenTreeTable
|
<>
|
||||||
url={apiUrl(ApiEndpoints.purchase_order_list)}
|
{newPurchaseOrder.modal}
|
||||||
tableState={table}
|
<InvenTreeTable
|
||||||
columns={tableColumns}
|
url={apiUrl(ApiEndpoints.purchase_order_list)}
|
||||||
props={{
|
tableState={table}
|
||||||
params: {
|
columns={tableColumns}
|
||||||
...params,
|
props={{
|
||||||
supplier_detail: true
|
params: {
|
||||||
},
|
supplier_detail: true,
|
||||||
tableFilters: tableFilters,
|
supplier: supplierId,
|
||||||
tableActions: tableActions,
|
supplier_part: supplierPartId
|
||||||
onRowClick: (row: any) => {
|
},
|
||||||
if (row.pk) {
|
tableFilters: tableFilters,
|
||||||
navigate(getDetailUrl(ModelType.purchaseorder, row.pk));
|
tableActions: tableActions,
|
||||||
|
onRowClick: (row: any) => {
|
||||||
|
if (row.pk) {
|
||||||
|
navigate(getDetailUrl(ModelType.purchaseorder, row.pk));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}}
|
||||||
}}
|
/>
|
||||||
/>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
import { t } from '@lingui/macro';
|
import { t } from '@lingui/macro';
|
||||||
import { useCallback, useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
|
||||||
import { ApiEndpoints } from '../../../enums/ApiEndpoints';
|
import { ApiEndpoints } from '../../../enums/ApiEndpoints';
|
||||||
import { ModelType } from '../../../enums/ModelType';
|
import { ModelType } from '../../../enums/ModelType';
|
||||||
import { UserRoles } from '../../../enums/Roles';
|
import { UserRoles } from '../../../enums/Roles';
|
||||||
import { notYetImplemented } from '../../../functions/notifications';
|
import { salesOrderFields } from '../../../forms/SalesOrderForms';
|
||||||
import { getDetailUrl } from '../../../functions/urls';
|
import { getDetailUrl } from '../../../functions/urls';
|
||||||
|
import { useCreateApiFormModal } from '../../../hooks/UseForm';
|
||||||
import { useTable } from '../../../hooks/UseTable';
|
import { useTable } from '../../../hooks/UseTable';
|
||||||
import { apiUrl } from '../../../states/ApiState';
|
import { apiUrl } from '../../../states/ApiState';
|
||||||
import { useUserState } from '../../../states/UserState';
|
import { useUserState } from '../../../states/UserState';
|
||||||
@ -31,7 +32,13 @@ import {
|
|||||||
} from '../Filter';
|
} from '../Filter';
|
||||||
import { InvenTreeTable } from '../InvenTreeTable';
|
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 table = useTable('sales-order');
|
||||||
const user = useUserState();
|
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(() => {
|
const tableColumns = useMemo(() => {
|
||||||
return [
|
return [
|
||||||
@ -97,38 +126,28 @@ 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 (
|
return (
|
||||||
<InvenTreeTable
|
<>
|
||||||
url={apiUrl(ApiEndpoints.sales_order_list)}
|
{newSalesOrder.modal}
|
||||||
tableState={table}
|
<InvenTreeTable
|
||||||
columns={tableColumns}
|
url={apiUrl(ApiEndpoints.sales_order_list)}
|
||||||
props={{
|
tableState={table}
|
||||||
params: {
|
columns={tableColumns}
|
||||||
...params,
|
props={{
|
||||||
customer_detail: true
|
params: {
|
||||||
},
|
customer_detail: true,
|
||||||
tableFilters: tableFilters,
|
part: partId,
|
||||||
tableActions: tableActions,
|
customer: customerId
|
||||||
onRowClick: (row: any) => {
|
},
|
||||||
if (row.pk) {
|
tableFilters: tableFilters,
|
||||||
navigate(getDetailUrl(ModelType.salesorder, row.pk));
|
tableActions: tableActions,
|
||||||
|
onRowClick: (row: any) => {
|
||||||
|
if (row.pk) {
|
||||||
|
navigate(getDetailUrl(ModelType.salesorder, row.pk));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}}
|
||||||
}}
|
/>
|
||||||
/>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,14 @@
|
|||||||
import { t } from '@lingui/macro';
|
import { t } from '@lingui/macro';
|
||||||
import { useCallback, useMemo } from 'react';
|
import { useCallback, useMemo, useState } from 'react';
|
||||||
|
|
||||||
import { ApiEndpoints } from '../../../enums/ApiEndpoints';
|
import { ApiEndpoints } from '../../../enums/ApiEndpoints';
|
||||||
import { UserRoles } from '../../../enums/Roles';
|
import { UserRoles } from '../../../enums/Roles';
|
||||||
|
import { customUnitsFields } from '../../../forms/CommonForms';
|
||||||
import {
|
import {
|
||||||
openCreateApiForm,
|
useCreateApiFormModal,
|
||||||
openDeleteApiForm,
|
useDeleteApiFormModal,
|
||||||
openEditApiForm
|
useEditApiFormModal
|
||||||
} from '../../../functions/forms';
|
} from '../../../hooks/UseForm';
|
||||||
import { useTable } from '../../../hooks/UseTable';
|
import { useTable } from '../../../hooks/UseTable';
|
||||||
import { apiUrl } from '../../../states/ApiState';
|
import { apiUrl } from '../../../states/ApiState';
|
||||||
import { useUserState } from '../../../states/UserState';
|
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(
|
const rowActions = useCallback(
|
||||||
(record: any): RowAction[] => {
|
(record: any): RowAction[] => {
|
||||||
return [
|
return [
|
||||||
RowEditAction({
|
RowEditAction({
|
||||||
hidden: !user.hasChangeRole(UserRoles.admin),
|
hidden: !user.hasChangeRole(UserRoles.admin),
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
openEditApiForm({
|
setSelectedUnit(record.pk);
|
||||||
url: ApiEndpoints.custom_unit_list,
|
editUnit.open();
|
||||||
pk: record.pk,
|
|
||||||
title: t`Edit custom unit`,
|
|
||||||
fields: {
|
|
||||||
name: {},
|
|
||||||
definition: {},
|
|
||||||
symbol: {}
|
|
||||||
},
|
|
||||||
onFormSuccess: table.refreshTable,
|
|
||||||
successMessage: t`Custom unit updated`
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
RowDeleteAction({
|
RowDeleteAction({
|
||||||
hidden: !user.hasDeleteRole(UserRoles.admin),
|
hidden: !user.hasDeleteRole(UserRoles.admin),
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
openDeleteApiForm({
|
setSelectedUnit(record.pk);
|
||||||
url: ApiEndpoints.custom_unit_list,
|
deleteUnit.open();
|
||||||
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?`
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
];
|
];
|
||||||
@ -85,40 +96,34 @@ export default function CustomUnitsTable() {
|
|||||||
[user]
|
[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(() => {
|
const tableActions = useMemo(() => {
|
||||||
let actions = [];
|
let actions = [];
|
||||||
|
|
||||||
actions.push(
|
actions.push(
|
||||||
// TODO: Adjust actions based on user permissions
|
// 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 actions;
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<InvenTreeTable
|
<>
|
||||||
url={apiUrl(ApiEndpoints.custom_unit_list)}
|
{newUnit.modal}
|
||||||
tableState={table}
|
{editUnit.modal}
|
||||||
columns={columns}
|
{deleteUnit.modal}
|
||||||
props={{
|
<InvenTreeTable
|
||||||
rowActions: rowActions,
|
url={apiUrl(ApiEndpoints.custom_unit_list)}
|
||||||
tableActions: tableActions
|
tableState={table}
|
||||||
}}
|
columns={columns}
|
||||||
/>
|
props={{
|
||||||
|
rowActions: rowActions,
|
||||||
|
tableActions: tableActions
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,13 @@
|
|||||||
import { Trans, t } from '@lingui/macro';
|
import { Trans, t } from '@lingui/macro';
|
||||||
import { Group, LoadingOverlay, Stack, Text, Title } from '@mantine/core';
|
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 { useNavigate } from 'react-router-dom';
|
||||||
|
|
||||||
import { ApiEndpoints } from '../../../enums/ApiEndpoints';
|
import { ApiEndpoints } from '../../../enums/ApiEndpoints';
|
||||||
import { openCreateApiForm, openDeleteApiForm } from '../../../functions/forms';
|
import {
|
||||||
|
useCreateApiFormModal,
|
||||||
|
useDeleteApiFormModal
|
||||||
|
} from '../../../hooks/UseForm';
|
||||||
import { useInstance } from '../../../hooks/UseInstance';
|
import { useInstance } from '../../../hooks/UseInstance';
|
||||||
import { useTable } from '../../../hooks/UseTable';
|
import { useTable } from '../../../hooks/UseTable';
|
||||||
import { apiUrl } from '../../../states/ApiState';
|
import { apiUrl } from '../../../states/ApiState';
|
||||||
@ -109,28 +112,31 @@ export function GroupTable() {
|
|||||||
}),
|
}),
|
||||||
RowDeleteAction({
|
RowDeleteAction({
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
openDeleteApiForm({
|
setSelectedGroup(record.pk), deleteGroup.open();
|
||||||
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?`
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
];
|
];
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const addGroup = useCallback(() => {
|
const [selectedGroup, setSelectedGroup] = useState<number | undefined>(
|
||||||
openCreateApiForm({
|
undefined
|
||||||
url: ApiEndpoints.group_list,
|
);
|
||||||
title: t`Add group`,
|
|
||||||
fields: { name: {} },
|
const deleteGroup = useDeleteApiFormModal({
|
||||||
onFormSuccess: table.refreshTable,
|
url: ApiEndpoints.group_list,
|
||||||
successMessage: t`Added group`
|
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
|
||||||
|
});
|
||||||
|
|
||||||
const tableActions = useMemo(() => {
|
const tableActions = useMemo(() => {
|
||||||
let actions = [];
|
let actions = [];
|
||||||
@ -138,7 +144,7 @@ export function GroupTable() {
|
|||||||
actions.push(
|
actions.push(
|
||||||
<AddItemButton
|
<AddItemButton
|
||||||
key={'add-group'}
|
key={'add-group'}
|
||||||
onClick={addGroup}
|
onClick={() => newGroup.open()}
|
||||||
tooltip={t`Add group`}
|
tooltip={t`Add group`}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
@ -148,6 +154,8 @@ export function GroupTable() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
{newGroup.modal}
|
||||||
|
{deleteGroup.modal}
|
||||||
<DetailDrawer
|
<DetailDrawer
|
||||||
title={t`Edit group`}
|
title={t`Edit group`}
|
||||||
renderContent={(id) => {
|
renderContent={(id) => {
|
||||||
|
@ -1,13 +1,14 @@
|
|||||||
import { t } from '@lingui/macro';
|
import { t } from '@lingui/macro';
|
||||||
import { useCallback, useMemo } from 'react';
|
import { useCallback, useMemo, useState } from 'react';
|
||||||
|
|
||||||
import { ApiEndpoints } from '../../../enums/ApiEndpoints';
|
import { ApiEndpoints } from '../../../enums/ApiEndpoints';
|
||||||
import { UserRoles } from '../../../enums/Roles';
|
import { UserRoles } from '../../../enums/Roles';
|
||||||
|
import { projectCodeFields } from '../../../forms/CommonForms';
|
||||||
import {
|
import {
|
||||||
openCreateApiForm,
|
useCreateApiFormModal,
|
||||||
openDeleteApiForm,
|
useDeleteApiFormModal,
|
||||||
openEditApiForm
|
useEditApiFormModal
|
||||||
} from '../../../functions/forms';
|
} from '../../../hooks/UseForm';
|
||||||
import { useTable } from '../../../hooks/UseTable';
|
import { useTable } from '../../../hooks/UseTable';
|
||||||
import { apiUrl } from '../../../states/ApiState';
|
import { apiUrl } from '../../../states/ApiState';
|
||||||
import { useUserState } from '../../../states/UserState';
|
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(
|
const rowActions = useCallback(
|
||||||
(record: any): RowAction[] => {
|
(record: any): RowAction[] => {
|
||||||
return [
|
return [
|
||||||
RowEditAction({
|
RowEditAction({
|
||||||
hidden: !user.hasChangeRole(UserRoles.admin),
|
hidden: !user.hasChangeRole(UserRoles.admin),
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
openEditApiForm({
|
setSelectedProjectCode(record.pk);
|
||||||
url: ApiEndpoints.project_code_list,
|
editProjectCode.open();
|
||||||
pk: record.pk,
|
|
||||||
title: t`Edit project code`,
|
|
||||||
fields: {
|
|
||||||
code: {},
|
|
||||||
description: {},
|
|
||||||
responsible: {}
|
|
||||||
},
|
|
||||||
onFormSuccess: table.refreshTable,
|
|
||||||
successMessage: t`Project code updated`
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
RowDeleteAction({
|
RowDeleteAction({
|
||||||
hidden: !user.hasDeleteRole(UserRoles.admin),
|
hidden: !user.hasDeleteRole(UserRoles.admin),
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
openDeleteApiForm({
|
setSelectedProjectCode(record.pk);
|
||||||
url: ApiEndpoints.project_code_list,
|
deleteProjectCode.open();
|
||||||
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?`
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
];
|
];
|
||||||
@ -75,39 +86,33 @@ export default function ProjectCodeTable() {
|
|||||||
[user]
|
[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(() => {
|
const tableActions = useMemo(() => {
|
||||||
let actions = [];
|
let actions = [];
|
||||||
|
|
||||||
actions.push(
|
actions.push(
|
||||||
<AddItemButton onClick={addProjectCode} tooltip={t`Add project code`} />
|
<AddItemButton
|
||||||
|
onClick={() => newProjectCode.open()}
|
||||||
|
tooltip={t`Add project code`}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
return actions;
|
return actions;
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<InvenTreeTable
|
<>
|
||||||
url={apiUrl(ApiEndpoints.project_code_list)}
|
{newProjectCode.modal}
|
||||||
tableState={table}
|
{editProjectCode.modal}
|
||||||
columns={columns}
|
{deleteProjectCode.modal}
|
||||||
props={{
|
<InvenTreeTable
|
||||||
rowActions: rowActions,
|
url={apiUrl(ApiEndpoints.project_code_list)}
|
||||||
tableActions: tableActions
|
tableState={table}
|
||||||
}}
|
columns={columns}
|
||||||
/>
|
props={{
|
||||||
|
rowActions: rowActions,
|
||||||
|
tableActions: tableActions
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,16 @@
|
|||||||
import { t } from '@lingui/macro';
|
import { t } from '@lingui/macro';
|
||||||
import { useCallback, useMemo } from 'react';
|
import { useCallback, useMemo, useState } from 'react';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
|
||||||
import { ApiEndpoints } from '../../../enums/ApiEndpoints';
|
import { ApiEndpoints } from '../../../enums/ApiEndpoints';
|
||||||
import { ModelType } from '../../../enums/ModelType';
|
import { ModelType } from '../../../enums/ModelType';
|
||||||
import { UserRoles } from '../../../enums/Roles';
|
import { UserRoles } from '../../../enums/Roles';
|
||||||
import { stockLocationFields } from '../../../forms/StockForms';
|
import { stockLocationFields } from '../../../forms/StockForms';
|
||||||
import { openCreateApiForm, openEditApiForm } from '../../../functions/forms';
|
|
||||||
import { getDetailUrl } from '../../../functions/urls';
|
import { getDetailUrl } from '../../../functions/urls';
|
||||||
|
import {
|
||||||
|
useCreateApiFormModal,
|
||||||
|
useEditApiFormModal
|
||||||
|
} from '../../../hooks/UseForm';
|
||||||
import { useTable } from '../../../hooks/UseTable';
|
import { useTable } from '../../../hooks/UseTable';
|
||||||
import { apiUrl } from '../../../states/ApiState';
|
import { apiUrl } from '../../../states/ApiState';
|
||||||
import { useUserState } from '../../../states/UserState';
|
import { useUserState } from '../../../states/UserState';
|
||||||
@ -96,26 +99,33 @@ export function StockLocationTable({ parentId }: { parentId?: any }) {
|
|||||||
];
|
];
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const addLocation = useCallback(() => {
|
const newLocation = useCreateApiFormModal({
|
||||||
let fields = stockLocationFields({});
|
url: ApiEndpoints.stock_location_list,
|
||||||
|
title: t`Add Stock Location`,
|
||||||
if (parentId) {
|
fields: stockLocationFields({}),
|
||||||
fields['parent'].value = parentId;
|
initialData: {
|
||||||
}
|
parent: parentId
|
||||||
|
},
|
||||||
openCreateApiForm({
|
onFormSuccess(data: any) {
|
||||||
url: apiUrl(ApiEndpoints.stock_location_list),
|
if (data.pk) {
|
||||||
title: t`Add Stock Location`,
|
navigate(getDetailUrl(ModelType.stocklocation, data.pk));
|
||||||
fields: fields,
|
} else {
|
||||||
onFormSuccess(data: any) {
|
table.refreshTable();
|
||||||
if (data.pk) {
|
|
||||||
navigate(`/stock/location/${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(() => {
|
const tableActions = useMemo(() => {
|
||||||
let can_add = user.hasAddRole(UserRoles.stock_location);
|
let can_add = user.hasAddRole(UserRoles.stock_location);
|
||||||
@ -123,7 +133,7 @@ export function StockLocationTable({ parentId }: { parentId?: any }) {
|
|||||||
return [
|
return [
|
||||||
<AddItemButton
|
<AddItemButton
|
||||||
tooltip={t`Add Stock Location`}
|
tooltip={t`Add Stock Location`}
|
||||||
onClick={addLocation}
|
onClick={() => newLocation.open()}
|
||||||
disabled={!can_add}
|
disabled={!can_add}
|
||||||
/>
|
/>
|
||||||
];
|
];
|
||||||
@ -137,14 +147,8 @@ export function StockLocationTable({ parentId }: { parentId?: any }) {
|
|||||||
RowEditAction({
|
RowEditAction({
|
||||||
hidden: !can_edit,
|
hidden: !can_edit,
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
openEditApiForm({
|
setSelectedLocation(record.pk);
|
||||||
url: ApiEndpoints.stock_location_list,
|
editLocation.open();
|
||||||
pk: record.pk,
|
|
||||||
title: t`Edit Stock Location`,
|
|
||||||
fields: stockLocationFields({}),
|
|
||||||
successMessage: t`Stock location updated`,
|
|
||||||
onFormSuccess: table.refreshTable
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
];
|
];
|
||||||
@ -153,23 +157,26 @@ export function StockLocationTable({ parentId }: { parentId?: any }) {
|
|||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<InvenTreeTable
|
<>
|
||||||
url={apiUrl(ApiEndpoints.stock_location_list)}
|
{newLocation.modal}
|
||||||
tableState={table}
|
{editLocation.modal}
|
||||||
columns={tableColumns}
|
<InvenTreeTable
|
||||||
props={{
|
url={apiUrl(ApiEndpoints.stock_location_list)}
|
||||||
enableDownload: true,
|
tableState={table}
|
||||||
params: {
|
columns={tableColumns}
|
||||||
parent: parentId ?? 'null'
|
props={{
|
||||||
},
|
enableDownload: true,
|
||||||
tableFilters: tableFilters,
|
params: {
|
||||||
tableActions: tableActions,
|
parent: parentId ?? 'null'
|
||||||
rowActions: rowActions,
|
},
|
||||||
onRowClick: (record) => {
|
tableFilters: tableFilters,
|
||||||
navigate(getDetailUrl(ModelType.stocklocation, record.pk));
|
tableActions: tableActions,
|
||||||
}
|
rowActions: rowActions,
|
||||||
// TODO: allow for "tree view" with cascade
|
onRowClick: (record) => {
|
||||||
}}
|
navigate(getDetailUrl(ModelType.stocklocation, record.pk));
|
||||||
/>
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
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 {
|
import {
|
||||||
IconAt,
|
IconAt,
|
||||||
IconCurrencyDollar,
|
IconCurrencyDollar,
|
||||||
@ -12,8 +11,6 @@ import {
|
|||||||
import { useEffect, useMemo, useState } from 'react';
|
import { useEffect, useMemo, useState } from 'react';
|
||||||
|
|
||||||
import { ApiFormFieldSet } from '../components/forms/fields/ApiFormField';
|
import { ApiFormFieldSet } from '../components/forms/fields/ApiFormField';
|
||||||
import { ApiEndpoints } from '../enums/ApiEndpoints';
|
|
||||||
import { openEditApiForm } from '../functions/forms';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Field set for SupplierPart instance
|
* Field set for SupplierPart instance
|
||||||
@ -131,54 +128,3 @@ export function companyFields(): ApiFormFieldSet {
|
|||||||
is_customer: {}
|
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 { IconPackages } from '@tabler/icons-react';
|
||||||
|
|
||||||
import { ApiFormFieldSet } from '../components/forms/fields/ApiFormField';
|
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
|
* Construct a set of fields for creating / editing a Part instance
|
||||||
@ -98,39 +96,6 @@ export function partFields({
|
|||||||
return fields;
|
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
|
* Construct a set of fields for creating / editing a PartCategory instance
|
||||||
*/
|
*/
|
||||||
@ -154,26 +119,3 @@ export function partCategoryFields({}: {}): ApiFormFieldSet {
|
|||||||
|
|
||||||
return fields;
|
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 {
|
import {
|
||||||
|
IconAddressBook,
|
||||||
IconCalendar,
|
IconCalendar,
|
||||||
IconCoins,
|
IconCoins,
|
||||||
IconCurrencyDollar,
|
IconCurrencyDollar,
|
||||||
|
IconHash,
|
||||||
IconLink,
|
IconLink,
|
||||||
|
IconList,
|
||||||
IconNotes,
|
IconNotes,
|
||||||
IconSitemap
|
IconSitemap,
|
||||||
|
IconUser,
|
||||||
|
IconUsers
|
||||||
} from '@tabler/icons-react';
|
} 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
|
* Construct a set of fields for creating / editing a PurchaseOrderLineItem instance
|
||||||
*/
|
*/
|
||||||
export function purchaseOrderLineItemFields({
|
export function purchaseOrderLineItemFields() {
|
||||||
supplierId,
|
|
||||||
orderId,
|
|
||||||
create = false
|
|
||||||
}: {
|
|
||||||
supplierId?: number;
|
|
||||||
orderId?: number;
|
|
||||||
create?: boolean;
|
|
||||||
}) {
|
|
||||||
let fields: ApiFormFieldSet = {
|
let fields: ApiFormFieldSet = {
|
||||||
order: {
|
order: {
|
||||||
filters: {
|
filters: {
|
||||||
supplier_detail: true
|
supplier_detail: true
|
||||||
},
|
},
|
||||||
value: orderId,
|
hidden: true
|
||||||
hidden: create != true || orderId != undefined
|
|
||||||
},
|
},
|
||||||
part: {
|
part: {
|
||||||
filters: {
|
filters: {
|
||||||
part_detail: true,
|
part_detail: true,
|
||||||
supplier_detail: true,
|
supplier_detail: true
|
||||||
supplier: supplierId
|
|
||||||
},
|
},
|
||||||
adjustFilters: (filters: any) => {
|
adjustFilters: (value: ApiFormAdjustFilterType) => {
|
||||||
// TODO: Filter by the supplier associated with the order
|
// TODO: Adjust part based on the supplier associated with the supplier
|
||||||
return filters;
|
return value.filters;
|
||||||
}
|
}
|
||||||
// TODO: Custom onEdit callback (see purchase_order.js)
|
|
||||||
// TODO: secondary modal (see purchase_order.js)
|
|
||||||
},
|
},
|
||||||
quantity: {},
|
quantity: {},
|
||||||
reference: {},
|
reference: {},
|
||||||
@ -66,3 +62,52 @@ export function purchaseOrderLineItemFields({
|
|||||||
|
|
||||||
return fields;
|
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({
|
return useCreateApiFormModal({
|
||||||
url: ApiEndpoints.stock_item_list,
|
url: ApiEndpoints.stock_item_list,
|
||||||
fields: fields,
|
fields: fields,
|
||||||
title: t`Create Stock Item`
|
title: t`Add Stock Item`
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { t } from '@lingui/macro';
|
import { t } from '@lingui/macro';
|
||||||
import { Divider, Stack } from '@mantine/core';
|
import { Alert, Divider, Stack } from '@mantine/core';
|
||||||
import { useId } from '@mantine/hooks';
|
import { useId } from '@mantine/hooks';
|
||||||
import { useEffect, useMemo, useRef } from 'react';
|
import { useEffect, useMemo, useRef } from 'react';
|
||||||
|
|
||||||
@ -82,6 +82,8 @@ export function useCreateApiFormModal(props: ApiFormModalProps) {
|
|||||||
const createProps = useMemo<ApiFormModalProps>(
|
const createProps = useMemo<ApiFormModalProps>(
|
||||||
() => ({
|
() => ({
|
||||||
...props,
|
...props,
|
||||||
|
fetchInitialData: props.fetchInitialData ?? false,
|
||||||
|
successMessage: props.successMessage ?? t`Item Created`,
|
||||||
method: 'POST'
|
method: 'POST'
|
||||||
}),
|
}),
|
||||||
[props]
|
[props]
|
||||||
@ -98,7 +100,8 @@ export function useEditApiFormModal(props: ApiFormModalProps) {
|
|||||||
() => ({
|
() => ({
|
||||||
...props,
|
...props,
|
||||||
fetchInitialData: props.fetchInitialData ?? true,
|
fetchInitialData: props.fetchInitialData ?? true,
|
||||||
method: 'PUT'
|
successMessage: props.successMessage ?? t`Item Updated`,
|
||||||
|
method: 'PATCH'
|
||||||
}),
|
}),
|
||||||
[props]
|
[props]
|
||||||
);
|
);
|
||||||
@ -116,6 +119,12 @@ export function useDeleteApiFormModal(props: ApiFormModalProps) {
|
|||||||
method: 'DELETE',
|
method: 'DELETE',
|
||||||
submitText: t`Delete`,
|
submitText: t`Delete`,
|
||||||
submitColor: 'red',
|
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: {}
|
fields: {}
|
||||||
}),
|
}),
|
||||||
[props]
|
[props]
|
||||||
|
@ -42,6 +42,14 @@ window.INVENTREE_SETTINGS = {
|
|||||||
host: `${window.location.origin}/`,
|
host: `${window.location.origin}/`,
|
||||||
name: 'Current Server'
|
name: 'Current Server'
|
||||||
},
|
},
|
||||||
|
...(IS_DEV
|
||||||
|
? {
|
||||||
|
'mantine-2j5j5j5j5': {
|
||||||
|
host: 'http://localhost:8000',
|
||||||
|
name: 'Localhost'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
: {}),
|
||||||
...(IS_DEV_OR_DEMO
|
...(IS_DEV_OR_DEMO
|
||||||
? {
|
? {
|
||||||
'mantine-u56l5jt85': {
|
'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,
|
show_server_selector: IS_DEV_OR_DEMO,
|
||||||
|
|
||||||
// merge in settings that are already set via django's spa_view or for development
|
// 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 { StatusRenderer } from '../../components/render/StatusRenderer';
|
||||||
import { ApiEndpoints } from '../../enums/ApiEndpoints';
|
import { ApiEndpoints } from '../../enums/ApiEndpoints';
|
||||||
import { ModelType } from '../../enums/ModelType';
|
import { ModelType } from '../../enums/ModelType';
|
||||||
import {
|
import { partCategoryFields, partFields } from '../../forms/PartForms';
|
||||||
createPart,
|
|
||||||
editPart,
|
|
||||||
partCategoryFields,
|
|
||||||
partFields
|
|
||||||
} from '../../forms/PartForms';
|
|
||||||
import { useCreateStockItem } from '../../forms/StockForms';
|
import { useCreateStockItem } from '../../forms/StockForms';
|
||||||
import {
|
import {
|
||||||
OpenApiFormProps,
|
useCreateApiFormModal,
|
||||||
openCreateApiForm,
|
useEditApiFormModal
|
||||||
openEditApiForm
|
} from '../../hooks/UseForm';
|
||||||
} from '../../functions/forms';
|
|
||||||
import { useCreateApiFormModal } from '../../hooks/UseForm';
|
|
||||||
|
|
||||||
// Generate some example forms using the modal API forms interface
|
// Generate some example forms using the modal API forms interface
|
||||||
const fields = partCategoryFields({});
|
const fields = partCategoryFields({});
|
||||||
|
|
||||||
function ApiFormsPlayground() {
|
function ApiFormsPlayground() {
|
||||||
const editCategoryForm: OpenApiFormProps = {
|
const editCategory = useEditApiFormModal({
|
||||||
url: ApiEndpoints.category_list,
|
url: ApiEndpoints.category_list,
|
||||||
pk: 2,
|
pk: 2,
|
||||||
title: 'Edit Category',
|
title: 'Edit Category',
|
||||||
fields: fields
|
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,
|
url: ApiEndpoints.part_attachment_list,
|
||||||
title: 'Create Attachment',
|
title: 'Create Attachment',
|
||||||
successMessage: 'Attachment uploaded',
|
|
||||||
fields: {
|
fields: {
|
||||||
part: {
|
part: {},
|
||||||
value: 1
|
|
||||||
},
|
|
||||||
attachment: {},
|
attachment: {},
|
||||||
comment: {}
|
comment: {}
|
||||||
}
|
},
|
||||||
};
|
initialData: {
|
||||||
|
part: 1
|
||||||
|
},
|
||||||
|
successMessage: 'Attachment uploaded'
|
||||||
|
});
|
||||||
|
|
||||||
const [active, setActive] = useState(true);
|
const [active, setActive] = useState(true);
|
||||||
const [name, setName] = useState('Hello');
|
const [name, setName] = useState('Hello');
|
||||||
|
|
||||||
@ -73,6 +85,14 @@ function ApiFormsPlayground() {
|
|||||||
url: ApiEndpoints.part_list,
|
url: ApiEndpoints.part_list,
|
||||||
title: 'Create part',
|
title: 'Create part',
|
||||||
fields: partFieldsState,
|
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: (
|
preFormContent: (
|
||||||
<Button onClick={() => setName('Hello world')}>
|
<Button onClick={() => setName('Hello world')}>
|
||||||
Set name="Hello world"
|
Set name="Hello world"
|
||||||
@ -86,19 +106,20 @@ function ApiFormsPlayground() {
|
|||||||
return (
|
return (
|
||||||
<Stack>
|
<Stack>
|
||||||
<Group>
|
<Group>
|
||||||
<Button onClick={() => createPart()}>Create New Part</Button>
|
<Button onClick={() => newPart.open()}>Create New Part</Button>
|
||||||
<Button onClick={() => editPart({ part_id: 1 })}>Edit Part</Button>
|
{newPart.modal}
|
||||||
|
|
||||||
|
<Button onClick={() => editPart.open()}>Edit Part</Button>
|
||||||
|
{editPart.modal}
|
||||||
|
|
||||||
<Button onClick={() => openCreateStockItem()}>Create Stock Item</Button>
|
<Button onClick={() => openCreateStockItem()}>Create Stock Item</Button>
|
||||||
{createStockItemModal}
|
{createStockItemModal}
|
||||||
|
|
||||||
<Button onClick={() => openEditApiForm(editCategoryForm)}>
|
<Button onClick={() => editCategory.open()}>Edit Category</Button>
|
||||||
Edit Category
|
{editCategory.modal}
|
||||||
</Button>
|
|
||||||
|
|
||||||
<Button onClick={() => openCreateApiForm(createAttachmentForm)}>
|
<Button onClick={() => newAttachment.open()}>Create Attachment</Button>
|
||||||
Create Attachment
|
{newAttachment.modal}
|
||||||
</Button>
|
|
||||||
|
|
||||||
<Button onClick={() => openCreatePart()}>Create Part new Modal</Button>
|
<Button onClick={() => openCreatePart()}>Create Part new Modal</Button>
|
||||||
{createPartModal}
|
{createPartModal}
|
||||||
|
@ -14,12 +14,11 @@ import {
|
|||||||
IconQrcode,
|
IconQrcode,
|
||||||
IconSitemap
|
IconSitemap
|
||||||
} from '@tabler/icons-react';
|
} from '@tabler/icons-react';
|
||||||
import { useCallback, useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
import { useParams } from 'react-router-dom';
|
import { useParams } from 'react-router-dom';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
ActionDropdown,
|
ActionDropdown,
|
||||||
DeleteItemAction,
|
|
||||||
DuplicateItemAction,
|
DuplicateItemAction,
|
||||||
EditItemAction,
|
EditItemAction,
|
||||||
LinkBarcodeAction,
|
LinkBarcodeAction,
|
||||||
@ -36,8 +35,9 @@ import { StockItemTable } from '../../components/tables/stock/StockItemTable';
|
|||||||
import { NotesEditor } from '../../components/widgets/MarkdownEditor';
|
import { NotesEditor } from '../../components/widgets/MarkdownEditor';
|
||||||
import { ApiEndpoints } from '../../enums/ApiEndpoints';
|
import { ApiEndpoints } from '../../enums/ApiEndpoints';
|
||||||
import { ModelType } from '../../enums/ModelType';
|
import { ModelType } from '../../enums/ModelType';
|
||||||
|
import { UserRoles } from '../../enums/Roles';
|
||||||
import { buildOrderFields } from '../../forms/BuildForms';
|
import { buildOrderFields } from '../../forms/BuildForms';
|
||||||
import { openEditApiForm } from '../../functions/forms';
|
import { useEditApiFormModal } from '../../hooks/UseForm';
|
||||||
import { useInstance } from '../../hooks/UseInstance';
|
import { useInstance } from '../../hooks/UseInstance';
|
||||||
import { apiUrl } from '../../states/ApiState';
|
import { apiUrl } from '../../states/ApiState';
|
||||||
import { useUserState } from '../../states/UserState';
|
import { useUserState } from '../../states/UserState';
|
||||||
@ -152,12 +152,10 @@ export default function BuildDetail() {
|
|||||||
name: 'child-orders',
|
name: 'child-orders',
|
||||||
label: t`Child Build Orders`,
|
label: t`Child Build Orders`,
|
||||||
icon: <IconSitemap />,
|
icon: <IconSitemap />,
|
||||||
content: (
|
content: build.pk ? (
|
||||||
<BuildOrderTable
|
<BuildOrderTable parentBuildId={build.pk} />
|
||||||
params={{
|
) : (
|
||||||
parent: id
|
<Skeleton />
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -187,24 +185,15 @@ export default function BuildDetail() {
|
|||||||
];
|
];
|
||||||
}, [build, id]);
|
}, [build, id]);
|
||||||
|
|
||||||
const editBuildOrder = useCallback(() => {
|
const editBuild = useEditApiFormModal({
|
||||||
let fields = buildOrderFields();
|
url: ApiEndpoints.build_order_list,
|
||||||
|
pk: build.pk,
|
||||||
// Cannot edit part field after creation
|
title: t`Edit Build Order`,
|
||||||
fields['part'].hidden = true;
|
fields: buildOrderFields(),
|
||||||
|
onFormSuccess: () => {
|
||||||
build.pk &&
|
refreshInstance();
|
||||||
openEditApiForm({
|
}
|
||||||
url: ApiEndpoints.build_order_list,
|
});
|
||||||
pk: build.pk,
|
|
||||||
title: t`Edit Build Order`,
|
|
||||||
fields: fields,
|
|
||||||
successMessage: t`Build Order updated`,
|
|
||||||
onFormSuccess: () => {
|
|
||||||
refreshInstance();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}, [build]);
|
|
||||||
|
|
||||||
const buildActions = useMemo(() => {
|
const buildActions = useMemo(() => {
|
||||||
// TODO: Disable certain actions based on user permissions
|
// TODO: Disable certain actions based on user permissions
|
||||||
@ -241,10 +230,10 @@ export default function BuildDetail() {
|
|||||||
icon={<IconDots />}
|
icon={<IconDots />}
|
||||||
actions={[
|
actions={[
|
||||||
EditItemAction({
|
EditItemAction({
|
||||||
onClick: editBuildOrder
|
onClick: () => editBuild.open(),
|
||||||
|
disabled: !user.hasChangeRole(UserRoles.build)
|
||||||
}),
|
}),
|
||||||
DuplicateItemAction({}),
|
DuplicateItemAction({})
|
||||||
DeleteItemAction({})
|
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
];
|
];
|
||||||
@ -263,6 +252,7 @@ export default function BuildDetail() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
{editBuild.modal}
|
||||||
<Stack spacing="xs">
|
<Stack spacing="xs">
|
||||||
<LoadingOverlay visible={instanceQuery.isFetching} />
|
<LoadingOverlay visible={instanceQuery.isFetching} />
|
||||||
<PageDetail
|
<PageDetail
|
||||||
|
@ -1,45 +1,17 @@
|
|||||||
import { t } from '@lingui/macro';
|
import { t } from '@lingui/macro';
|
||||||
import { Button, Stack } from '@mantine/core';
|
import { Stack } from '@mantine/core';
|
||||||
import { useCallback } from 'react';
|
|
||||||
import { useNavigate } from 'react-router-dom';
|
|
||||||
|
|
||||||
import { PageDetail } from '../../components/nav/PageDetail';
|
import { PageDetail } from '../../components/nav/PageDetail';
|
||||||
import { BuildOrderTable } from '../../components/tables/build/BuildOrderTable';
|
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
|
* Build Order index page
|
||||||
*/
|
*/
|
||||||
export default function BuildIndex() {
|
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 (
|
return (
|
||||||
<>
|
<>
|
||||||
<Stack>
|
<Stack>
|
||||||
<PageDetail
|
<PageDetail title={t`Build Orders`} actions={[]} />
|
||||||
title={t`Build Orders`}
|
|
||||||
actions={[
|
|
||||||
<Button key="new-build" color="green" onClick={newBuildOrder}>
|
|
||||||
{t`New Build Order`}
|
|
||||||
</Button>
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
<BuildOrderTable />
|
<BuildOrderTable />
|
||||||
</Stack>
|
</Stack>
|
||||||
</>
|
</>
|
||||||
|
@ -39,7 +39,8 @@ import { StockItemTable } from '../../components/tables/stock/StockItemTable';
|
|||||||
import { NotesEditor } from '../../components/widgets/MarkdownEditor';
|
import { NotesEditor } from '../../components/widgets/MarkdownEditor';
|
||||||
import { ApiEndpoints } from '../../enums/ApiEndpoints';
|
import { ApiEndpoints } from '../../enums/ApiEndpoints';
|
||||||
import { UserRoles } from '../../enums/Roles';
|
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 { useInstance } from '../../hooks/UseInstance';
|
||||||
import { apiUrl } from '../../states/ApiState';
|
import { apiUrl } from '../../states/ApiState';
|
||||||
import { useUserState } from '../../states/UserState';
|
import { useUserState } from '../../states/UserState';
|
||||||
@ -98,9 +99,7 @@ export default function CompanyDetail(props: CompanyDetailProps) {
|
|||||||
label: t`Purchase Orders`,
|
label: t`Purchase Orders`,
|
||||||
icon: <IconShoppingCart />,
|
icon: <IconShoppingCart />,
|
||||||
hidden: !company?.is_supplier,
|
hidden: !company?.is_supplier,
|
||||||
content: company?.pk && (
|
content: company?.pk && <PurchaseOrderTable supplierId={company.pk} />
|
||||||
<PurchaseOrderTable params={{ supplier: company.pk }} />
|
|
||||||
)
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'stock-items',
|
name: 'stock-items',
|
||||||
@ -116,9 +115,7 @@ export default function CompanyDetail(props: CompanyDetailProps) {
|
|||||||
label: t`Sales Orders`,
|
label: t`Sales Orders`,
|
||||||
icon: <IconTruckDelivery />,
|
icon: <IconTruckDelivery />,
|
||||||
hidden: !company?.is_customer,
|
hidden: !company?.is_customer,
|
||||||
content: company?.pk && (
|
content: company?.pk && <SalesOrderTable customerId={company.pk} />
|
||||||
<SalesOrderTable params={{ customer: company.pk }} />
|
|
||||||
)
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'return-orders',
|
name: 'return-orders',
|
||||||
@ -179,6 +176,14 @@ export default function CompanyDetail(props: CompanyDetailProps) {
|
|||||||
];
|
];
|
||||||
}, [id, company]);
|
}, [id, company]);
|
||||||
|
|
||||||
|
const editCompany = useEditApiFormModal({
|
||||||
|
url: ApiEndpoints.company_list,
|
||||||
|
pk: company?.pk,
|
||||||
|
title: t`Edit Company`,
|
||||||
|
fields: companyFields(),
|
||||||
|
onFormSuccess: refreshInstance
|
||||||
|
});
|
||||||
|
|
||||||
const companyActions = useMemo(() => {
|
const companyActions = useMemo(() => {
|
||||||
return [
|
return [
|
||||||
<ActionDropdown
|
<ActionDropdown
|
||||||
@ -188,14 +193,7 @@ export default function CompanyDetail(props: CompanyDetailProps) {
|
|||||||
actions={[
|
actions={[
|
||||||
EditItemAction({
|
EditItemAction({
|
||||||
disabled: !user.hasChangeRole(UserRoles.purchase_order),
|
disabled: !user.hasChangeRole(UserRoles.purchase_order),
|
||||||
onClick: () => {
|
onClick: () => editCompany.open()
|
||||||
if (company?.pk) {
|
|
||||||
editCompany({
|
|
||||||
pk: company?.pk,
|
|
||||||
callback: refreshInstance
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}),
|
}),
|
||||||
DeleteItemAction({
|
DeleteItemAction({
|
||||||
disabled: !user.hasDeleteRole(UserRoles.purchase_order)
|
disabled: !user.hasDeleteRole(UserRoles.purchase_order)
|
||||||
@ -206,16 +204,19 @@ export default function CompanyDetail(props: CompanyDetailProps) {
|
|||||||
}, [id, company, user]);
|
}, [id, company, user]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack spacing="xs">
|
<>
|
||||||
<LoadingOverlay visible={instanceQuery.isFetching} />
|
{editCompany.modal}
|
||||||
<PageDetail
|
<Stack spacing="xs">
|
||||||
title={t`Company` + `: ${company.name}`}
|
<LoadingOverlay visible={instanceQuery.isFetching} />
|
||||||
subtitle={company.description}
|
<PageDetail
|
||||||
actions={companyActions}
|
title={t`Company` + `: ${company.name}`}
|
||||||
imageUrl={company.image}
|
subtitle={company.description}
|
||||||
breadcrumbs={props.breadcrumbs}
|
actions={companyActions}
|
||||||
/>
|
imageUrl={company.image}
|
||||||
<PanelGroup pageKey="company" panels={companyPanels} />
|
breadcrumbs={props.breadcrumbs}
|
||||||
</Stack>
|
/>
|
||||||
|
<PanelGroup pageKey="company" panels={companyPanels} />
|
||||||
|
</Stack>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -45,7 +45,7 @@ export default function SupplierPartDetail() {
|
|||||||
label: t`Purchase Orders`,
|
label: t`Purchase Orders`,
|
||||||
icon: <IconShoppingCart />,
|
icon: <IconShoppingCart />,
|
||||||
content: supplierPart?.pk ? (
|
content: supplierPart?.pk ? (
|
||||||
<PurchaseOrderTable params={{ supplier_part: supplierPart.pk }} />
|
<PurchaseOrderTable supplierPartId={supplierPart.pk} />
|
||||||
) : (
|
) : (
|
||||||
<Skeleton />
|
<Skeleton />
|
||||||
)
|
)
|
||||||
|
@ -63,7 +63,8 @@ import { NotesEditor } from '../../components/widgets/MarkdownEditor';
|
|||||||
import { formatPriceRange } from '../../defaults/formatters';
|
import { formatPriceRange } from '../../defaults/formatters';
|
||||||
import { ApiEndpoints } from '../../enums/ApiEndpoints';
|
import { ApiEndpoints } from '../../enums/ApiEndpoints';
|
||||||
import { UserRoles } from '../../enums/Roles';
|
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 { useInstance } from '../../hooks/UseInstance';
|
||||||
import { apiUrl } from '../../states/ApiState';
|
import { apiUrl } from '../../states/ApiState';
|
||||||
import { useUserState } from '../../states/UserState';
|
import { useUserState } from '../../states/UserState';
|
||||||
@ -504,14 +505,7 @@ export default function PartDetail() {
|
|||||||
label: t`Build Orders`,
|
label: t`Build Orders`,
|
||||||
icon: <IconTools />,
|
icon: <IconTools />,
|
||||||
hidden: !part.assembly,
|
hidden: !part.assembly,
|
||||||
content: (
|
content: part?.pk ? <BuildOrderTable partId={part.pk} /> : <Skeleton />
|
||||||
<BuildOrderTable
|
|
||||||
params={{
|
|
||||||
part_detail: true,
|
|
||||||
part: part.pk ?? -1
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'used_in',
|
name: 'used_in',
|
||||||
@ -562,15 +556,7 @@ export default function PartDetail() {
|
|||||||
label: t`Sales Orders`,
|
label: t`Sales Orders`,
|
||||||
icon: <IconTruckDelivery />,
|
icon: <IconTruckDelivery />,
|
||||||
hidden: !part.salable,
|
hidden: !part.salable,
|
||||||
content: part.pk ? (
|
content: part.pk ? <SalesOrderTable partId={part.pk} /> : <Skeleton />
|
||||||
<SalesOrderTable
|
|
||||||
params={{
|
|
||||||
part: part.pk ?? -1
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<Skeleton />
|
|
||||||
)
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'scheduling',
|
name: 'scheduling',
|
||||||
@ -647,6 +633,14 @@ export default function PartDetail() {
|
|||||||
);
|
);
|
||||||
}, [part, id]);
|
}, [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(() => {
|
const partActions = useMemo(() => {
|
||||||
// TODO: Disable actions based on user permissions
|
// TODO: Disable actions based on user permissions
|
||||||
return [
|
return [
|
||||||
@ -685,13 +679,8 @@ export default function PartDetail() {
|
|||||||
actions={[
|
actions={[
|
||||||
DuplicateItemAction({}),
|
DuplicateItemAction({}),
|
||||||
EditItemAction({
|
EditItemAction({
|
||||||
onClick: () => {
|
disabled: !user.hasChangeRole(UserRoles.part),
|
||||||
part.pk &&
|
onClick: () => editPart.open()
|
||||||
editPart({
|
|
||||||
part_id: part.pk,
|
|
||||||
callback: refreshInstance
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}),
|
}),
|
||||||
DeleteItemAction({
|
DeleteItemAction({
|
||||||
disabled: part?.active
|
disabled: part?.active
|
||||||
@ -703,6 +692,7 @@ export default function PartDetail() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
{editPart.modal}
|
||||||
<Stack spacing="xs">
|
<Stack spacing="xs">
|
||||||
<LoadingOverlay visible={instanceQuery.isFetching} />
|
<LoadingOverlay visible={instanceQuery.isFetching} />
|
||||||
<PartCategoryTree
|
<PartCategoryTree
|
||||||
|
@ -20,7 +20,6 @@ export default function PurchasingIndex() {
|
|||||||
label: t`Purchase Orders`,
|
label: t`Purchase Orders`,
|
||||||
icon: <IconShoppingCart />,
|
icon: <IconShoppingCart />,
|
||||||
content: <PurchaseOrderTable />
|
content: <PurchaseOrderTable />
|
||||||
// TODO: Add optional "calendar" display here...
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'suppliers',
|
name: 'suppliers',
|
||||||
|
@ -62,11 +62,7 @@ export default function SalesOrderDetail() {
|
|||||||
label: t`Build Orders`,
|
label: t`Build Orders`,
|
||||||
icon: <IconTools />,
|
icon: <IconTools />,
|
||||||
content: order?.pk ? (
|
content: order?.pk ? (
|
||||||
<BuildOrderTable
|
<BuildOrderTable salesOrderId={order.pk} />
|
||||||
params={{
|
|
||||||
sales_order: order.pk
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
) : (
|
) : (
|
||||||
<Skeleton />
|
<Skeleton />
|
||||||
)
|
)
|
||||||
|
Loading…
Reference in New Issue
Block a user