diff --git a/front/src/modules/companies/components/CompanyPickerCell.tsx b/front/src/modules/companies/components/CompanyPickerCell.tsx new file mode 100644 index 0000000000..03c02df853 --- /dev/null +++ b/front/src/modules/companies/components/CompanyPickerCell.tsx @@ -0,0 +1,60 @@ +import { useFilteredSearchCompanyQuery } from '@/companies/queries'; +import { useSetHotkeyScope } from '@/ui/hotkey/hooks/useSetHotkeyScope'; +import { useRecoilScopedState } from '@/ui/recoil-scope/hooks/useRecoilScopedState'; +import { SingleEntitySelect } from '@/ui/relation-picker/components/SingleEntitySelect'; +import { relationPickerSearchFilterScopedState } from '@/ui/relation-picker/states/relationPickerSearchFilterScopedState'; +import { isCreateModeScopedState } from '@/ui/table/editable-cell/states/isCreateModeScopedState'; +import { TableHotkeyScope } from '@/ui/table/types/TableHotkeyScope'; + +import { EntityForSelect } from '../../ui/relation-picker/types/EntityForSelect'; + +export type OwnProps = { + companyId: string | null; + onSubmit: (newCompany: EntityForSelect | null) => void; + onCancel?: () => void; + createModeEnabled?: boolean; +}; + +export function CompanyPickerCell({ + companyId, + onSubmit, + onCancel, + createModeEnabled, +}: OwnProps) { + const [, setIsCreating] = useRecoilScopedState(isCreateModeScopedState); + + const [searchFilter] = useRecoilScopedState( + relationPickerSearchFilterScopedState, + ); + + const setHotkeyScope = useSetHotkeyScope(); + + const companies = useFilteredSearchCompanyQuery({ + searchFilter, + selectedIds: [companyId ?? ''], + }); + + async function handleEntitySelected( + entity: EntityForSelect | null | undefined, + ) { + onSubmit(entity ?? null); + } + + function handleCreate() { + setIsCreating(true); + setHotkeyScope(TableHotkeyScope.CellDoubleTextInput); + } + + return ( + + ); +} diff --git a/front/src/modules/people/components/PeopleCompanyPicker.tsx b/front/src/modules/people/components/PeopleCompanyPicker.tsx index 0c96fa8437..bdc3e54465 100644 --- a/front/src/modules/people/components/PeopleCompanyPicker.tsx +++ b/front/src/modules/people/components/PeopleCompanyPicker.tsx @@ -1,12 +1,8 @@ -import { Key } from 'ts-key-enum'; - import { useFilteredSearchCompanyQuery } from '@/companies/queries'; -import { useScopedHotkeys } from '@/ui/hotkey/hooks/useScopedHotkeys'; import { useSetHotkeyScope } from '@/ui/hotkey/hooks/useSetHotkeyScope'; import { useRecoilScopedState } from '@/ui/recoil-scope/hooks/useRecoilScopedState'; import { SingleEntitySelect } from '@/ui/relation-picker/components/SingleEntitySelect'; import { relationPickerSearchFilterScopedState } from '@/ui/relation-picker/states/relationPickerSearchFilterScopedState'; -import { RelationPickerHotkeyScope } from '@/ui/relation-picker/types/RelationPickerHotkeyScope'; import { useEditableCell } from '@/ui/table/editable-cell/hooks/useEditableCell'; import { isCreateModeScopedState } from '@/ui/table/editable-cell/states/isCreateModeScopedState'; import { TableHotkeyScope } from '@/ui/table/types/TableHotkeyScope'; @@ -63,13 +59,6 @@ export function PeopleCompanyPicker({ people }: OwnProps) { addToScopeStack(TableHotkeyScope.CellDoubleTextInput); } - useScopedHotkeys( - Key.Escape, - () => closeEditableCell(), - RelationPickerHotkeyScope.RelationPicker, - [closeEditableCell], - ); - return ( void; + onCancel?: () => void; +}; + +type PersonForSelect = EntityForSelect & { + entityType: Entity.Person; +}; + +export function PeoplePicker({ personId, onSubmit, onCancel }: OwnProps) { + const [searchFilter] = useRecoilScopedState( + relationPickerSearchFilterScopedState, + ); + + const people = useFilteredSearchEntityQuery({ + queryHook: useSearchPeopleQuery, + selectedIds: [personId], + searchFilter: searchFilter, + mappingFunction: (person) => ({ + entityType: Entity.Person, + id: person.id, + name: person.firstName + ' ' + person.lastName, + avatarType: 'rounded', + }), + orderByField: 'firstName', + searchOnFields: ['firstName', 'lastName'], + }); + + async function handleEntitySelected( + selectedPerson: PersonForSelect | null | undefined, + ) { + onSubmit(selectedPerson?.id ?? null); + } + + return ( + + ); +} diff --git a/front/src/modules/people/constants/peopleFieldMetadataArray.tsx b/front/src/modules/people/constants/peopleFieldMetadataArray.tsx index 9549a6f9ab..3f21bf769f 100644 --- a/front/src/modules/people/constants/peopleFieldMetadataArray.tsx +++ b/front/src/modules/people/constants/peopleFieldMetadataArray.tsx @@ -1,5 +1,10 @@ -import { IconBriefcase, IconMap } from '@tabler/icons-react'; +import { + IconBriefcase, + IconBuildingSkyscraper, + IconMap, +} from '@tabler/icons-react'; +import { Entity } from '@/ui/relation-picker/types/EntityTypeForSelect'; import { EntityFieldMetadata } from '@/ui/table/types/EntityFieldMetadata'; export const peopleFieldMetadataArray: EntityFieldMetadata[] = [ @@ -17,4 +22,12 @@ export const peopleFieldMetadataArray: EntityFieldMetadata[] = [ columnSize: 150, type: 'text', }, + { + fieldName: 'company', + label: 'Company', + icon: , + columnSize: 150, + type: 'relation', + relationType: Entity.Company, + }, ]; diff --git a/front/src/modules/people/hooks/useUpdateEntityField.ts b/front/src/modules/people/hooks/useUpdateEntityField.ts new file mode 100644 index 0000000000..1affa08dc3 --- /dev/null +++ b/front/src/modules/people/hooks/useUpdateEntityField.ts @@ -0,0 +1,65 @@ +import { useContext } from 'react'; +import { useRecoilValue } from 'recoil'; + +import { EntityForSelect } from '@/ui/relation-picker/types/EntityForSelect'; +import { entityFieldMetadataArrayState } from '@/ui/table/states/entityFieldMetadataArrayState'; +import { EntityUpdateMutationHookContext } from '@/ui/table/states/EntityUpdateMutationHookContext'; + +export function useUpdateEntityField() { + const useUpdateEntityMutation = useContext(EntityUpdateMutationHookContext); + + const [updateEntity] = useUpdateEntityMutation(); + + const entityFieldMetadataArray = useRecoilValue( + entityFieldMetadataArrayState, + ); + + return function updatePeopleField( + currentEntityId: string, + fieldName: string, + newFieldValue: unknown, + ) { + const fieldMetadata = entityFieldMetadataArray.find( + (metadata) => metadata.fieldName === fieldName, + ); + + if (!fieldMetadata) { + throw new Error(`Field metadata not found for field ${fieldName}`); + } + + if (fieldMetadata.type === 'relation') { + const newSelectedEntity = newFieldValue as EntityForSelect | null; + + if (!newSelectedEntity) { + updateEntity({ + variables: { + where: { id: currentEntityId }, + data: { + [fieldName]: { + disconnect: true, + }, + }, + }, + }); + } else { + updateEntity({ + variables: { + where: { id: currentEntityId }, + data: { + [fieldName]: { + connect: { id: newSelectedEntity.id }, + }, + }, + }, + }); + } + } else { + updateEntity({ + variables: { + where: { id: currentEntityId }, + data: { [fieldName]: newFieldValue }, + }, + }); + } + }; +} diff --git a/front/src/modules/people/hooks/useUpdatePeopleField.ts b/front/src/modules/people/hooks/useUpdatePeopleField.ts deleted file mode 100644 index eaaa3b372b..0000000000 --- a/front/src/modules/people/hooks/useUpdatePeopleField.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { useUpdateOnePersonMutation } from '~/generated/graphql'; - -export function useUpdatePeopleField() { - const [updatePeople] = useUpdateOnePersonMutation(); - - return function updatePeopleField( - peopleId: string, - fieldName: string, - fieldValue: unknown, - ) { - updatePeople({ - variables: { - where: { id: peopleId }, - data: { [fieldName]: fieldValue }, - }, - }); - }; -} diff --git a/front/src/modules/people/table/components/PeopleTableV2.tsx b/front/src/modules/people/table/components/PeopleTableV2.tsx index fad160abde..50a972e594 100644 --- a/front/src/modules/people/table/components/PeopleTableV2.tsx +++ b/front/src/modules/people/table/components/PeopleTableV2.tsx @@ -3,7 +3,6 @@ import { useCallback, useMemo, useState } from 'react'; import { defaultOrderBy } from '@/companies/queries'; import { GenericEntityTableData } from '@/people/components/GenericEntityTableData'; import { peopleFieldMetadataArray } from '@/people/constants/peopleFieldMetadataArray'; -import { useUpdatePeopleField } from '@/people/hooks/useUpdatePeopleField'; import { PeopleSelectedSortType } from '@/people/queries'; import { reduceSortsToOrderBy } from '@/ui/filter-n-sort/helpers'; import { filtersScopedState } from '@/ui/filter-n-sort/states/filtersScopedState'; @@ -15,6 +14,7 @@ import { TableContext } from '@/ui/table/states/TableContext'; import { PersonOrderByWithRelationInput, useGetPeopleQuery, + useUpdateOnePersonMutation, } from '~/generated/graphql'; import { availableSorts } from '~/pages/people/people-sorts'; @@ -46,7 +46,7 @@ export function PeopleTable() { viewIcon={} availableSorts={availableSorts} onSortsUpdate={updateSorts} - useUpdateField={useUpdatePeopleField} + useUpdateEntityMutation={useUpdateOnePersonMutation} /> ); diff --git a/front/src/modules/ui/hotkey/hooks/useScopedHotkeyCallback.ts b/front/src/modules/ui/hotkey/hooks/useScopedHotkeyCallback.ts index 656903121d..d73f70d5b3 100644 --- a/front/src/modules/ui/hotkey/hooks/useScopedHotkeyCallback.ts +++ b/front/src/modules/ui/hotkey/hooks/useScopedHotkeyCallback.ts @@ -3,6 +3,8 @@ import { useRecoilCallback } from 'recoil'; import { internalHotkeysEnabledScopesState } from '../states/internal/internalHotkeysEnabledScopesState'; +const DEBUG_HOTKEY_SCOPE = false; + export function useScopedHotkeyCallback() { return useRecoilCallback( ({ snapshot }) => @@ -24,9 +26,31 @@ export function useScopedHotkeyCallback() { .valueOrThrow(); if (!currentHotkeyScopes.includes(scope)) { + if (DEBUG_HOTKEY_SCOPE) { + console.debug( + `%cI can't call hotkey (${ + hotkeysEvent.keys + }) because I'm in scope [${scope}] and the active scopes are : [${currentHotkeyScopes.join( + ', ', + )}]`, + 'color: gray; ', + ); + } + return; } + if (DEBUG_HOTKEY_SCOPE) { + console.debug( + `%cI can call hotkey (${ + hotkeysEvent.keys + }) because I'm in scope [${scope}] and the active scopes are : [${currentHotkeyScopes.join( + ', ', + )}]`, + 'color: green;', + ); + } + if (preventDefault) { keyboardEvent.stopPropagation(); keyboardEvent.preventDefault(); diff --git a/front/src/modules/ui/table/components/EntityTableCellV2.tsx b/front/src/modules/ui/table/components/EntityTableCellV2.tsx index 52e95a5593..3f7979749e 100644 --- a/front/src/modules/ui/table/components/EntityTableCellV2.tsx +++ b/front/src/modules/ui/table/components/EntityTableCellV2.tsx @@ -1,7 +1,7 @@ import { useContext } from 'react'; import { useSetRecoilState } from 'recoil'; -import { GenericEditableCell } from '@/people/table/components/GenericEditableCell'; +import { GenericEditableCell } from '@/ui/table/components/GenericEditableCell'; import { RecoilScope } from '../../recoil-scope/components/RecoilScope'; import { useCurrentRowSelected } from '../hooks/useCurrentRowSelected'; diff --git a/front/src/modules/ui/table/components/EntityTableV2.tsx b/front/src/modules/ui/table/components/EntityTableV2.tsx index f34c70f741..21c3413502 100644 --- a/front/src/modules/ui/table/components/EntityTableV2.tsx +++ b/front/src/modules/ui/table/components/EntityTableV2.tsx @@ -6,9 +6,8 @@ import { useListenClickOutside } from '@/ui/hooks/useListenClickOutside'; import { useLeaveTableFocus } from '../hooks/useLeaveTableFocus'; import { useMapKeyboardToSoftFocus } from '../hooks/useMapKeyboardToSoftFocus'; -import { EntityUpdateFieldHookContext } from '../states/EntityUpdateFieldHookContext'; +import { EntityUpdateMutationHookContext } from '../states/EntityUpdateMutationHookContext'; import { TableHeader } from '../table-header/components/TableHeader'; -import { EntityUpdateFieldHook } from '../types/CellUpdateFieldHook'; import { EntityTableBody } from './EntityTableBodyV2'; import { EntityTableHeader } from './EntityTableHeaderV2'; @@ -90,7 +89,7 @@ type OwnProps = { availableSorts?: Array>; onSortsUpdate?: (sorts: Array>) => void; onRowSelectionChange?: (rowSelection: string[]) => void; - useUpdateField: EntityUpdateFieldHook; + useUpdateEntityMutation: any; }; export function EntityTable({ @@ -98,7 +97,7 @@ export function EntityTable({ viewIcon, availableSorts, onSortsUpdate, - useUpdateField, + useUpdateEntityMutation, }: OwnProps) { const tableBodyRef = React.useRef(null); @@ -114,7 +113,7 @@ export function EntityTable({ }); return ( - + ({ - + ); } diff --git a/front/src/modules/people/table/components/GenericEditableCell.tsx b/front/src/modules/ui/table/components/GenericEditableCell.tsx similarity index 65% rename from front/src/modules/people/table/components/GenericEditableCell.tsx rename to front/src/modules/ui/table/components/GenericEditableCell.tsx index 2b8a6fff0d..839244d36a 100644 --- a/front/src/modules/people/table/components/GenericEditableCell.tsx +++ b/front/src/modules/ui/table/components/GenericEditableCell.tsx @@ -1,5 +1,6 @@ import { EntityFieldMetadata } from '@/ui/table/types/EntityFieldMetadata'; +import { GenericEditableRelationCell } from './GenericEditableRelationCell'; import { GenericEditableTextCell } from './GenericEditableTextCell'; type OwnProps = { @@ -16,8 +17,15 @@ export function GenericEditableCell({ entityFieldMetadata }: OwnProps) { editModeHorizontalAlign="left" /> ); - + case 'relation': { + return ( + + ); + } default: + console.warn( + `Unknown field type: ${entityFieldMetadata.type} in GenericEditableCell`, + ); return <>; } } diff --git a/front/src/modules/ui/table/components/GenericEditableRelationCell.tsx b/front/src/modules/ui/table/components/GenericEditableRelationCell.tsx new file mode 100644 index 0000000000..9e5d0876ec --- /dev/null +++ b/front/src/modules/ui/table/components/GenericEditableRelationCell.tsx @@ -0,0 +1,35 @@ +import { RelationPickerHotkeyScope } from '@/ui/relation-picker/types/RelationPickerHotkeyScope'; +import { EditableCell } from '@/ui/table/editable-cell/components/EditableCell'; +import { EntityFieldMetadata } from '@/ui/table/types/EntityFieldMetadata'; + +import { GenericEditableRelationCellDisplayMode } from './GenericEditableRelationCellDisplayMode'; +import { GenericEditableRelationCellEditMode } from './GenericEditableRelationCellEditMode'; + +type OwnProps = { + fieldMetadata: EntityFieldMetadata; + editModeHorizontalAlign?: 'left' | 'right'; + placeholder?: string; +}; + +export function GenericEditableRelationCell({ + fieldMetadata, + editModeHorizontalAlign, + placeholder, +}: OwnProps) { + return ( + + } + nonEditModeContent={ + + } + > + ); +} diff --git a/front/src/modules/ui/table/components/GenericEditableRelationCellDisplayMode.tsx b/front/src/modules/ui/table/components/GenericEditableRelationCellDisplayMode.tsx new file mode 100644 index 0000000000..8488516cdb --- /dev/null +++ b/front/src/modules/ui/table/components/GenericEditableRelationCellDisplayMode.tsx @@ -0,0 +1,44 @@ +import { useRecoilValue } from 'recoil'; + +import { CompanyChip } from '@/companies/components/CompanyChip'; +import { Entity } from '@/ui/relation-picker/types/EntityTypeForSelect'; +import { useCurrentRowEntityId } from '@/ui/table/hooks/useCurrentEntityId'; +import { tableEntityFieldFamilySelector } from '@/ui/table/states/tableEntityFieldFamilySelector'; +import { EntityFieldMetadata } from '@/ui/table/types/EntityFieldMetadata'; +import { getLogoUrlFromDomainName } from '~/utils'; + +type OwnProps = { + fieldMetadata: EntityFieldMetadata; + editModeHorizontalAlign?: 'left' | 'right'; + placeholder?: string; +}; + +export function GenericEditableRelationCellDisplayMode({ + fieldMetadata, +}: OwnProps) { + const currentRowEntityId = useCurrentRowEntityId(); + + const fieldValue = useRecoilValue( + tableEntityFieldFamilySelector({ + entityId: currentRowEntityId ?? '', + fieldName: fieldMetadata.fieldName, + }), + ); + + switch (fieldMetadata.relationType) { + case Entity.Company: { + return ( + + ); + } + default: + console.warn( + `Unknown relation type: "${fieldMetadata.relationType}" in GenericEditableRelationCellEditMode`, + ); + return <> ; + } +} diff --git a/front/src/modules/ui/table/components/GenericEditableRelationCellEditMode.tsx b/front/src/modules/ui/table/components/GenericEditableRelationCellEditMode.tsx new file mode 100644 index 0000000000..5fb2311e0e --- /dev/null +++ b/front/src/modules/ui/table/components/GenericEditableRelationCellEditMode.tsx @@ -0,0 +1,68 @@ +import { useRecoilState } from 'recoil'; + +import { CompanyPickerCell } from '@/companies/components/CompanyPickerCell'; +import { useUpdateEntityField } from '@/people/hooks/useUpdateEntityField'; +import { EntityForSelect } from '@/ui/relation-picker/types/EntityForSelect'; +import { Entity } from '@/ui/relation-picker/types/EntityTypeForSelect'; +import { useEditableCell } from '@/ui/table/editable-cell/hooks/useEditableCell'; +import { useCurrentRowEntityId } from '@/ui/table/hooks/useCurrentEntityId'; +import { tableEntityFieldFamilySelector } from '@/ui/table/states/tableEntityFieldFamilySelector'; +import { EntityFieldMetadata } from '@/ui/table/types/EntityFieldMetadata'; + +type OwnProps = { + fieldMetadata: EntityFieldMetadata; +}; + +export function GenericEditableRelationCellEditMode({ + fieldMetadata, +}: OwnProps) { + const currentRowEntityId = useCurrentRowEntityId(); + + const { closeEditableCell } = useEditableCell(); + + const [fieldValueEntity] = useRecoilState( + tableEntityFieldFamilySelector({ + entityId: currentRowEntityId ?? '', + fieldName: fieldMetadata.fieldName, + }), + ); + + const updateEntityField = useUpdateEntityField(); + + function handleEntitySubmit(newFieldEntity: EntityForSelect | null) { + if ( + newFieldEntity?.id !== fieldValueEntity?.id && + currentRowEntityId && + updateEntityField + ) { + updateEntityField( + currentRowEntityId, + fieldMetadata.fieldName, + newFieldEntity, + ); + } + + closeEditableCell(); + } + + function handleCancel() { + closeEditableCell(); + } + + switch (fieldMetadata.relationType) { + case Entity.Company: { + return ( + + ); + } + default: + console.warn( + `Unknown relation type: "${fieldMetadata.relationType}" in GenericEditableRelationCellEditMode`, + ); + return <>; + } +} diff --git a/front/src/modules/people/table/components/GenericEditableTextCell.tsx b/front/src/modules/ui/table/components/GenericEditableTextCell.tsx similarity index 100% rename from front/src/modules/people/table/components/GenericEditableTextCell.tsx rename to front/src/modules/ui/table/components/GenericEditableTextCell.tsx diff --git a/front/src/modules/people/table/components/GenericEditableTextCellEditMode.tsx b/front/src/modules/ui/table/components/GenericEditableTextCellEditMode.tsx similarity index 86% rename from front/src/modules/people/table/components/GenericEditableTextCellEditMode.tsx rename to front/src/modules/ui/table/components/GenericEditableTextCellEditMode.tsx index 6f0010da9b..18058e9af1 100644 --- a/front/src/modules/people/table/components/GenericEditableTextCellEditMode.tsx +++ b/front/src/modules/ui/table/components/GenericEditableTextCellEditMode.tsx @@ -1,7 +1,7 @@ import { useRecoilState } from 'recoil'; +import { useUpdateEntityField } from '@/people/hooks/useUpdateEntityField'; import { InplaceInputTextEditMode } from '@/ui/inplace-input/components/InplaceInputTextEditMode'; -import { useEntityUpdateFieldHook } from '@/ui/table/hooks/useCellUpdateFieldHook'; import { useCurrentRowEntityId } from '@/ui/table/hooks/useCurrentEntityId'; import { tableEntityFieldFamilySelector } from '@/ui/table/states/tableEntityFieldFamilySelector'; @@ -23,8 +23,7 @@ export function GenericEditableTextCellEditMode({ }), ); - const useUpdateField = useEntityUpdateFieldHook(); - const updateField = useUpdateField?.(); + const updateField = useUpdateEntityField(); function handleSubmit(newText: string) { if (newText === fieldValue) return; diff --git a/front/src/modules/ui/table/hooks/useCellUpdateFieldHook.ts b/front/src/modules/ui/table/hooks/useCellUpdateFieldHook.ts deleted file mode 100644 index 9b0ba8d54c..0000000000 --- a/front/src/modules/ui/table/hooks/useCellUpdateFieldHook.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { useContext } from 'react'; - -import { EntityUpdateFieldHookContext } from '../states/EntityUpdateFieldHookContext'; - -export function useEntityUpdateFieldHook() { - return useContext(EntityUpdateFieldHookContext); -} diff --git a/front/src/modules/ui/table/states/EntityUpdateFieldHookContext.ts b/front/src/modules/ui/table/states/EntityUpdateFieldHookContext.ts deleted file mode 100644 index 2ed5eaf308..0000000000 --- a/front/src/modules/ui/table/states/EntityUpdateFieldHookContext.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { createContext } from 'react'; - -import { EntityUpdateFieldHook } from '../types/CellUpdateFieldHook'; - -export const EntityUpdateFieldHookContext = - createContext(null); diff --git a/front/src/modules/ui/table/states/EntityUpdateMutationHookContext.ts b/front/src/modules/ui/table/states/EntityUpdateMutationHookContext.ts new file mode 100644 index 0000000000..621130e58d --- /dev/null +++ b/front/src/modules/ui/table/states/EntityUpdateMutationHookContext.ts @@ -0,0 +1,3 @@ +import { createContext } from 'react'; + +export const EntityUpdateMutationHookContext = createContext(null); diff --git a/front/src/modules/ui/table/types/EntityFieldMetadata.ts b/front/src/modules/ui/table/types/EntityFieldMetadata.ts index fa78483deb..4231306ffb 100644 --- a/front/src/modules/ui/table/types/EntityFieldMetadata.ts +++ b/front/src/modules/ui/table/types/EntityFieldMetadata.ts @@ -1,10 +1,6 @@ -export type EntityFieldType = - | 'text' - | 'number' - | 'date' - | 'select' - | 'checkbox' - | 'icon'; +import { Entity } from '@/ui/relation-picker/types/EntityTypeForSelect'; + +export type EntityFieldType = 'text' | 'relation'; export type EntityFieldMetadata = { fieldName: string; @@ -13,4 +9,5 @@ export type EntityFieldMetadata = { icon: JSX.Element; columnSize: number; filterIcon?: JSX.Element; + relationType?: Entity; // TODO: condition this type with type === "relation" }; diff --git a/front/src/modules/users/components/UserPicker.tsx b/front/src/modules/users/components/UserPicker.tsx new file mode 100644 index 0000000000..5ad2b5a568 --- /dev/null +++ b/front/src/modules/users/components/UserPicker.tsx @@ -0,0 +1,56 @@ +import { useFilteredSearchEntityQuery } from '@/search/hooks/useFilteredSearchEntityQuery'; +import { useRecoilScopedState } from '@/ui/recoil-scope/hooks/useRecoilScopedState'; +import { SingleEntitySelect } from '@/ui/relation-picker/components/SingleEntitySelect'; +import { relationPickerSearchFilterScopedState } from '@/ui/relation-picker/states/relationPickerSearchFilterScopedState'; +import { EntityForSelect } from '@/ui/relation-picker/types/EntityForSelect'; +import { Entity } from '@/ui/relation-picker/types/EntityTypeForSelect'; +import { useSearchUserQuery } from '~/generated/graphql'; + +export type OwnProps = { + userId: string; + onSubmit: (newUserId: string) => void; + onCancel?: () => void; +}; + +type UserForSelect = EntityForSelect & { + entityType: Entity.User; +}; + +export function UserPicker({ userId, onSubmit, onCancel }: OwnProps) { + const [searchFilter] = useRecoilScopedState( + relationPickerSearchFilterScopedState, + ); + + const users = useFilteredSearchEntityQuery({ + queryHook: useSearchUserQuery, + selectedIds: [userId], + searchFilter: searchFilter, + mappingFunction: (user) => ({ + entityType: Entity.User, + id: user.id, + name: user.displayName, + avatarType: 'rounded', + avatarUrl: user.avatarUrl ?? '', + }), + orderByField: 'firstName', + searchOnFields: ['firstName', 'lastName'], + }); + + async function handleEntitySelected( + selectedUser: UserForSelect | null | undefined, + ) { + onSubmit(selectedUser?.id ?? ''); + } + + return ( + + ); +}