diff --git a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownOptionSelect.tsx b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownOptionSelect.tsx
index 24a7fb87e2..b94ba55f99 100644
--- a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownOptionSelect.tsx
+++ b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownOptionSelect.tsx
@@ -18,6 +18,7 @@ import { selectedItemIdComponentState } from '@/ui/layout/selectable-list/states
import { useHotkeysOnFocusedElement } from '@/ui/utilities/hotkey/hooks/useHotkeysOnFocusedElement';
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
+import { t } from '@lingui/core/macro';
import { isNonEmptyString } from '@sniptt/guards';
import { MAX_OPTIONS_TO_DISPLAY } from 'twenty-shared/constants';
import { isDefined } from 'twenty-shared/utils';
@@ -145,6 +146,7 @@ export const ObjectFilterDropdownOptionSelect = ({
);
const showNoResult = optionsInDropdown?.length === 0;
+
const objectRecordsIds = optionsInDropdown.map((option) => option.id);
return (
@@ -155,7 +157,7 @@ export const ObjectFilterDropdownOptionSelect = ({
>
{showNoResult ? (
-
+
) : (
optionsInDropdown?.map((option) => (
{
+ const { objectNamePlural } = useParams();
+ const navigateSettings = useNavigateSettings();
+ const setNavigationMemorizedUrl = useSetRecoilState(
+ navigationMemorizedUrlState,
+ );
+
+ const setShouldNavigateBackToMemorizedUrlOnSave = useSetRecoilState(
+ shouldNavigateBackToMemorizedUrlOnSaveState,
+ );
+
+ const addSelectOption = useCallback(
+ (optionName: string) => {
+ if (!fieldName || !objectNamePlural) return;
+
+ setNavigationMemorizedUrl(
+ window.location.pathname + window.location.search,
+ );
+ setShouldNavigateBackToMemorizedUrlOnSave(true);
+
+ navigateSettings(
+ SettingsPath.ObjectFieldEdit,
+ { objectNamePlural, fieldName },
+ { newOption: optionName },
+ );
+ },
+ [
+ fieldName,
+ objectNamePlural,
+ navigateSettings,
+ setNavigationMemorizedUrl,
+ setShouldNavigateBackToMemorizedUrlOnSave,
+ ],
+ );
+
+ return { addSelectOption };
+};
diff --git a/packages/twenty-front/src/modules/object-record/record-field/ui/meta-types/hooks/useCanAddSelectOption.ts b/packages/twenty-front/src/modules/object-record/record-field/ui/meta-types/hooks/useCanAddSelectOption.ts
new file mode 100644
index 0000000000..83df7c5465
--- /dev/null
+++ b/packages/twenty-front/src/modules/object-record/record-field/ui/meta-types/hooks/useCanAddSelectOption.ts
@@ -0,0 +1,19 @@
+import { useHasPermissionFlag } from '@/settings/roles/hooks/useHasPermissionFlag';
+import { isNonEmptyString } from '@sniptt/guards';
+import { useParams } from 'react-router-dom';
+import { PermissionFlagType } from '~/generated-metadata/graphql';
+
+export const useCanAddSelectOption = (fieldName: string) => {
+ const { objectNamePlural } = useParams();
+
+ const userHasPermissionToEditDataModel = useHasPermissionFlag(
+ PermissionFlagType.DATA_MODEL,
+ );
+
+ const canAddSelectOption =
+ userHasPermissionToEditDataModel &&
+ isNonEmptyString(fieldName) &&
+ isNonEmptyString(objectNamePlural);
+
+ return { canAddSelectOption };
+};
diff --git a/packages/twenty-front/src/modules/object-record/record-field/ui/meta-types/input/components/MultiSelectFieldInput.tsx b/packages/twenty-front/src/modules/object-record/record-field/ui/meta-types/input/components/MultiSelectFieldInput.tsx
index 42bf337a1a..572f6e82b3 100644
--- a/packages/twenty-front/src/modules/object-record/record-field/ui/meta-types/input/components/MultiSelectFieldInput.tsx
+++ b/packages/twenty-front/src/modules/object-record/record-field/ui/meta-types/input/components/MultiSelectFieldInput.tsx
@@ -1,15 +1,22 @@
import { FieldInputEventContext } from '@/object-record/record-field/ui/contexts/FieldInputEventContext';
+import { useAddSelectOption } from '@/object-record/record-field/ui/meta-types/hooks/useAddSelectOption';
+import { useCanAddSelectOption } from '@/object-record/record-field/ui/meta-types/hooks/useCanAddSelectOption';
import { useMultiSelectField } from '@/object-record/record-field/ui/meta-types/hooks/useMultiSelectField';
import { SELECT_FIELD_INPUT_SELECTABLE_LIST_COMPONENT_INSTANCE_ID } from '@/object-record/record-field/ui/meta-types/input/constants/SelectFieldInputSelectableListComponentInstanceId';
import { RecordFieldComponentInstanceContext } from '@/object-record/record-field/ui/states/contexts/RecordFieldComponentInstanceContext';
import { type FieldMultiSelectValue } from '@/object-record/record-field/ui/types/FieldMetadata';
-
import { MultiSelectInput } from '@/ui/field/input/components/MultiSelectInput';
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
import { useContext } from 'react';
export const MultiSelectFieldInput = () => {
const { fieldDefinition, draftValue, setDraftValue } = useMultiSelectField();
+ const { addSelectOption } = useAddSelectOption(
+ fieldDefinition?.metadata?.fieldName,
+ );
+ const { canAddSelectOption } = useCanAddSelectOption(
+ fieldDefinition?.metadata?.fieldName,
+ );
const { onSubmit } = useContext(FieldInputEventContext);
@@ -25,6 +32,13 @@ export const MultiSelectFieldInput = () => {
onSubmit?.({ newValue: draftValue });
};
+ const handleAddSelectOption = (optionName: string) => {
+ if (!canAddSelectOption) {
+ return;
+ }
+ addSelectOption(optionName);
+ };
+
return (
{
onCancel={handleCancel}
onOptionSelected={handleOptionSelected}
values={draftValue}
+ onAddSelectOption={handleAddSelectOption}
/>
);
};
diff --git a/packages/twenty-front/src/modules/object-record/record-field/ui/meta-types/input/components/SelectFieldInput.tsx b/packages/twenty-front/src/modules/object-record/record-field/ui/meta-types/input/components/SelectFieldInput.tsx
index 53a1feb384..83dfa8ab1e 100644
--- a/packages/twenty-front/src/modules/object-record/record-field/ui/meta-types/input/components/SelectFieldInput.tsx
+++ b/packages/twenty-front/src/modules/object-record/record-field/ui/meta-types/input/components/SelectFieldInput.tsx
@@ -1,5 +1,7 @@
import { FieldInputEventContext } from '@/object-record/record-field/ui/contexts/FieldInputEventContext';
import { useClearField } from '@/object-record/record-field/ui/hooks/useClearField';
+import { useAddSelectOption } from '@/object-record/record-field/ui/meta-types/hooks/useAddSelectOption';
+import { useCanAddSelectOption } from '@/object-record/record-field/ui/meta-types/hooks/useCanAddSelectOption';
import { useSelectField } from '@/object-record/record-field/ui/meta-types/hooks/useSelectField';
import { SELECT_FIELD_INPUT_SELECTABLE_LIST_COMPONENT_INSTANCE_ID } from '@/object-record/record-field/ui/meta-types/input/constants/SelectFieldInputSelectableListComponentInstanceId';
import { RecordFieldComponentInstanceContext } from '@/object-record/record-field/ui/states/contexts/RecordFieldComponentInstanceContext';
@@ -14,6 +16,12 @@ import { type SelectOption } from 'twenty-ui/input';
export const SelectFieldInput = () => {
const { fieldDefinition, fieldValue } = useSelectField();
+ const { addSelectOption } = useAddSelectOption(
+ fieldDefinition?.metadata?.fieldName,
+ );
+ const { canAddSelectOption } = useCanAddSelectOption(
+ fieldDefinition?.metadata?.fieldName,
+ );
const { onCancel, onSubmit } = useContext(FieldInputEventContext);
@@ -37,6 +45,13 @@ export const SelectFieldInput = () => {
onCancel?.();
};
+ const handleAddSelectOption = (optionName: string) => {
+ if (!canAddSelectOption) {
+ return;
+ }
+ addSelectOption(optionName);
+ };
+
const handleSubmit = (option: SelectOption) => {
onSubmit?.({ newValue: option.value });
@@ -82,6 +97,7 @@ export const SelectFieldInput = () => {
fieldDefinition.metadata.isNullable ? handleClearField : undefined
}
clearLabel={fieldDefinition.label}
+ onAddSelectOption={handleAddSelectOption}
/>
);
};
diff --git a/packages/twenty-front/src/modules/settings/data-model/fields/forms/select/components/AddSelectOptionMenuItem.tsx b/packages/twenty-front/src/modules/settings/data-model/fields/forms/select/components/AddSelectOptionMenuItem.tsx
new file mode 100644
index 0000000000..82ae9033ec
--- /dev/null
+++ b/packages/twenty-front/src/modules/settings/data-model/fields/forms/select/components/AddSelectOptionMenuItem.tsx
@@ -0,0 +1,37 @@
+import { t } from '@lingui/core/macro';
+import { isNonEmptyString } from '@sniptt/guards';
+import { isDefined } from 'twenty-shared/utils';
+import { IconPlus } from 'twenty-ui/display';
+import { MenuItem } from 'twenty-ui/navigation';
+
+type AddSelectOptionMenuItemProps = {
+ name: string;
+ onAddSelectOption?: (optionName: string) => void;
+};
+
+export const AddSelectOptionMenuItem = ({
+ name,
+ onAddSelectOption,
+}: AddSelectOptionMenuItemProps) => {
+ const trimmedName = name.trim();
+ const showAddOption =
+ isNonEmptyString(trimmedName) && isDefined(onAddSelectOption);
+
+ const handleClick = () => {
+ if (isDefined(onAddSelectOption)) {
+ onAddSelectOption(trimmedName);
+ }
+ };
+
+ if (!showAddOption) {
+ return null;
+ }
+
+ return (
+
+ );
+};
diff --git a/packages/twenty-front/src/modules/settings/data-model/fields/forms/select/components/SettingsDataModelFieldSelectForm.tsx b/packages/twenty-front/src/modules/settings/data-model/fields/forms/select/components/SettingsDataModelFieldSelectForm.tsx
index afeafb3503..a5eebe77d6 100644
--- a/packages/twenty-front/src/modules/settings/data-model/fields/forms/select/components/SettingsDataModelFieldSelectForm.tsx
+++ b/packages/twenty-front/src/modules/settings/data-model/fields/forms/select/components/SettingsDataModelFieldSelectForm.tsx
@@ -20,7 +20,10 @@ import { applySimpleQuotesToString } from '~/utils/string/applySimpleQuotesToStr
import { AdvancedSettingsWrapper } from '@/settings/components/AdvancedSettingsWrapper';
import { isAdvancedModeEnabledState } from '@/ui/navigation/navigation-drawer/states/isAdvancedModeEnabledState';
import { t } from '@lingui/core/macro';
+import { useEffect, useState } from 'react';
+import { useSearchParams } from 'react-router-dom';
import { useRecoilValue } from 'recoil';
+import { isDefined } from 'twenty-shared/utils';
import { IconPlus, IconPoint } from 'twenty-ui/display';
import { LightButton } from 'twenty-ui/input';
import { CardContent, CardFooter } from 'twenty-ui/layout';
@@ -114,8 +117,11 @@ export const SettingsDataModelFieldSelectForm = ({
useSelectSettingsFormInitialValues({
fieldMetadataId: existingFieldMetadataId,
});
+
const isAdvancedModeEnabled = useRecoilValue(isAdvancedModeEnabledState);
+ const [searchParams] = useSearchParams();
+
const {
control,
setValue: setFormValue,
@@ -123,6 +129,21 @@ export const SettingsDataModelFieldSelectForm = ({
getValues,
} = useFormContext();
+ const [hasAppliedNewOption, setHasAppliedNewOption] = useState(false);
+
+ useEffect(() => {
+ const newOptionValue = searchParams.get('newOption');
+
+ if (isDefined(newOptionValue) && !hasAppliedNewOption) {
+ const newOption = generateNewSelectOption(initialOptions, newOptionValue);
+
+ const optionsWithNew = [...initialOptions, newOption];
+
+ setFormValue('options', optionsWithNew, { shouldDirty: true });
+ setHasAppliedNewOption(true);
+ }
+ }, [searchParams, hasAppliedNewOption, initialOptions, setFormValue]);
+
const handleDragEnd = (
values: FieldMetadataItemOption[],
result: DropResult,
diff --git a/packages/twenty-front/src/modules/settings/data-model/fields/forms/select/utils/generateNewSelectOption.ts b/packages/twenty-front/src/modules/settings/data-model/fields/forms/select/utils/generateNewSelectOption.ts
index f3872e5172..4392d72562 100644
--- a/packages/twenty-front/src/modules/settings/data-model/fields/forms/select/utils/generateNewSelectOption.ts
+++ b/packages/twenty-front/src/modules/settings/data-model/fields/forms/select/utils/generateNewSelectOption.ts
@@ -7,9 +7,9 @@ import { computeOptionValueFromLabel } from '~/pages/settings/data-model/utils/c
export const generateNewSelectOption = (
options: FieldMetadataItemOption[],
+ label?: string,
): FieldMetadataItemOption => {
- const newOptionLabel = generateNewSelectOptionLabel(options);
-
+ const newOptionLabel = label ?? generateNewSelectOptionLabel(options);
return {
color: getNextThemeColor(options[options.length - 1]?.color),
id: v4(),
diff --git a/packages/twenty-front/src/modules/ui/field/input/components/MultiSelectInput.tsx b/packages/twenty-front/src/modules/ui/field/input/components/MultiSelectInput.tsx
index be64184b63..3d601681bf 100644
--- a/packages/twenty-front/src/modules/ui/field/input/components/MultiSelectInput.tsx
+++ b/packages/twenty-front/src/modules/ui/field/input/components/MultiSelectInput.tsx
@@ -7,6 +7,7 @@ import { DropdownMenuSearchInput } from '@/ui/layout/dropdown/components/Dropdow
import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator';
import { SelectableList } from '@/ui/layout/selectable-list/components/SelectableList';
+import { AddSelectOptionMenuItem } from '@/settings/data-model/fields/forms/select/components/AddSelectOptionMenuItem';
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem';
import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectableList';
@@ -14,7 +15,7 @@ import { selectedItemIdComponentState } from '@/ui/layout/selectable-list/states
import { useHotkeysOnFocusedElement } from '@/ui/utilities/hotkey/hooks/useHotkeysOnFocusedElement';
import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside';
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
-import { useLingui } from '@lingui/react/macro';
+import { t } from '@lingui/core/macro';
import { isDefined } from 'twenty-shared/utils';
import { type SelectOption } from 'twenty-ui/input';
import { MenuItem, MenuItemMultiSelectTag } from 'twenty-ui/navigation';
@@ -29,6 +30,7 @@ type MultiSelectInputProps = {
options: SelectOption[];
onOptionSelected: (value: FieldMultiSelectValue) => void;
dropdownWidth?: number;
+ onAddSelectOption?: (optionName: string) => void;
};
export const MultiSelectInput = ({
@@ -39,9 +41,8 @@ export const MultiSelectInput = ({
onCancel,
onOptionSelected,
dropdownWidth,
+ onAddSelectOption,
}: MultiSelectInputProps) => {
- const { t } = useLingui();
-
const { resetSelectedItem } = useSelectableList(
selectableListComponentInstanceId,
);
@@ -50,6 +51,7 @@ export const MultiSelectInput = ({
selectedItemIdComponentState,
selectableListComponentInstanceId,
);
+
const [searchFilter, setSearchFilter] = useState('');
const containerRef = useRef(null);
@@ -129,7 +131,7 @@ export const MultiSelectInput = ({
{filteredOptionsInDropDown.length === 0 ? (
-
+
) : (
filteredOptionsInDropDown.map((option) => {
return (
@@ -156,6 +158,19 @@ export const MultiSelectInput = ({
})
)}
+ {onAddSelectOption &&
+ searchFilter &&
+ filteredOptionsInDropDown.length === 0 && (
+ <>
+
+
+
+
+ >
+ )}
);
diff --git a/packages/twenty-front/src/modules/ui/field/input/components/SelectInput.tsx b/packages/twenty-front/src/modules/ui/field/input/components/SelectInput.tsx
index 9fcdb1a41c..5942d7521e 100644
--- a/packages/twenty-front/src/modules/ui/field/input/components/SelectInput.tsx
+++ b/packages/twenty-front/src/modules/ui/field/input/components/SelectInput.tsx
@@ -14,6 +14,7 @@ type SelectInputProps = {
onFilterChange?: ((filteredOptions: SelectOption[]) => void) | undefined;
onClear?: (() => void) | undefined;
clearLabel?: string;
+ onAddSelectOption?: (optionName: string) => void;
};
export const SelectInput = ({
@@ -27,6 +28,7 @@ export const SelectInput = ({
onFilterChange,
onClear,
clearLabel,
+ onAddSelectOption,
}: SelectInputProps) => {
return (
);
diff --git a/packages/twenty-front/src/modules/ui/input/components/SelectInput.tsx b/packages/twenty-front/src/modules/ui/input/components/SelectInput.tsx
index 1e25318ee8..c8d314b40f 100644
--- a/packages/twenty-front/src/modules/ui/input/components/SelectInput.tsx
+++ b/packages/twenty-front/src/modules/ui/input/components/SelectInput.tsx
@@ -1,3 +1,4 @@
+import { AddSelectOptionMenuItem } from '@/settings/data-model/fields/forms/select/components/AddSelectOptionMenuItem';
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
import { DropdownMenuSearchInput } from '@/ui/layout/dropdown/components/DropdownMenuSearchInput';
@@ -24,6 +25,7 @@ interface SelectInputProps {
onClear?: () => void;
clearLabel?: string;
focusId: string;
+ onAddSelectOption?: (optionName: string) => void;
}
export const SelectInput = ({
@@ -34,6 +36,7 @@ export const SelectInput = ({
onCancel,
defaultOption,
onFilterChange,
+ onAddSelectOption,
}: SelectInputProps) => {
const containerRef = useRef(null);
@@ -144,6 +147,17 @@ export const SelectInput = ({
);
})}
+ {onAddSelectOption && searchFilter && optionsToSelect.length === 0 && (
+ <>
+
+
+
+
+ >
+ )}
);
};
diff --git a/packages/twenty-front/src/modules/ui/navigation/states/shouldNavigateBackToMemorizedUrlOnSaveState.ts b/packages/twenty-front/src/modules/ui/navigation/states/shouldNavigateBackToMemorizedUrlOnSaveState.ts
new file mode 100644
index 0000000000..40220c21f8
--- /dev/null
+++ b/packages/twenty-front/src/modules/ui/navigation/states/shouldNavigateBackToMemorizedUrlOnSaveState.ts
@@ -0,0 +1,8 @@
+import { createState } from 'twenty-ui/utilities';
+
+export const shouldNavigateBackToMemorizedUrlOnSaveState = createState(
+ {
+ key: 'shouldNavigateBackToMemorizedUrlOnSaveState',
+ defaultValue: false,
+ },
+);
diff --git a/packages/twenty-front/src/pages/settings/data-model/SettingsObjectFieldEdit.tsx b/packages/twenty-front/src/pages/settings/data-model/SettingsObjectFieldEdit.tsx
index 392e452317..aa950d0fb6 100644
--- a/packages/twenty-front/src/pages/settings/data-model/SettingsObjectFieldEdit.tsx
+++ b/packages/twenty-front/src/pages/settings/data-model/SettingsObjectFieldEdit.tsx
@@ -3,7 +3,7 @@ import omit from 'lodash.omit';
import pick from 'lodash.pick';
import { useEffect, useState } from 'react';
import { FormProvider, useForm } from 'react-hook-form';
-import { useParams } from 'react-router-dom';
+import { useNavigate, useParams } from 'react-router-dom';
import { type z } from 'zod';
import { useFieldMetadataItem } from '@/object-metadata/hooks/useFieldMetadataItem';
@@ -23,8 +23,11 @@ import { settingsFieldFormSchema } from '@/settings/data-model/fields/forms/vali
import { type SettingsFieldType } from '@/settings/data-model/types/SettingsFieldType';
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
import { SubMenuTopBarContainer } from '@/ui/layout/page/components/SubMenuTopBarContainer';
+import { navigationMemorizedUrlState } from '@/ui/navigation/states/navigationMemorizedUrlState';
+import { shouldNavigateBackToMemorizedUrlOnSaveState } from '@/ui/navigation/states/shouldNavigateBackToMemorizedUrlOnSaveState';
import { ApolloError } from '@apollo/client';
import { useLingui } from '@lingui/react/macro';
+import { useRecoilState } from 'recoil';
import { AppPath, SettingsPath } from 'twenty-shared/types';
import { getSettingsPath, isDefined } from 'twenty-shared/utils';
import { H2Title, IconArchive, IconArchiveOff } from 'twenty-ui/display';
@@ -45,6 +48,17 @@ export const SettingsObjectFieldEdit = () => {
const navigateApp = useNavigateApp();
const { t } = useLingui();
+ const navigate = useNavigate();
+
+ const [navigationMemorizedUrl, setNavigationMemorizedUrl] = useRecoilState(
+ navigationMemorizedUrlState,
+ );
+
+ const [
+ shouldNavigateBackToMemorizedUrlOnSave,
+ setShouldNavigateBackToMemorizedUrlOnSave,
+ ] = useRecoilState(shouldNavigateBackToMemorizedUrlOnSaveState);
+
const { enqueueErrorSnackBar } = useSnackBar();
const { objectNamePlural = '', fieldName = '' } = useParams();
@@ -141,9 +155,7 @@ export const SettingsObjectFieldEdit = () => {
updatePayload: formattedInput,
});
- navigateSettings(SettingsPath.ObjectDetail, {
- objectNamePlural,
- });
+ navigateBackOrToSettings();
}
} catch (error) {
enqueueErrorSnackBar({
@@ -152,6 +164,28 @@ export const SettingsObjectFieldEdit = () => {
}
};
+ const navigateBackOrToSettings = () => {
+ if (
+ shouldNavigateBackToMemorizedUrlOnSave &&
+ isDefined(navigationMemorizedUrl)
+ ) {
+ navigate(navigationMemorizedUrl, { replace: true });
+
+ setShouldNavigateBackToMemorizedUrlOnSave(false);
+ setNavigationMemorizedUrl('/');
+
+ return;
+ }
+
+ navigateSettings(SettingsPath.ObjectDetail, {
+ objectNamePlural,
+ });
+ };
+
+ const handleCancel = () => {
+ navigateBackOrToSettings();
+ };
+
const handleDeactivate = async () => {
await deactivateMetadataField(fieldMetadataItem.id, objectMetadataItem.id);
navigateSettings(SettingsPath.ObjectDetail, {
@@ -196,11 +230,7 @@ export const SettingsObjectFieldEdit = () => {
isLoading={isSubmitting}
isSaveDisabled={!canSave}
isCancelDisabled={isSubmitting}
- onCancel={() =>
- navigateSettings(SettingsPath.ObjectDetail, {
- objectNamePlural,
- })
- }
+ onCancel={handleCancel}
onSave={formConfig.handleSubmit(handleSave)}
/>
}