diff --git a/front/src/modules/companies/components/CompanyPicker.tsx b/front/src/modules/companies/components/CompanyPicker.tsx index 53bf48090..9ecc7b509 100644 --- a/front/src/modules/companies/components/CompanyPicker.tsx +++ b/front/src/modules/companies/components/CompanyPicker.tsx @@ -11,16 +11,24 @@ export type CompanyPickerProps = { companyId: string | null; onSubmit: (newCompanyId: EntityForSelect | null) => void; onCancel?: () => void; + initialSearchFilter?: string | null; }; export const CompanyPicker = ({ companyId, onSubmit, onCancel, + initialSearchFilter, }: CompanyPickerProps) => { const [relationPickerSearchFilter, setRelationPickerSearchFilter] = useRecoilScopedState(relationPickerSearchFilterScopedState); + useEffect(() => { + if (initialSearchFilter) { + setRelationPickerSearchFilter(initialSearchFilter); + } + }, [initialSearchFilter, setRelationPickerSearchFilter]); + const companies = useFilteredSearchCompanyQuery({ searchFilter: relationPickerSearchFilter, selectedIds: companyId ? [companyId] : [], @@ -32,10 +40,6 @@ export const CompanyPicker = ({ onSubmit(selectedCompany ?? null); }; - useEffect(() => { - setRelationPickerSearchFilter(''); - }, [setRelationPickerSearchFilter]); - return ( void; onCreate?: () => void; excludePersonIds?: string[]; + initialSearchFilter?: string | null; }; export type PersonForSelect = EntityForSelect & { @@ -26,10 +29,14 @@ export const PeoplePicker = ({ onCancel, onCreate, excludePersonIds, + initialSearchFilter, }: PeoplePickerProps) => { - const [relationPickerSearchFilter] = useRecoilScopedState( - relationPickerSearchFilterScopedState, - ); + const [relationPickerSearchFilter, setRelationPickerSearchFilter] = + useRecoilScopedState(relationPickerSearchFilterScopedState); + + useEffect(() => { + setRelationPickerSearchFilter(initialSearchFilter ?? ''); + }, [initialSearchFilter, setRelationPickerSearchFilter]); const queryFilters = [ { diff --git a/front/src/modules/ui/input/relation-picker/hooks/useEntitySelectSearch.ts b/front/src/modules/ui/input/relation-picker/hooks/useEntitySelectSearch.ts index e9a15af31..551b9e7d8 100644 --- a/front/src/modules/ui/input/relation-picker/hooks/useEntitySelectSearch.ts +++ b/front/src/modules/ui/input/relation-picker/hooks/useEntitySelectSearch.ts @@ -1,4 +1,3 @@ -import { useEffect } from 'react'; import debounce from 'lodash.debounce'; import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState'; @@ -31,10 +30,6 @@ export const useEntitySelectSearch = () => { setRelationPickerPreselectedId(''); }; - useEffect(() => { - setRelationPickerSearchFilter(''); - }, [setRelationPickerSearchFilter]); - return { searchFilter: relationPickerSearchFilter, handleSearchFilterChange, diff --git a/front/src/modules/ui/object/field/hooks/useFieldInitialValue.ts b/front/src/modules/ui/object/field/hooks/useFieldInitialValue.ts new file mode 100644 index 000000000..b228b1b98 --- /dev/null +++ b/front/src/modules/ui/object/field/hooks/useFieldInitialValue.ts @@ -0,0 +1,18 @@ +import { useContext } from 'react'; +import { useRecoilValue } from 'recoil'; + +import { FieldContext } from '../contexts/FieldContext'; +import { entityFieldInitialValueFamilyState } from '../states/entityFieldInitialValueFamilyState'; + +export const useFieldInitialValue = () => { + const { entityId, fieldDefinition } = useContext(FieldContext); + + const fieldInitialValue = useRecoilValue( + entityFieldInitialValueFamilyState({ + fieldId: fieldDefinition.fieldId, + entityId, + }), + ); + + return fieldInitialValue; +}; diff --git a/front/src/modules/ui/object/field/hooks/useGetButtonIcon.ts b/front/src/modules/ui/object/field/hooks/useGetButtonIcon.ts index 6e7d009ec..47f209b56 100644 --- a/front/src/modules/ui/object/field/hooks/useGetButtonIcon.ts +++ b/front/src/modules/ui/object/field/hooks/useGetButtonIcon.ts @@ -11,6 +11,9 @@ import { isFieldURL } from '../types/guards/isFieldURL'; export const useGetButtonIcon = (): IconComponent | undefined => { const { fieldDefinition } = useContext(FieldContext); + + if (!fieldDefinition) return undefined; + if ( isFieldURL(fieldDefinition) || isFieldEmail(fieldDefinition) || diff --git a/front/src/modules/ui/object/field/meta-types/hooks/useChipField.ts b/front/src/modules/ui/object/field/meta-types/hooks/useChipField.ts index dffefaa7b..616c7b7d0 100644 --- a/front/src/modules/ui/object/field/meta-types/hooks/useChipField.ts +++ b/front/src/modules/ui/object/field/meta-types/hooks/useChipField.ts @@ -2,6 +2,7 @@ import { useContext } from 'react'; import { useRecoilState } from 'recoil'; import { FieldContext } from '../../contexts/FieldContext'; +import { useFieldInitialValue } from '../../hooks/useFieldInitialValue'; import { entityFieldsFamilySelector } from '../../states/selectors/entityFieldsFamilySelector'; import { assertFieldMetadata } from '../../types/guards/assertFieldMetadata'; import { isFieldChip } from '../../types/guards/isFieldChip'; @@ -30,11 +31,25 @@ export const useChipField = () => { const entityType = fieldDefinition.metadata.relationType; + const fieldInitialValue = useFieldInitialValue(); + + const initialContentValue = fieldInitialValue?.isEmpty + ? '' + : fieldInitialValue?.value ?? contentFieldValue; + + const initialAvatarValue = fieldInitialValue?.isEmpty + ? '' + : fieldInitialValue?.value + ? '' + : avatarFieldValue; + return { fieldDefinition, contentFieldValue, + initialContentValue, setContentFieldValue, avatarFieldValue, + initialAvatarValue, setAvatarFieldValue, entityType, entityId, diff --git a/front/src/modules/ui/object/field/meta-types/hooks/useDoubleTextChipField.ts b/front/src/modules/ui/object/field/meta-types/hooks/useDoubleTextChipField.ts index cefda738a..42c789bf9 100644 --- a/front/src/modules/ui/object/field/meta-types/hooks/useDoubleTextChipField.ts +++ b/front/src/modules/ui/object/field/meta-types/hooks/useDoubleTextChipField.ts @@ -2,6 +2,7 @@ import { useContext } from 'react'; import { useRecoilState } from 'recoil'; import { FieldContext } from '../../contexts/FieldContext'; +import { useFieldInitialValue } from '../../hooks/useFieldInitialValue'; import { entityFieldsFamilySelector } from '../../states/selectors/entityFieldsFamilySelector'; import { assertFieldMetadata } from '../../types/guards/assertFieldMetadata'; import { isFieldDoubleTextChip } from '../../types/guards/isFieldDoubleTextChip'; @@ -40,6 +41,24 @@ export const useDoubleTextChipField = () => { const entityType = fieldDefinition.metadata.entityType; + const fieldInitialValue = useFieldInitialValue(); + + const initialFirstValue = fieldInitialValue?.isEmpty + ? '' + : fieldInitialValue?.value ?? firstValue; + + const initialSecondValue = fieldInitialValue?.isEmpty + ? '' + : fieldInitialValue?.value + ? '' + : secondValue; + + const initialAvatarUrl = fieldInitialValue?.isEmpty + ? '' + : fieldInitialValue?.value + ? '' + : avatarUrl; + return { fieldDefinition, avatarUrl, @@ -52,5 +71,8 @@ export const useDoubleTextChipField = () => { entityType, entityId, hotkeyScope, + initialAvatarUrl, + initialFirstValue, + initialSecondValue, }; }; diff --git a/front/src/modules/ui/object/field/meta-types/hooks/useDoubleTextField.ts b/front/src/modules/ui/object/field/meta-types/hooks/useDoubleTextField.ts index d4d0690b4..d60a6f041 100644 --- a/front/src/modules/ui/object/field/meta-types/hooks/useDoubleTextField.ts +++ b/front/src/modules/ui/object/field/meta-types/hooks/useDoubleTextField.ts @@ -2,6 +2,7 @@ import { useContext } from 'react'; import { useRecoilState } from 'recoil'; import { FieldContext } from '../../contexts/FieldContext'; +import { useFieldInitialValue } from '../../hooks/useFieldInitialValue'; import { entityFieldsFamilySelector } from '../../states/selectors/entityFieldsFamilySelector'; import { assertFieldMetadata } from '../../types/guards/assertFieldMetadata'; import { isFieldDoubleText } from '../../types/guards/isFieldDoubleText'; @@ -25,6 +26,18 @@ export const useDoubleTextField = () => { }), ); + const fieldInitialValue = useFieldInitialValue(); + + const initialFirstValue = fieldInitialValue?.isEmpty + ? '' + : fieldInitialValue?.value ?? firstValue; + + const initialSecondValue = fieldInitialValue?.isEmpty + ? '' + : fieldInitialValue?.value + ? '' + : secondValue; + const fullValue = [firstValue, secondValue].filter(Boolean).join(' '); return { @@ -32,6 +45,8 @@ export const useDoubleTextField = () => { secondValue, setSecondValue, firstValue, + initialFirstValue, + initialSecondValue, setFirstValue, fullValue, hotkeyScope, diff --git a/front/src/modules/ui/object/field/meta-types/hooks/useEmailField.ts b/front/src/modules/ui/object/field/meta-types/hooks/useEmailField.ts index 2402b28f0..6dbaccfad 100644 --- a/front/src/modules/ui/object/field/meta-types/hooks/useEmailField.ts +++ b/front/src/modules/ui/object/field/meta-types/hooks/useEmailField.ts @@ -2,6 +2,7 @@ import { useContext } from 'react'; import { useRecoilState } from 'recoil'; import { FieldContext } from '../../contexts/FieldContext'; +import { useFieldInitialValue } from '../../hooks/useFieldInitialValue'; import { entityFieldsFamilySelector } from '../../states/selectors/entityFieldsFamilySelector'; import { assertFieldMetadata } from '../../types/guards/assertFieldMetadata'; import { isFieldEmail } from '../../types/guards/isFieldEmail'; @@ -20,9 +21,16 @@ export const useEmailField = () => { }), ); + const fieldInitialValue = useFieldInitialValue(); + + const initialValue = fieldInitialValue?.isEmpty + ? '' + : fieldInitialValue?.value ?? fieldValue; + return { fieldDefinition, fieldValue, + initialValue, setFieldValue, hotkeyScope, }; diff --git a/front/src/modules/ui/object/field/meta-types/hooks/useMoneyAmountV2Field.ts b/front/src/modules/ui/object/field/meta-types/hooks/useMoneyAmountV2Field.ts index fb4c01507..d41816af9 100644 --- a/front/src/modules/ui/object/field/meta-types/hooks/useMoneyAmountV2Field.ts +++ b/front/src/modules/ui/object/field/meta-types/hooks/useMoneyAmountV2Field.ts @@ -2,6 +2,7 @@ import { useContext } from 'react'; import { useRecoilState } from 'recoil'; import { FieldContext } from '../../contexts/FieldContext'; +import { useFieldInitialValue } from '../../hooks/useFieldInitialValue'; import { usePersistField } from '../../hooks/usePersistField'; import { entityFieldsFamilySelector } from '../../states/selectors/entityFieldsFamilySelector'; import { FieldMoneyAmountV2Value } from '../../types/FieldMetadata'; @@ -33,9 +34,18 @@ export const useMoneyAmountV2Field = () => { persistField(newValue); }; + const fieldInitialValue = useFieldInitialValue(); + + const initialValue: FieldMoneyAmountV2Value = fieldInitialValue?.isEmpty + ? { amount: 0, currency: '' } + : !isNaN(Number(fieldInitialValue?.value)) + ? { amount: Number(fieldInitialValue?.value), currency: '' } + : { amount: 0, currency: '' } ?? fieldValue; + return { fieldDefinition, fieldValue, + initialValue, setFieldValue, hotkeyScope, persistMoneyAmountV2Field, diff --git a/front/src/modules/ui/object/field/meta-types/hooks/useMoneyField.ts b/front/src/modules/ui/object/field/meta-types/hooks/useMoneyField.ts index 595bc2376..865ee1e76 100644 --- a/front/src/modules/ui/object/field/meta-types/hooks/useMoneyField.ts +++ b/front/src/modules/ui/object/field/meta-types/hooks/useMoneyField.ts @@ -7,6 +7,7 @@ import { } from '~/utils/cast-as-integer-or-null'; import { FieldContext } from '../../contexts/FieldContext'; +import { useFieldInitialValue } from '../../hooks/useFieldInitialValue'; import { usePersistField } from '../../hooks/usePersistField'; import { entityFieldsFamilySelector } from '../../states/selectors/entityFieldsFamilySelector'; import { assertFieldMetadata } from '../../types/guards/assertFieldMetadata'; @@ -38,9 +39,18 @@ export const useMoneyField = () => { persistField(castedValue); }; + const fieldInitialValue = useFieldInitialValue(); + + const initialValue = fieldInitialValue?.isEmpty + ? null + : !isNaN(Number(fieldInitialValue?.value)) + ? Number(fieldInitialValue?.value) + : null ?? fieldValue; + return { fieldDefinition, fieldValue, + initialValue, setFieldValue, hotkeyScope, persistMoneyField, diff --git a/front/src/modules/ui/object/field/meta-types/hooks/useNumberField.ts b/front/src/modules/ui/object/field/meta-types/hooks/useNumberField.ts index 830156849..6da233554 100644 --- a/front/src/modules/ui/object/field/meta-types/hooks/useNumberField.ts +++ b/front/src/modules/ui/object/field/meta-types/hooks/useNumberField.ts @@ -7,6 +7,7 @@ import { } from '~/utils/cast-as-integer-or-null'; import { FieldContext } from '../../contexts/FieldContext'; +import { useFieldInitialValue } from '../../hooks/useFieldInitialValue'; import { usePersistField } from '../../hooks/usePersistField'; import { entityFieldsFamilySelector } from '../../states/selectors/entityFieldsFamilySelector'; import { assertFieldMetadata } from '../../types/guards/assertFieldMetadata'; @@ -38,9 +39,18 @@ export const useNumberField = () => { persistField(castedValue); }; + const fieldInitialValue = useFieldInitialValue(); + + const initialValue = fieldInitialValue?.isEmpty + ? null + : !isNaN(Number(fieldInitialValue?.value)) + ? Number(fieldInitialValue?.value) + : null ?? fieldValue; + return { fieldDefinition, fieldValue, + initialValue, setFieldValue, hotkeyScope, persistNumberField, diff --git a/front/src/modules/ui/object/field/meta-types/hooks/usePhoneField.ts b/front/src/modules/ui/object/field/meta-types/hooks/usePhoneField.ts index 0d7130798..7c69ebce0 100644 --- a/front/src/modules/ui/object/field/meta-types/hooks/usePhoneField.ts +++ b/front/src/modules/ui/object/field/meta-types/hooks/usePhoneField.ts @@ -3,6 +3,7 @@ import { isPossiblePhoneNumber } from 'libphonenumber-js'; import { useRecoilState } from 'recoil'; import { FieldContext } from '../../contexts/FieldContext'; +import { useFieldInitialValue } from '../../hooks/useFieldInitialValue'; import { usePersistField } from '../../hooks/usePersistField'; import { entityFieldsFamilySelector } from '../../states/selectors/entityFieldsFamilySelector'; import { assertFieldMetadata } from '../../types/guards/assertFieldMetadata'; @@ -30,9 +31,16 @@ export const usePhoneField = () => { persistField(newPhoneValue); }; + const fieldInitialValue = useFieldInitialValue(); + + const initialValue = fieldInitialValue?.isEmpty + ? '' + : fieldInitialValue?.value ?? fieldValue; + return { fieldDefinition, fieldValue, + initialValue, setFieldValue, hotkeyScope, persistPhoneField, diff --git a/front/src/modules/ui/object/field/meta-types/hooks/useRelationField.ts b/front/src/modules/ui/object/field/meta-types/hooks/useRelationField.ts index 116ed73bf..45ba5acbd 100644 --- a/front/src/modules/ui/object/field/meta-types/hooks/useRelationField.ts +++ b/front/src/modules/ui/object/field/meta-types/hooks/useRelationField.ts @@ -2,6 +2,7 @@ import { useContext } from 'react'; import { useRecoilState } from 'recoil'; import { FieldContext } from '../../contexts/FieldContext'; +import { useFieldInitialValue } from '../../hooks/useFieldInitialValue'; import { entityFieldsFamilySelector } from '../../states/selectors/entityFieldsFamilySelector'; import { assertFieldMetadata } from '../../types/guards/assertFieldMetadata'; import { isFieldRelation } from '../../types/guards/isFieldRelation'; @@ -21,9 +22,19 @@ export const useRelationField = () => { }), ); + const fieldInitialValue = useFieldInitialValue(); + + const initialSearchValue = fieldInitialValue?.isEmpty + ? null + : fieldInitialValue?.value; + + const initialValue = fieldInitialValue?.isEmpty ? null : fieldValue; + return { fieldDefinition, fieldValue, + initialValue, + initialSearchValue, setFieldValue, }; }; diff --git a/front/src/modules/ui/object/field/meta-types/hooks/useTextField.ts b/front/src/modules/ui/object/field/meta-types/hooks/useTextField.ts index 01bb9931c..11968e8fc 100644 --- a/front/src/modules/ui/object/field/meta-types/hooks/useTextField.ts +++ b/front/src/modules/ui/object/field/meta-types/hooks/useTextField.ts @@ -2,6 +2,7 @@ import { useContext } from 'react'; import { useRecoilState } from 'recoil'; import { FieldContext } from '../../contexts/FieldContext'; +import { useFieldInitialValue } from '../../hooks/useFieldInitialValue'; import { entityFieldsFamilySelector } from '../../states/selectors/entityFieldsFamilySelector'; import { assertFieldMetadata } from '../../types/guards/assertFieldMetadata'; import { isFieldText } from '../../types/guards/isFieldText'; @@ -20,9 +21,16 @@ export const useTextField = () => { }), ); + const fieldInitialValue = useFieldInitialValue(); + + const initialValue = fieldInitialValue?.isEmpty + ? '' + : fieldInitialValue?.value ?? fieldValue; + return { fieldDefinition, fieldValue, + initialValue, setFieldValue, hotkeyScope, }; diff --git a/front/src/modules/ui/object/field/meta-types/hooks/useURLField.ts b/front/src/modules/ui/object/field/meta-types/hooks/useURLField.ts index c33b06f60..fb3de1a83 100644 --- a/front/src/modules/ui/object/field/meta-types/hooks/useURLField.ts +++ b/front/src/modules/ui/object/field/meta-types/hooks/useURLField.ts @@ -4,6 +4,7 @@ import { useRecoilState } from 'recoil'; import { isURL } from '~/utils/is-url'; import { FieldContext } from '../../contexts/FieldContext'; +import { useFieldInitialValue } from '../../hooks/useFieldInitialValue'; import { usePersistField } from '../../hooks/usePersistField'; import { entityFieldsFamilySelector } from '../../states/selectors/entityFieldsFamilySelector'; import { assertFieldMetadata } from '../../types/guards/assertFieldMetadata'; @@ -23,6 +24,12 @@ export const useURLField = () => { }), ); + const fieldInitialValue = useFieldInitialValue(); + + const initialValue = fieldInitialValue?.isEmpty + ? '' + : fieldInitialValue?.value ?? fieldValue; + const persistField = usePersistField(); const persistURLField = (newValue: string) => { @@ -36,6 +43,7 @@ export const useURLField = () => { return { fieldDefinition, fieldValue, + initialValue, setFieldValue, hotkeyScope, persistURLField, diff --git a/front/src/modules/ui/object/field/meta-types/hooks/useURLV2Field.ts b/front/src/modules/ui/object/field/meta-types/hooks/useURLV2Field.ts index 821a0d6e1..9ea08fc31 100644 --- a/front/src/modules/ui/object/field/meta-types/hooks/useURLV2Field.ts +++ b/front/src/modules/ui/object/field/meta-types/hooks/useURLV2Field.ts @@ -2,6 +2,7 @@ import { useContext } from 'react'; import { useRecoilState } from 'recoil'; import { FieldContext } from '../../contexts/FieldContext'; +import { useFieldInitialValue } from '../../hooks/useFieldInitialValue'; import { usePersistField } from '../../hooks/usePersistField'; import { entityFieldsFamilySelector } from '../../states/selectors/entityFieldsFamilySelector'; import { FieldURLV2Value } from '../../types/FieldMetadata'; @@ -23,6 +24,12 @@ export const useURLV2Field = () => { }), ); + const fieldInitialValue = useFieldInitialValue(); + + const initialValue: FieldURLV2Value = fieldInitialValue?.isEmpty + ? { link: '', text: '' } + : { link: fieldInitialValue?.value ?? '', text: '' } ?? fieldValue; + const persistField = usePersistField(); const persistURLField = (newValue: FieldURLV2Value) => { @@ -36,6 +43,7 @@ export const useURLV2Field = () => { return { fieldDefinition, fieldValue, + initialValue, setFieldValue, hotkeyScope, persistURLField, diff --git a/front/src/modules/ui/object/field/meta-types/input/components/ChipFieldInput.tsx b/front/src/modules/ui/object/field/meta-types/input/components/ChipFieldInput.tsx index 45888bb6b..60d43e503 100644 --- a/front/src/modules/ui/object/field/meta-types/input/components/ChipFieldInput.tsx +++ b/front/src/modules/ui/object/field/meta-types/input/components/ChipFieldInput.tsx @@ -21,7 +21,7 @@ export const ChipFieldInput = ({ onTab, onShiftTab, }: ChipFieldInputProps) => { - const { fieldDefinition, contentFieldValue, hotkeyScope } = useChipField(); + const { fieldDefinition, initialContentValue, hotkeyScope } = useChipField(); const persistField = usePersistField(); @@ -53,7 +53,7 @@ export const ChipFieldInput = ({ { - const { fieldDefinition, firstValue, secondValue, hotkeyScope } = - useDoubleTextChipField(); + const { + fieldDefinition, + initialFirstValue, + initialSecondValue, + hotkeyScope, + } = useDoubleTextChipField(); const persistField = usePersistField(); @@ -53,8 +57,8 @@ export const DoubleTextChipFieldInput = ({ return ( { - const { fieldDefinition, firstValue, secondValue, hotkeyScope } = - useDoubleTextField(); + const { + fieldDefinition, + initialFirstValue, + initialSecondValue, + hotkeyScope, + } = useDoubleTextField(); const persistField = usePersistField(); @@ -53,8 +57,8 @@ export const DoubleTextFieldInput = ({ return ( { - const { fieldDefinition, fieldValue, hotkeyScope } = useEmailField(); + const { fieldDefinition, initialValue, hotkeyScope } = useEmailField(); const persistField = usePersistField(); @@ -53,7 +53,7 @@ export const EmailFieldInput = ({ { - const { fieldValue, hotkeyScope } = useMoneyAmountV2Field(); + const { hotkeyScope, initialValue } = useMoneyAmountV2Field(); const persistField = usePersistField(); @@ -77,8 +77,8 @@ export const MoneyAmountV2FieldInput = ({ return ( { - const { fieldDefinition, fieldValue, hotkeyScope, persistMoneyField } = + const { fieldDefinition, hotkeyScope, persistMoneyField, initialValue } = useMoneyField(); const handleEnter = (newText: string) => { @@ -52,7 +52,7 @@ export const MoneyFieldInput = ({ { - const { fieldDefinition, fieldValue, hotkeyScope, persistNumberField } = + const { fieldDefinition, initialValue, hotkeyScope, persistNumberField } = useNumberField(); const handleEnter = (newText: string) => { @@ -52,7 +52,7 @@ export const NumberFieldInput = ({ { - const { fieldDefinition, fieldValue, hotkeyScope, persistPhoneField } = + const { fieldDefinition, initialValue, hotkeyScope, persistPhoneField } = usePhoneField(); const handleEnter = (newText: string) => { @@ -51,7 +51,7 @@ export const PhoneFieldInput = ({ { - const { fieldDefinition, fieldValue } = useRelationField(); + const { fieldDefinition, initialValue, initialSearchValue } = + useRelationField(); const persistField = usePersistField(); @@ -34,26 +36,31 @@ export const RelationFieldInput = ({ onSubmit?.(() => persistField(newEntity?.originalEntity ?? null)); }; + useEffect(() => {}, [initialSearchValue]); + return ( {fieldDefinition.metadata.relationType === Entity.Person ? ( ) : fieldDefinition.metadata.relationType === Entity.User ? ( ) : fieldDefinition.metadata.relationType === Entity.Company ? ( ) : null} diff --git a/front/src/modules/ui/object/field/meta-types/input/components/TextFieldInput.tsx b/front/src/modules/ui/object/field/meta-types/input/components/TextFieldInput.tsx index af8599869..1f12b1fd6 100644 --- a/front/src/modules/ui/object/field/meta-types/input/components/TextFieldInput.tsx +++ b/front/src/modules/ui/object/field/meta-types/input/components/TextFieldInput.tsx @@ -21,7 +21,7 @@ export const TextFieldInput = ({ onTab, onShiftTab, }: TextFieldInputProps) => { - const { fieldDefinition, fieldValue, hotkeyScope } = useTextField(); + const { fieldDefinition, initialValue, hotkeyScope } = useTextField(); const persistField = usePersistField(); @@ -53,7 +53,7 @@ export const TextFieldInput = ({ { - const { fieldDefinition, fieldValue, hotkeyScope, persistURLField } = + const { fieldDefinition, initialValue, hotkeyScope, persistURLField } = useURLField(); const handleEnter = (newText: string) => { @@ -51,7 +51,7 @@ export const URLFieldInput = ({ { - const { fieldValue, hotkeyScope, persistURLField } = useURLV2Field(); + const { initialValue, hotkeyScope, persistURLField } = useURLV2Field(); const handleEnter = (newURL: FieldDoubleText) => { onEnter?.(() => @@ -73,8 +73,8 @@ export const URLV2FieldInput = ({ return ( ({ + key: 'entityFieldInitialValueFamilyState', + default: undefined, +}); diff --git a/front/src/modules/ui/object/field/states/selectors/isEntityFieldEmptyFamilySelector.ts b/front/src/modules/ui/object/field/states/selectors/isEntityFieldEmptyFamilySelector.ts index 0a851734c..d3ce4d5d8 100644 --- a/front/src/modules/ui/object/field/states/selectors/isEntityFieldEmptyFamilySelector.ts +++ b/front/src/modules/ui/object/field/states/selectors/isEntityFieldEmptyFamilySelector.ts @@ -2,6 +2,7 @@ import { selectorFamily } from 'recoil'; import { FieldDefinition } from '../../types/FieldDefinition'; import { FieldMetadata } from '../../types/FieldMetadata'; +import { isFieldBoolean } from '../../types/guards/isFieldBoolean'; import { isFieldChip } from '../../types/guards/isFieldChip'; import { isFieldDate } from '../../types/guards/isFieldDate'; import { isFieldDoubleTextChip } from '../../types/guards/isFieldDoubleTextChip'; @@ -35,6 +36,7 @@ export const isEntityFieldEmptyFamilySelector = selectorFamily({ isFieldNumber(fieldDefinition) || isFieldMoney(fieldDefinition) || isFieldEmail(fieldDefinition) || + isFieldBoolean(fieldDefinition) || isFieldPhone(fieldDefinition) ) { const fieldName = fieldDefinition.metadata.fieldName; @@ -88,6 +90,10 @@ export const isEntityFieldEmptyFamilySelector = selectorFamily({ contentFieldSecondValue === undefined || contentFieldSecondValue === '') ); + } else { + throw new Error( + `Entity field type not supported in isEntityFieldEmptyFamilySelector : ${fieldDefinition.type}}`, + ); } return false; diff --git a/front/src/modules/ui/object/field/types/FieldInitialValue.ts b/front/src/modules/ui/object/field/types/FieldInitialValue.ts new file mode 100644 index 000000000..e6353e9c0 --- /dev/null +++ b/front/src/modules/ui/object/field/types/FieldInitialValue.ts @@ -0,0 +1,4 @@ +export type FieldInitialValue = { + isEmpty?: boolean; + value?: string; +}; diff --git a/front/src/modules/ui/object/record-table/states/tableCellInitialValueFamilyState.ts b/front/src/modules/ui/object/record-table/states/tableCellInitialValueFamilyState.ts new file mode 100644 index 000000000..cd3375481 --- /dev/null +++ b/front/src/modules/ui/object/record-table/states/tableCellInitialValueFamilyState.ts @@ -0,0 +1,12 @@ +import { atomFamily } from 'recoil'; + +import { TableCellInitialValue } from '../types/TableCellInitialValue'; +import { TableCellPosition } from '../types/TableCellPosition'; + +export const tableCellInitialValueFamilyState = atomFamily< + TableCellInitialValue | undefined, + TableCellPosition +>({ + key: 'tableCellInitialValueFamilyState', + default: undefined, +}); diff --git a/front/src/modules/ui/object/record-table/table-cell/components/TableCell.tsx b/front/src/modules/ui/object/record-table/table-cell/components/TableCell.tsx index d1aacfe8e..1b42ecb47 100644 --- a/front/src/modules/ui/object/record-table/table-cell/components/TableCell.tsx +++ b/front/src/modules/ui/object/record-table/table-cell/components/TableCell.tsx @@ -1,13 +1,8 @@ -import { useContext } from 'react'; - -import { IconArrowUpRight } from '@/ui/display/icon'; import { FieldDisplay } from '@/ui/object/field/components/FieldDisplay'; import { FieldInput } from '@/ui/object/field/components/FieldInput'; -import { useGetButtonIcon } from '@/ui/object/field/hooks/useGetButtonIcon'; import { FieldInputEvent } from '@/ui/object/field/types/FieldInputEvent'; import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope'; -import { ColumnIndexContext } from '../../contexts/ColumnIndexContext'; import { useMoveSoftFocus } from '../../hooks/useMoveSoftFocus'; import { useTableCell } from '../hooks/useTableCell'; @@ -18,10 +13,6 @@ export const TableCell = ({ }: { customHotkeyScope: HotkeyScope; }) => { - const isFirstColumn = useContext(ColumnIndexContext) === 0; - - const buttonIcon = useGetButtonIcon(); - const { closeTableCell } = useTableCell(); const { moveLeft, moveRight, moveDown } = useMoveSoftFocus(); @@ -72,7 +63,6 @@ export const TableCell = ({ /> } nonEditModeContent={} - buttonIcon={isFirstColumn ? IconArrowUpRight : buttonIcon} > ); }; diff --git a/front/src/modules/ui/object/record-table/table-cell/components/TableCellContainer.tsx b/front/src/modules/ui/object/record-table/table-cell/components/TableCellContainer.tsx index 9c364a3a7..f664f1b10 100644 --- a/front/src/modules/ui/object/record-table/table-cell/components/TableCellContainer.tsx +++ b/front/src/modules/ui/object/record-table/table-cell/components/TableCellContainer.tsx @@ -1,7 +1,8 @@ import { ReactElement, useContext, useState } from 'react'; import styled from '@emotion/styled'; -import { IconComponent } from '@/ui/display/icon/types/IconComponent'; +import { IconArrowUpRight } from '@/ui/display/icon'; +import { useGetButtonIcon } from '@/ui/object/field/hooks/useGetButtonIcon'; import { useIsFieldEmpty } from '@/ui/object/field/hooks/useIsFieldEmpty'; import { useIsFieldInputOnly } from '@/ui/object/field/hooks/useIsFieldInputOnly'; import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope'; @@ -39,7 +40,6 @@ export type TableCellContainerProps = { editHotkeyScope?: HotkeyScope; transparent?: boolean; maxContentWidth?: number; - buttonIcon?: IconComponent; onSubmit?: () => void; onCancel?: () => void; }; @@ -54,7 +54,6 @@ export const TableCellContainer = ({ editModeContent, nonEditModeContent, editHotkeyScope, - buttonIcon, }: TableCellContainerProps) => { const { isCurrentTableCellInEditMode } = useCurrentTableCellEditMode(); @@ -95,6 +94,12 @@ export const TableCellContainer = ({ const isEmpty = useIsFieldEmpty(); + const isFirstColumn = useContext(ColumnIndexContext) === 0; + + const customButtonIcon = useGetButtonIcon(); + + const buttonIcon = isFirstColumn ? IconArrowUpRight : customButtonIcon; + const showButton = !!buttonIcon && hasSoftFocus && diff --git a/front/src/modules/ui/object/record-table/table-cell/components/TableCellSoftFocusMode.tsx b/front/src/modules/ui/object/record-table/table-cell/components/TableCellSoftFocusMode.tsx index df6aee7bc..9e5a91363 100644 --- a/front/src/modules/ui/object/record-table/table-cell/components/TableCellSoftFocusMode.tsx +++ b/front/src/modules/ui/object/record-table/table-cell/components/TableCellSoftFocusMode.tsx @@ -1,4 +1,5 @@ import { PropsWithChildren, useEffect, useRef } from 'react'; +import { Key } from 'ts-key-enum'; import { useIsFieldInputOnly } from '@/ui/object/field/hooks/useIsFieldInputOnly'; import { useToggleEditOnlyInput } from '@/ui/object/field/hooks/useToggleEditOnlyInput'; @@ -26,7 +27,25 @@ export const TableCellSoftFocusMode = ({ }, []); useScopedHotkeys( - 'enter', + [Key.Backspace, Key.Delete], + () => { + if (!isFieldInputOnly) { + openTableCell({ + initialValue: { + isEmpty: true, + }, + }); + } + }, + TableHotkeyScope.TableSoftFocus, + [openTableCell], + { + enabled: !isFieldInputOnly, + }, + ); + + useScopedHotkeys( + Key.Enter, () => { if (!isFieldInputOnly) { openTableCell(); @@ -42,6 +61,10 @@ export const TableCellSoftFocusMode = ({ '*', (keyboardEvent) => { if (!isFieldInputOnly) { + keyboardEvent.preventDefault(); + keyboardEvent.stopPropagation(); + keyboardEvent.stopImmediatePropagation(); + const isWritingText = !isNonTextWritingKey(keyboardEvent.key) && !keyboardEvent.ctrlKey && @@ -51,7 +74,11 @@ export const TableCellSoftFocusMode = ({ return; } - openTableCell(); + openTableCell({ + initialValue: { + value: keyboardEvent.key, + }, + }); } }, TableHotkeyScope.TableSoftFocus, diff --git a/front/src/modules/ui/object/record-table/table-cell/hooks/useCurrentTableCellInitialValue.ts b/front/src/modules/ui/object/record-table/table-cell/hooks/useCurrentTableCellInitialValue.ts new file mode 100644 index 000000000..3b48c0fe1 --- /dev/null +++ b/front/src/modules/ui/object/record-table/table-cell/hooks/useCurrentTableCellInitialValue.ts @@ -0,0 +1,17 @@ +import { useRecoilState } from 'recoil'; + +import { tableCellInitialValueFamilyState } from '../../states/tableCellInitialValueFamilyState'; + +import { useCurrentTableCellPosition } from './useCurrentCellPosition'; + +export const useCurrentTableCellInitialValue = () => { + const currentTableCellPosition = useCurrentTableCellPosition(); + + const [currentTableCellInitialValue, setCurrentTableCellInitialValue] = + useRecoilState(tableCellInitialValueFamilyState(currentTableCellPosition)); + + return { + currentTableCellInitialValue, + setCurrentTableCellInitialValue, + }; +}; diff --git a/front/src/modules/ui/object/record-table/table-cell/hooks/useTableCell.ts b/front/src/modules/ui/object/record-table/table-cell/hooks/useTableCell.ts index ce8927d8c..d8423411d 100644 --- a/front/src/modules/ui/object/record-table/table-cell/hooks/useTableCell.ts +++ b/front/src/modules/ui/object/record-table/table-cell/hooks/useTableCell.ts @@ -1,8 +1,11 @@ import { useContext } from 'react'; import { useNavigate } from 'react-router-dom'; +import { useRecoilState } from 'recoil'; import { FieldContext } from '@/ui/object/field/contexts/FieldContext'; import { useIsFieldEmpty } from '@/ui/object/field/hooks/useIsFieldEmpty'; +import { entityFieldInitialValueFamilyState } from '@/ui/object/field/states/entityFieldInitialValueFamilyState'; +import { FieldInitialValue } from '@/ui/object/field/types/FieldInitialValue'; import { useDragSelect } from '@/ui/utilities/drag-select/hooks/useDragSelect'; import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope'; import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope'; @@ -28,12 +31,6 @@ export const useTableCell = () => { const customCellHotkeyScope = useContext(CellHotkeyScopeContext); - const closeTableCell = () => { - setDragSelectionStartEnabled(true); - closeCurrentTableCellInEditMode(); - setHotkeyScope(TableHotkeyScope.TableSoftFocus); - }; - const navigate = useNavigate(); const isFirstColumnCell = useContext(ColumnIndexContext) === 0; @@ -42,7 +39,14 @@ export const useTableCell = () => { const { entityId, fieldDefinition } = useContext(FieldContext); - const openTableCell = () => { + const [, setFieldInitialValue] = useRecoilState( + entityFieldInitialValueFamilyState({ + entityId, + fieldId: fieldDefinition.fieldId, + }), + ); + + const openTableCell = (options?: { initialValue?: FieldInitialValue }) => { if (isFirstColumnCell && !isEmpty && fieldDefinition.basePathToShowPage) { navigate(`${fieldDefinition.basePathToShowPage}${entityId}`); return; @@ -51,6 +55,10 @@ export const useTableCell = () => { setDragSelectionStartEnabled(false); setCurrentTableCellInEditMode(); + if (options?.initialValue) { + setFieldInitialValue(options.initialValue); + } + if (customCellHotkeyScope) { setHotkeyScope( customCellHotkeyScope.scope, @@ -61,6 +69,13 @@ export const useTableCell = () => { } }; + const closeTableCell = () => { + setDragSelectionStartEnabled(true); + closeCurrentTableCellInEditMode(); + setFieldInitialValue(undefined); + setHotkeyScope(TableHotkeyScope.TableSoftFocus); + }; + return { closeTableCell, openTableCell, diff --git a/front/src/modules/users/components/UserPicker.tsx b/front/src/modules/users/components/UserPicker.tsx index dee2e14c7..1b24dac37 100644 --- a/front/src/modules/users/components/UserPicker.tsx +++ b/front/src/modules/users/components/UserPicker.tsx @@ -1,3 +1,5 @@ +import { useEffect } from 'react'; + import { useFilteredSearchEntityQuery } from '@/search/hooks/useFilteredSearchEntityQuery'; import { IconUserCircle } from '@/ui/display/icon'; import { SingleEntitySelect } from '@/ui/input/relation-picker/components/SingleEntitySelect'; @@ -12,6 +14,7 @@ export type UserPickerProps = { onSubmit: (newUser: EntityForSelect | null) => void; onCancel?: () => void; width?: number; + initialSearchFilter?: string | null; }; type UserForSelect = EntityForSelect & { @@ -23,10 +26,14 @@ export const UserPicker = ({ onSubmit, onCancel, width, + initialSearchFilter, }: UserPickerProps) => { - const [relationPickerSearchFilter] = useRecoilScopedState( - relationPickerSearchFilterScopedState, - ); + const [relationPickerSearchFilter, setRelationPickerSearchFilter] = + useRecoilScopedState(relationPickerSearchFilterScopedState); + + useEffect(() => { + setRelationPickerSearchFilter(initialSearchFilter ?? ''); + }, [initialSearchFilter, setRelationPickerSearchFilter]); const users = useFilteredSearchEntityQuery({ queryHook: useSearchUserQuery,