diff --git a/packages/sdk-react/src/components/approvalPolicies/ApprovalPolicyDetails/ApprovalPolicyForm/ApprovalPolicyForm.tsx b/packages/sdk-react/src/components/approvalPolicies/ApprovalPolicyDetails/ApprovalPolicyForm/ApprovalPolicyForm.tsx
index 0320f08ed..f8a2e45c0 100644
--- a/packages/sdk-react/src/components/approvalPolicies/ApprovalPolicyDetails/ApprovalPolicyForm/ApprovalPolicyForm.tsx
+++ b/packages/sdk-react/src/components/approvalPolicies/ApprovalPolicyDetails/ApprovalPolicyForm/ApprovalPolicyForm.tsx
@@ -16,6 +16,10 @@ import {
ApprovalPoliciesOperator,
AmountTuple,
} from '@/components/approvalPolicies/useApprovalPolicyTrigger';
+import {
+ CounterpartAutocomplete,
+ CounterpartsAutocompleteOptionProps,
+} from '@/components/counterparts/CounterpartAutocomplete';
import { getCounterpartName } from '@/components/counterparts/helpers';
import { RHFTextField } from '@/components/RHF/RHFTextField';
import { useMoniteContext } from '@/core/context/MoniteContext';
@@ -45,10 +49,6 @@ import * as yup from 'yup';
import { ConditionsTable } from '../ConditionsTable';
import { RulesTable } from '../RulesTable';
-import {
- AutocompleteCounterparts,
- CounterpartsAutocompleteOptionProps,
-} from './AutocompleteCounterparts';
import { AutocompleteTags } from './AutocompleteTags';
import { AutocompleteUsers } from './AutocompleteUsers';
@@ -919,10 +919,11 @@ export const ApprovalPolicyForm = ({
{(triggerInEdit === 'counterpart_id' ||
(isAddingTrigger &&
currentTriggerType === 'counterpart_id')) && (
-
)}
{(triggerInEdit === 'amount' ||
diff --git a/packages/sdk-react/src/components/approvalPolicies/ApprovalPolicyDetails/ApprovalPolicyForm/AutocompleteCounterparts/AutocompleteCounterparts.tsx b/packages/sdk-react/src/components/approvalPolicies/ApprovalPolicyDetails/ApprovalPolicyForm/AutocompleteCounterparts/AutocompleteCounterparts.tsx
deleted file mode 100644
index 4b35324c3..000000000
--- a/packages/sdk-react/src/components/approvalPolicies/ApprovalPolicyDetails/ApprovalPolicyForm/AutocompleteCounterparts/AutocompleteCounterparts.tsx
+++ /dev/null
@@ -1,216 +0,0 @@
-import { useState, useCallback, useMemo, useEffect } from 'react';
-import { Controller, Control, useFormContext } from 'react-hook-form';
-
-import { getCounterpartName } from '@/components/counterparts/helpers';
-import { CreateCounterpartDialog } from '@/components/receivables/InvoiceDetails/CreateReceivable/sections/components/CreateCounterpartDialog';
-import { useMoniteContext } from '@/core/context/MoniteContext';
-import { useRootElements } from '@/core/context/RootElementsProvider';
-import { t } from '@lingui/macro';
-import { useLingui } from '@lingui/react';
-import AddIcon from '@mui/icons-material/Add';
-import {
- Autocomplete,
- CircularProgress,
- TextField,
- FormHelperText,
- createFilterOptions,
- Button,
-} from '@mui/material';
-
-import type { FormValues } from '../ApprovalPolicyForm';
-
-interface AutocompleteCreatedByProps {
- control: Control;
- name: 'triggers.counterpart_id';
- label: string;
-}
-
-export interface CounterpartsAutocompleteOptionProps {
- id: string;
- label: string;
-}
-
-const COUNTERPART_CREATE_NEW_ID = '__create-new__';
-
-export const AutocompleteCounterparts = ({
- control,
- name,
- label,
-}: AutocompleteCreatedByProps) => {
- const { i18n } = useLingui();
- const { api } = useMoniteContext();
- const { root } = useRootElements();
- const { setValue, getValues } = useFormContext();
- const [inputValue, setInputValue] = useState('');
- const [isCreateCounterpartOpened, setIsCreateCounterpartOpened] =
- useState(false);
- const [newCounterpartId, setNewCounterpartId] = useState(null);
-
- const handleCreateNewCounterpart = useCallback(() => {
- setIsCreateCounterpartOpened(true);
- }, [setIsCreateCounterpartOpened]);
-
- const handleCloseCreateCounterpart = useCallback(() => {
- setIsCreateCounterpartOpened(false);
- }, [setIsCreateCounterpartOpened]);
-
- const {
- data: counterparts,
- isLoading: isCounterpartsLoading,
- refetch,
- } = api.counterparts.getCounterparts.useQuery({
- query: {
- ...(inputValue && { counterpart_name__icontains: inputValue }),
- },
- });
-
- const counterpartsAutocompleteData = useMemo<
- Array
- >(
- () =>
- counterparts?.data.map((counterpart) => ({
- id: counterpart.id,
- label: getCounterpartName(counterpart),
- })) ?? [],
- [counterparts]
- );
-
- const filter = createFilterOptions();
-
- function isCreateNewCounterpartOption(
- counterpartOption: CounterpartsAutocompleteOptionProps | undefined | null
- ): boolean {
- return counterpartOption?.id === COUNTERPART_CREATE_NEW_ID;
- }
-
- useEffect(() => {
- if (newCounterpartId) {
- const currentValues = getValues(name) || [];
-
- const isAlreadyAdded = currentValues.some(
- (counterpart: CounterpartsAutocompleteOptionProps) =>
- counterpart.id === newCounterpartId
- );
-
- if (!isAlreadyAdded) {
- const existingCounterpart = counterpartsAutocompleteData.find(
- (counterpart) => counterpart.id === newCounterpartId
- );
-
- const newCounterpart = {
- id: newCounterpartId,
- label: existingCounterpart
- ? existingCounterpart.label
- : 'New Counterpart',
- };
-
- setValue(name, [...currentValues, newCounterpart]);
- }
- }
- }, [
- newCounterpartId,
- setValue,
- name,
- getValues,
- counterpartsAutocompleteData,
- ]);
-
- return (
- <>
-
- (
- <>
-
- isCreateNewCounterpartOption(counterpartOption)
- ? ''
- : counterpartOption.label
- }
- getOptionKey={(option) => option.id}
- isOptionEqualToValue={(option, value) => option.id === value.id}
- filterOptions={(options, params) => {
- const filtered = filter(options, params);
-
- filtered.unshift({
- id: COUNTERPART_CREATE_NEW_ID,
- label: t(i18n)`Create new counterpart`,
- });
-
- return filtered;
- }}
- onInputChange={(_, newInputValue) => {
- setInputValue(newInputValue);
- }}
- onChange={(_, value) => {
- setValue(name, value);
- refetch();
- }}
- renderInput={(params) => (
-
- {isCounterpartsLoading ? (
-
- ) : null}
- {params.InputProps.endAdornment}
- >
- ),
- }}
- />
- )}
- renderOption={(props, counterpartOption) => {
- return isCreateNewCounterpartOption(counterpartOption) ? (
- }
- fullWidth
- sx={{
- justifyContent: 'flex-start',
- px: 2,
- }}
- onClick={handleCreateNewCounterpart}
- >
- {counterpartOption.label}
-
- ) : (
-
- {counterpartOption.label}
-
- );
- }}
- />
- {error && {error.message}}
- >
- )}
- />
- >
- );
-};
diff --git a/packages/sdk-react/src/components/approvalPolicies/ApprovalPolicyDetails/ApprovalPolicyForm/AutocompleteCounterparts/index.ts b/packages/sdk-react/src/components/approvalPolicies/ApprovalPolicyDetails/ApprovalPolicyForm/AutocompleteCounterparts/index.ts
deleted file mode 100644
index 5557ccfa3..000000000
--- a/packages/sdk-react/src/components/approvalPolicies/ApprovalPolicyDetails/ApprovalPolicyForm/AutocompleteCounterparts/index.ts
+++ /dev/null
@@ -1 +0,0 @@
-export * from './AutocompleteCounterparts';
diff --git a/packages/sdk-react/src/components/counterparts/CounterpartAutocomplete/CounterpartAutocomplete.tsx b/packages/sdk-react/src/components/counterparts/CounterpartAutocomplete/CounterpartAutocomplete.tsx
new file mode 100644
index 000000000..931c48c12
--- /dev/null
+++ b/packages/sdk-react/src/components/counterparts/CounterpartAutocomplete/CounterpartAutocomplete.tsx
@@ -0,0 +1,269 @@
+import { useState, useCallback, useMemo, useEffect } from 'react';
+import {
+ Controller,
+ Control,
+ useFormContext,
+ FieldValues,
+ FieldPath,
+ PathValue,
+} from 'react-hook-form';
+
+import type {
+ DefaultValuesOCRIndividual,
+ DefaultValuesOCROrganization,
+} from '@/components/counterparts/Counterpart.types';
+import { CreateCounterpartDialog } from '@/components/counterparts/CreateCounterpartDialog';
+import { getCounterpartName } from '@/components/counterparts/helpers';
+import { useRootElements } from '@/core/context/RootElementsProvider';
+import { useCounterpartList } from '@/core/queries';
+import { t } from '@lingui/macro';
+import { useLingui } from '@lingui/react';
+import AddIcon from '@mui/icons-material/Add';
+import {
+ Autocomplete,
+ CircularProgress,
+ TextField,
+ FormHelperText,
+ createFilterOptions,
+ Button,
+} from '@mui/material';
+
+export interface CounterpartsAutocompleteOptionProps {
+ id: string;
+ label: string;
+}
+
+const COUNTERPART_CREATE_NEW_ID = '__create-new__';
+
+const filter = createFilterOptions();
+
+function isCreateNewCounterpartOption(
+ counterpartOption: CounterpartsAutocompleteOptionProps | undefined | null
+): boolean {
+ return counterpartOption?.id === COUNTERPART_CREATE_NEW_ID;
+}
+
+interface CounterpartAutocompleteProps {
+ control: Control;
+ name: FieldPath;
+ label: string;
+ disabled?: boolean;
+ required?: boolean;
+ getCounterpartDefaultValues?: (
+ type?: string
+ ) => DefaultValuesOCRIndividual | DefaultValuesOCROrganization;
+ multiple?: boolean;
+}
+
+export const CounterpartAutocomplete = ({
+ control,
+ name,
+ label,
+ required = true,
+ getCounterpartDefaultValues,
+ multiple = false,
+ disabled = false,
+}: CounterpartAutocompleteProps) => {
+ const { i18n } = useLingui();
+ const { root } = useRootElements();
+ const { setValue, getValues } = useFormContext();
+ const [isCreateCounterpartOpened, setIsCreateCounterpartOpened] =
+ useState(false);
+ const [newCounterpartId, setNewCounterpartId] = useState(null);
+ console.log(newCounterpartId, 'newCounterpartId');
+ const handleCreateNewCounterpart = useCallback(() => {
+ setIsCreateCounterpartOpened(true);
+ }, [setIsCreateCounterpartOpened]);
+
+ const handleCloseCreateCounterpart = useCallback(() => {
+ setIsCreateCounterpartOpened(false);
+ }, [setIsCreateCounterpartOpened]);
+
+ const { data: counterparts, isLoading: isCounterpartsLoading } =
+ useCounterpartList();
+
+ const counterpartsAutocompleteData = useMemo<
+ Array
+ >(
+ () =>
+ counterparts?.data.map((counterpart) => ({
+ id: counterpart.id,
+ label: getCounterpartName(counterpart),
+ })) ?? [],
+ [counterparts]
+ );
+
+ useEffect(() => {
+ if (newCounterpartId) {
+ const currentValues = getValues(name);
+ if (multiple) {
+ const values = currentValues ? currentValues : [];
+ const isAlreadyAdded = (
+ values as Array
+ )?.some((counterpart) => counterpart.id === newCounterpartId);
+
+ if (!isAlreadyAdded) {
+ const existingCounterpart = counterpartsAutocompleteData.find(
+ (counterpart) => counterpart.id === newCounterpartId
+ );
+
+ const newCounterpart = {
+ id: newCounterpartId,
+ label: existingCounterpart
+ ? existingCounterpart.label
+ : 'New Counterpart',
+ };
+
+ setValue(name, [
+ ...(values as Array),
+ newCounterpart,
+ ] as PathValue>);
+ }
+ } else {
+ setValue(
+ name,
+ newCounterpartId as PathValue>
+ );
+ }
+ }
+ }, [
+ newCounterpartId,
+ setValue,
+ name,
+ getValues,
+ counterpartsAutocompleteData,
+ multiple,
+ ]);
+
+ return (
+ <>
+
+ {
+ const selectedCounterpart = counterparts?.data.find(
+ (counterpart) => counterpart.id === field.value
+ );
+
+ /**
+ * We have to set `selectedCounterpartOption` to `null`
+ * if `selectedCounterpart` is `null` because
+ * `Autocomplete` component doesn't work with `undefined`
+ */
+ const selectedCounterpartOption = selectedCounterpart
+ ? {
+ id: selectedCounterpart.id,
+ label: getCounterpartName(selectedCounterpart),
+ }
+ : null;
+ return (
+ <>
+
+ isCreateNewCounterpartOption(counterpartOption)
+ ? ''
+ : counterpartOption.label
+ }
+ getOptionKey={(option) => option.id}
+ isOptionEqualToValue={(option, value) => option.id === value.id}
+ filterOptions={(options, params) => {
+ const filtered = filter(options, params);
+
+ filtered.unshift({
+ id: COUNTERPART_CREATE_NEW_ID,
+ label: t(i18n)`Create new counterpart`,
+ });
+
+ return filtered;
+ }}
+ onChange={(_, value) => {
+ if (multiple) {
+ setValue(
+ name,
+ value as PathValue>
+ );
+ } else {
+ setValue(
+ name,
+ (value as CounterpartsAutocompleteOptionProps | null)
+ ?.id as PathValue>
+ );
+ }
+ }}
+ renderInput={(params) => (
+
+ {isCounterpartsLoading ? (
+
+ ) : null}
+ {params.InputProps.endAdornment}
+ >
+ ),
+ }}
+ />
+ )}
+ renderOption={(props, counterpartOption) => {
+ return isCreateNewCounterpartOption(counterpartOption) ? (
+ }
+ fullWidth
+ sx={{
+ justifyContent: 'flex-start',
+ px: 2,
+ }}
+ onClick={handleCreateNewCounterpart}
+ >
+ {counterpartOption.label}
+
+ ) : (
+
+ {counterpartOption.label}
+
+ );
+ }}
+ />
+ {error && {error.message}}
+ >
+ );
+ }}
+ />
+ >
+ );
+};
diff --git a/packages/sdk-react/src/components/counterparts/CounterpartAutocomplete/index.tsx b/packages/sdk-react/src/components/counterparts/CounterpartAutocomplete/index.tsx
new file mode 100644
index 000000000..055e61f8c
--- /dev/null
+++ b/packages/sdk-react/src/components/counterparts/CounterpartAutocomplete/index.tsx
@@ -0,0 +1 @@
+export * from './CounterpartAutocomplete';
diff --git a/packages/sdk-react/src/components/receivables/InvoiceDetails/CreateReceivable/sections/components/CreateCounterpartDialog.tsx b/packages/sdk-react/src/components/counterparts/CreateCounterpartDialog/CreateCounterpartDialog.tsx
similarity index 100%
rename from packages/sdk-react/src/components/receivables/InvoiceDetails/CreateReceivable/sections/components/CreateCounterpartDialog.tsx
rename to packages/sdk-react/src/components/counterparts/CreateCounterpartDialog/CreateCounterpartDialog.tsx
diff --git a/packages/sdk-react/src/components/receivables/InvoiceDetails/CreateReceivable/sections/components/CreateCounterpartDialog.types.ts b/packages/sdk-react/src/components/counterparts/CreateCounterpartDialog/CreateCounterpartDialog.types.ts
similarity index 100%
rename from packages/sdk-react/src/components/receivables/InvoiceDetails/CreateReceivable/sections/components/CreateCounterpartDialog.types.ts
rename to packages/sdk-react/src/components/counterparts/CreateCounterpartDialog/CreateCounterpartDialog.types.ts
diff --git a/packages/sdk-react/src/components/counterparts/CreateCounterpartDialog/index.tsx b/packages/sdk-react/src/components/counterparts/CreateCounterpartDialog/index.tsx
new file mode 100644
index 000000000..1f84ea8b2
--- /dev/null
+++ b/packages/sdk-react/src/components/counterparts/CreateCounterpartDialog/index.tsx
@@ -0,0 +1 @@
+export { CreateCounterpartDialog } from './CreateCounterpartDialog';
diff --git a/packages/sdk-react/src/components/counterparts/index.tsx b/packages/sdk-react/src/components/counterparts/index.tsx
index ab754ac90..3f4defd3a 100644
--- a/packages/sdk-react/src/components/counterparts/index.tsx
+++ b/packages/sdk-react/src/components/counterparts/index.tsx
@@ -1,4 +1,5 @@
export * from './CounterpartsTable';
export * from './CounterpartDetails';
export * from './Counterparts';
+export * from './CounterpartAutocomplete';
export { getCounterpartName } from './helpers';
diff --git a/packages/sdk-react/src/components/payables/PayableDetails/PayableDetailsForm/PayableDetailsForm.tsx b/packages/sdk-react/src/components/payables/PayableDetails/PayableDetailsForm/PayableDetailsForm.tsx
index 88a4d6cff..ecb512f52 100644
--- a/packages/sdk-react/src/components/payables/PayableDetails/PayableDetailsForm/PayableDetailsForm.tsx
+++ b/packages/sdk-react/src/components/payables/PayableDetails/PayableDetailsForm/PayableDetailsForm.tsx
@@ -13,7 +13,7 @@ import {
DefaultValuesOCRIndividual,
DefaultValuesOCROrganization,
} from '@/components/counterparts/Counterpart.types';
-import { CounterpartAutocompleteWithCreate } from '@/components/receivables/InvoiceDetails/CreateReceivable/sections/components/CounterpartAutocompleteWithCreate';
+import { CounterpartAutocomplete } from '@/components/counterparts/CounterpartAutocomplete';
import { useMoniteContext } from '@/core/context/MoniteContext';
import { MoniteScopedProviders } from '@/core/context/MoniteScopedProviders';
import { useRootElements } from '@/core/context/RootElementsProvider';
@@ -286,7 +286,7 @@ const PayableDetailsFormBase = forwardRef<
useEffect(() => {
methods.reset();
- }, [payablesValidations, methods]);
+ }, [payablesValidations, methods.reset, methods]);
useEffect(() => {
reset(prepareDefaultValues(formatFromMinorUnits, payable, lineItems));
@@ -456,7 +456,8 @@ const PayableDetailsFormBase = forwardRef<
/>
)}
/>
- {
return (
- ();
-
-const COUNTERPART_CREATE_NEW_ID = '__create-new__';
-
-function isCreateNewCounterpartOption(
- counterpartOption: CounterpartsAutocompleteOptionProps | undefined | null
-): boolean {
- return counterpartOption?.id === COUNTERPART_CREATE_NEW_ID;
-}
-
-export const CounterpartAutocompleteWithCreate = <
- TFieldValues extends FieldValues
->({
- disabled,
- name,
- label,
- getCounterpartDefaultValues,
- required = true,
-}: {
- disabled?: boolean;
- name: FieldPath;
- label: string;
- getCounterpartDefaultValues?: (
- type?: string
- ) => DefaultValuesOCRIndividual | DefaultValuesOCROrganization;
- required?: boolean;
-}) => {
- const { i18n } = useLingui();
- const { control, setValue } = useFormContext();
-
- const { root } = useRootElements();
-
- const { data: counterparts, isLoading: isCounterpartsLoading } =
- useCounterpartList();
-
- const counterpartsAutocompleteData = useMemo<
- Array
- >(
- () =>
- counterparts
- ? counterparts?.data.map((counterpart) => ({
- id: counterpart.id,
- label: getCounterpartName(counterpart),
- }))
- : [],
- [counterparts]
- );
-
- const [isCreateCounterpartOpened, setIsCreateCounterpartOpened] =
- useState(false);
-
- const handleCreateNewCounterpart = useCallback(() => {
- setIsCreateCounterpartOpened(true);
- }, [setIsCreateCounterpartOpened]);
-
- const [newCounterpartId, setNewCounterpartId] = useState(null);
-
- useEffect(() => {
- if (newCounterpartId) {
- setValue(
- name,
- newCounterpartId as PathValue>
- );
- }
- }, [newCounterpartId, setValue, name]);
-
- return (
- <>
- {
- setIsCreateCounterpartOpened(false);
- }}
- onCreate={setNewCounterpartId}
- getCounterpartDefaultValues={getCounterpartDefaultValues}
- />
- {
- const selectedCounterpart = counterparts?.data.find(
- (counterpart) => counterpart.id === field.value
- );
-
- /**
- * We have to set `selectedCounterpartOption` to `null`
- * if `selectedCounterpart` is `null` because
- * `Autocomplete` component doesn't work with `undefined`
- */
- const selectedCounterpartOption = selectedCounterpart
- ? {
- id: selectedCounterpart.id,
- label: getCounterpartName(selectedCounterpart),
- }
- : null;
-
- return (
- {
- if (isCreateNewCounterpartOption(value)) {
- field.onChange(null);
-
- return;
- }
-
- field.onChange(value?.id);
- }}
- slotProps={{
- popper: {
- container: root,
- },
- }}
- filterOptions={(options, params) => {
- const filtered = filter(options, params);
-
- filtered.unshift({
- id: COUNTERPART_CREATE_NEW_ID,
- label: t(i18n)`Create new counterpart`,
- });
-
- return filtered;
- }}
- renderInput={(params) => (
-
- ) : null,
- }}
- />
- )}
- loading={isCounterpartsLoading || disabled}
- options={counterpartsAutocompleteData}
- getOptionLabel={(counterpartOption) =>
- isCreateNewCounterpartOption(counterpartOption)
- ? ''
- : counterpartOption.label
- }
- isOptionEqualToValue={(option, value) => option.id === value.id}
- selectOnFocus
- clearOnBlur
- handleHomeEndKeys
- renderOption={(props, counterpartOption) =>
- isCreateNewCounterpartOption(counterpartOption) ? (
- }
- fullWidth
- sx={{
- justifyContent: 'flex-start',
- px: 2,
- }}
- onClick={handleCreateNewCounterpart}
- >
- {counterpartOption.label}
-
- ) : (
-
- {counterpartOption.label}
-
- )
- }
- />
- );
- }}
- />
- >
- );
-};