306 lines
7.5 KiB
TypeScript
306 lines
7.5 KiB
TypeScript
import { IconUsers } from '@tabler/icons-react';
|
|
import { useCallback, useEffect, useMemo, useState } from 'react';
|
|
|
|
import { ApiEndpoints } from '@lib/enums/ApiEndpoints';
|
|
import { ModelType } from '@lib/enums/ModelType';
|
|
import { apiUrl } from '@lib/functions/Api';
|
|
import type { ApiFormFieldSet } from '@lib/types/Forms';
|
|
import { t } from '@lingui/core/macro';
|
|
import type {
|
|
StatusCodeInterface,
|
|
StatusCodeListInterface
|
|
} from '../components/render/StatusRenderer';
|
|
import { useApi } from '../contexts/ApiContext';
|
|
import { useGlobalStatusState } from '../states/GlobalStatusState';
|
|
import { useUserState } from '../states/UserState';
|
|
|
|
export function projectCodeFields(): ApiFormFieldSet {
|
|
return {
|
|
code: {},
|
|
description: {},
|
|
responsible: {
|
|
icon: <IconUsers />
|
|
}
|
|
};
|
|
}
|
|
|
|
export function useCustomStateFields(): ApiFormFieldSet {
|
|
// Status codes
|
|
const statusCodes = useGlobalStatusState();
|
|
|
|
// Selected base status class
|
|
const [statusClass, setStatusClass] = useState<string>('');
|
|
|
|
// Construct a list of status options based on the selected status class
|
|
const statusOptions: any[] = useMemo(() => {
|
|
const options: any[] = [];
|
|
|
|
const valuesList = Object.values(statusCodes.status ?? {}).find(
|
|
(value: StatusCodeListInterface) => value.status_class === statusClass
|
|
);
|
|
|
|
Object.values(valuesList?.values ?? {}).forEach(
|
|
(value: StatusCodeInterface) => {
|
|
options.push({
|
|
value: value.key,
|
|
display_name: value.label
|
|
});
|
|
}
|
|
);
|
|
|
|
return options;
|
|
}, [statusCodes, statusClass]);
|
|
|
|
return useMemo(() => {
|
|
return {
|
|
reference_status: {
|
|
onValueChange(value) {
|
|
setStatusClass(value);
|
|
}
|
|
},
|
|
logical_key: {
|
|
field_type: 'choice',
|
|
choices: statusOptions
|
|
},
|
|
key: {},
|
|
name: {},
|
|
label: {},
|
|
color: {},
|
|
model: {}
|
|
};
|
|
}, [statusOptions]);
|
|
}
|
|
|
|
export function customUnitsFields(): ApiFormFieldSet {
|
|
return {
|
|
name: {},
|
|
definition: {},
|
|
symbol: {}
|
|
};
|
|
}
|
|
|
|
export function extraLineItemFields(): ApiFormFieldSet {
|
|
return {
|
|
order: {
|
|
hidden: true
|
|
},
|
|
line: {},
|
|
reference: {},
|
|
description: {},
|
|
quantity: {},
|
|
price: {},
|
|
price_currency: {},
|
|
project_code: {
|
|
description: t`Select project code for this line item`
|
|
},
|
|
notes: {},
|
|
link: {}
|
|
};
|
|
}
|
|
|
|
export function useParameterTemplateFields(): ApiFormFieldSet {
|
|
return useMemo(() => {
|
|
return {
|
|
name: {},
|
|
description: {},
|
|
units: {},
|
|
model_type: {},
|
|
choices: {},
|
|
checkbox: {},
|
|
selectionlist: {
|
|
filters: {
|
|
active: true
|
|
}
|
|
},
|
|
enabled: {}
|
|
};
|
|
}, []);
|
|
}
|
|
|
|
export function useParameterFields({
|
|
modelType,
|
|
modelId
|
|
}: {
|
|
modelType: ModelType;
|
|
modelId: number;
|
|
}): ApiFormFieldSet {
|
|
const api = useApi();
|
|
|
|
const user = useUserState.getState();
|
|
|
|
const templateCreateFields = useParameterTemplateFields();
|
|
|
|
const [selectionListId, setSelectionListId] = useState<number | null>(null);
|
|
|
|
// Valid field choices
|
|
const [choices, setChoices] = useState<any[]>([]);
|
|
|
|
// Field type for "data" input
|
|
const [fieldType, setFieldType] = useState<
|
|
'string' | 'boolean' | 'choice' | 'related field'
|
|
>('string');
|
|
|
|
// Memoized value for the "data" field
|
|
const [data, setData] = useState<string>('');
|
|
|
|
const fetchSelectionEntry = useCallback(
|
|
(value: any) => {
|
|
if (!value || !selectionListId) {
|
|
return null;
|
|
}
|
|
|
|
return api
|
|
.get(apiUrl(ApiEndpoints.selectionentry_list, selectionListId), {
|
|
params: {
|
|
value: value
|
|
}
|
|
})
|
|
.then((response) => {
|
|
if (response.data && response.data.length == 1) {
|
|
return response.data[0];
|
|
} else {
|
|
return null;
|
|
}
|
|
});
|
|
},
|
|
[selectionListId]
|
|
);
|
|
|
|
// Reset the field type and choices when the model changes
|
|
useEffect(() => {
|
|
setSelectionListId(null);
|
|
setFieldType('string');
|
|
setChoices([]);
|
|
setData('');
|
|
}, [modelType, modelId]);
|
|
|
|
return useMemo(() => {
|
|
return {
|
|
model_type: {
|
|
hidden: true,
|
|
value: modelType
|
|
},
|
|
model_id: {
|
|
hidden: true,
|
|
value: modelId
|
|
},
|
|
template: {
|
|
filters: {
|
|
for_model: modelType,
|
|
enabled: true
|
|
},
|
|
onValueChange: (value: any, record: any) => {
|
|
setSelectionListId(record?.selectionlist || null);
|
|
|
|
// Adjust the type of the "data" field based on the selected template
|
|
if (record?.checkbox) {
|
|
// This is a "checkbox" field
|
|
setChoices([]);
|
|
setFieldType('boolean');
|
|
setData('false');
|
|
} else if (record?.choices) {
|
|
const _choices: string[] = record.choices.split(',');
|
|
|
|
if (_choices.length > 0) {
|
|
setChoices(
|
|
_choices.map((choice) => {
|
|
return {
|
|
display_name: choice.trim(),
|
|
value: choice.trim()
|
|
};
|
|
})
|
|
);
|
|
setFieldType('choice');
|
|
} else {
|
|
setChoices([]);
|
|
setFieldType('string');
|
|
}
|
|
} else if (record?.selectionlist) {
|
|
setFieldType('related field');
|
|
} else {
|
|
// Default to a simple string field
|
|
setFieldType('string');
|
|
}
|
|
},
|
|
addCreateFields: user.isStaff() ? templateCreateFields : undefined
|
|
},
|
|
data: {
|
|
value: data,
|
|
onValueChange: (value: any, record: any) => {
|
|
if (fieldType === 'related field' && selectionListId) {
|
|
// For related fields, we need to store the selected primary key value (not the string representation)
|
|
setData(record?.value ?? value);
|
|
} else {
|
|
setData(value);
|
|
}
|
|
},
|
|
type: fieldType,
|
|
field_type: fieldType,
|
|
choices: fieldType === 'choice' ? choices : undefined,
|
|
default: fieldType === 'boolean' ? false : undefined,
|
|
pk_field:
|
|
fieldType === 'related field' && selectionListId
|
|
? 'value'
|
|
: undefined,
|
|
model:
|
|
fieldType === 'related field' && selectionListId
|
|
? ModelType.selectionentry
|
|
: undefined,
|
|
api_url:
|
|
fieldType === 'related field' && selectionListId
|
|
? apiUrl(ApiEndpoints.selectionentry_list, selectionListId)
|
|
: undefined,
|
|
filters:
|
|
fieldType === 'related field'
|
|
? {
|
|
active: true
|
|
}
|
|
: undefined,
|
|
adjustValue: (value: any) => {
|
|
// Coerce boolean value into a string (required by backend)
|
|
|
|
let v: string = value.toString().trim();
|
|
|
|
if (fieldType === 'boolean') {
|
|
if (v.toLowerCase() !== 'true') {
|
|
v = 'false';
|
|
}
|
|
}
|
|
|
|
return v;
|
|
},
|
|
singleFetchFunction: fetchSelectionEntry
|
|
},
|
|
note: {}
|
|
};
|
|
}, [
|
|
data,
|
|
modelType,
|
|
fieldType,
|
|
choices,
|
|
modelId,
|
|
selectionListId,
|
|
templateCreateFields,
|
|
user
|
|
]);
|
|
}
|
|
|
|
export function selectionListFields(): ApiFormFieldSet {
|
|
return {
|
|
name: {},
|
|
description: {},
|
|
active: {},
|
|
source_plugin: {},
|
|
source_string: {}
|
|
};
|
|
}
|
|
|
|
export function selectionEntryFields(): ApiFormFieldSet {
|
|
return {
|
|
value: {},
|
|
label: {},
|
|
description: {},
|
|
active: {}
|
|
};
|
|
}
|