From 299bed511f693b3f9e78262359f2346d8ab0dda2 Mon Sep 17 00:00:00 2001 From: gitstart-twenty <140154534+gitstart-twenty@users.noreply.github.com> Date: Thu, 11 Jan 2024 20:26:11 +0100 Subject: [PATCH] Filter the opportunities "Point of contact" field (#3191) * Filter the opportunities "Point of contact" field Co-authored-by: Toledodev Co-authored-by: Rafael Toledo <87545086+Toledodev@users.noreply.github.com> * Refactor according to review Co-authored-by: v1b3m Co-authored-by: Toledodev Co-authored-by: Rafael Toledo <87545086+Toledodev@users.noreply.github.com> --------- Co-authored-by: gitstart-twenty Co-authored-by: Toledodev Co-authored-by: Rafael Toledo <87545086+Toledodev@users.noreply.github.com> Co-authored-by: v1b3m Co-authored-by: Lucas Bordeau --- .../components/AddPersonToCompany.tsx | 99 +++++++++++++++++++ .../components/RelationPicker.tsx | 68 ++++++++++--- .../components/SingleEntitySelect.tsx | 9 +- .../SingleEntitySelectMenuItems.tsx | 90 +++++++++-------- .../SingleEntitySelectMenuItemsWithSearch.tsx | 20 +++- .../types/RelationPickerHotkeyScope.ts | 1 + 6 files changed, 231 insertions(+), 56 deletions(-) create mode 100644 packages/twenty-front/src/modules/companies/components/AddPersonToCompany.tsx diff --git a/packages/twenty-front/src/modules/companies/components/AddPersonToCompany.tsx b/packages/twenty-front/src/modules/companies/components/AddPersonToCompany.tsx new file mode 100644 index 000000000..2a7adb4f4 --- /dev/null +++ b/packages/twenty-front/src/modules/companies/components/AddPersonToCompany.tsx @@ -0,0 +1,99 @@ +import styled from '@emotion/styled'; +import { v4 } from 'uuid'; + +import { FieldDoubleText } from '@/object-record/field/types/FieldDoubleText'; +import { useCreateOneRecord } from '@/object-record/hooks/useCreateOneRecord'; +import { EntityForSelect } from '@/object-record/relation-picker/types/EntityForSelect'; +import { RelationPickerHotkeyScope } from '@/object-record/relation-picker/types/RelationPickerHotkeyScope'; +import { Person } from '@/people/types/Person'; +import { DoubleTextInput } from '@/ui/field/input/components/DoubleTextInput'; +import { usePreviousHotkeyScope } from '@/ui/utilities/hotkey/hooks/usePreviousHotkeyScope'; + +export const StyledInputContainer = styled.div` + background-color: transparent; + box-shadow: ${({ theme }) => theme.boxShadow.strong}; + display: flex; + gap: ${({ theme }) => theme.spacing(0.5)}; + width: ${({ theme }) => theme.spacing(62.5)}; + & input, + div { + background-color: ${({ theme }) => theme.background.primary}; + width: 100%; + } + div { + border-radius: ${({ theme }) => theme.spacing(1)}; + overflow: hidden; + } + input { + display: flex; + flex-grow: 1; + padding: ${({ theme }) => theme.spacing(2)}; + } +`; + +type AddPersonToCompanyProps = { + companyId: string; + onEntitySelected: (entity?: EntityForSelect | undefined) => void; + closeDropdown?: () => void; +}; + +export const AddPersonToCompany = ({ + companyId, + onEntitySelected, + closeDropdown, +}: AddPersonToCompanyProps) => { + const { goBackToPreviousHotkeyScope } = usePreviousHotkeyScope(); + + const handleEscape = () => { + goBackToPreviousHotkeyScope(); + closeDropdown?.(); + }; + + const { createOneRecord: createPerson } = useCreateOneRecord({ + objectNameSingular: 'person', + }); + + const handleCreatePerson = async ({ + firstValue, + secondValue, + }: FieldDoubleText) => { + if (!firstValue && !secondValue) return; + + const person = await createPerson({ + companyId, + id: v4(), + name: { + firstName: firstValue, + lastName: secondValue, + }, + }); + + if (person) { + const entityForSelect: EntityForSelect = { + id: person.id, + name: person.name?.firstName ?? '', + avatarUrl: person.avatarUrl ?? '', + avatarType: 'rounded', + record: person, + }; + onEntitySelected(entityForSelect); + } + goBackToPreviousHotkeyScope(); + closeDropdown?.(); + }; + + return ( + + + + ); +}; diff --git a/packages/twenty-front/src/modules/object-record/relation-picker/components/RelationPicker.tsx b/packages/twenty-front/src/modules/object-record/relation-picker/components/RelationPicker.tsx index 38e624118..2cb353c2f 100644 --- a/packages/twenty-front/src/modules/object-record/relation-picker/components/RelationPicker.tsx +++ b/packages/twenty-front/src/modules/object-record/relation-picker/components/RelationPicker.tsx @@ -1,13 +1,20 @@ -import { useEffect } from 'react'; +import { useContext, useEffect, useState } from 'react'; +import { useRecoilState } from 'recoil'; +import { AddPersonToCompany } from '@/companies/components/AddPersonToCompany'; +import { companyProgressesFamilyState } from '@/companies/states/companyProgressesFamilyState'; import { useObjectNameSingularFromPlural } from '@/object-metadata/hooks/useObjectNameSingularFromPlural'; import { FieldDefinition } from '@/object-record/field/types/FieldDefinition'; import { FieldRelationMetadata } from '@/object-record/field/types/FieldMetadata'; +import { BoardCardIdContext } from '@/object-record/record-board/contexts/BoardCardIdContext'; import { SingleEntitySelect } from '@/object-record/relation-picker/components/SingleEntitySelect'; import { useRelationPicker } from '@/object-record/relation-picker/hooks/useRelationPicker'; import { EntityForSelect } from '@/object-record/relation-picker/types/EntityForSelect'; +import { RelationPickerHotkeyScope } from '@/object-record/relation-picker/types/RelationPickerHotkeyScope'; import { useFilteredSearchEntityQuery } from '@/search/hooks/useFilteredSearchEntityQuery'; import { IconForbid } from '@/ui/display/icon'; +import { usePreviousHotkeyScope } from '@/ui/utilities/hotkey/hooks/usePreviousHotkeyScope'; +import { isDefined } from '~/utils/isDefined'; export type RelationPickerProps = { recordId?: string; @@ -35,10 +42,24 @@ export const RelationPicker = ({ searchQuery, } = useRelationPicker(); + const [showAddNewDropdown, setShowAddNewDropdown] = useState(false); + + const { setHotkeyScopeAndMemorizePreviousScope } = usePreviousHotkeyScope(); + useEffect(() => { setRelationPickerSearchFilter(initialSearchFilter ?? ''); }, [initialSearchFilter, setRelationPickerSearchFilter]); + const boardCardId = useContext(BoardCardIdContext); + const weAreInOpportunitiesPageCard = isDefined(boardCardId); + + const [companyProgress] = useRecoilState( + companyProgressesFamilyState(boardCardId ?? ''), + ); + + const { company } = companyProgress ?? {}; + const companyId = company?.id; + const { objectNameSingular: relationObjectNameSingular } = useObjectNameSingularFromPlural({ objectNamePlural: @@ -69,16 +90,41 @@ export const RelationPicker = ({ const handleEntitySelected = (selectedEntity: any | null | undefined) => onSubmit(selectedEntity ?? null); + const entitiesToSelect = entities.entitiesToSelect.filter((entity) => + weAreInOpportunitiesPageCard ? entity.record.companyId === companyId : true, + ); + + const weAreAddingNewPerson = + weAreInOpportunitiesPageCard && showAddNewDropdown && companyId; + return ( - + <> + {!weAreAddingNewPerson ? ( + { + if (weAreInOpportunitiesPageCard) { + setShowAddNewDropdown(true); + setHotkeyScopeAndMemorizePreviousScope( + RelationPickerHotkeyScope.AddNew, + ); + } + }} + /> + ) : ( + setShowAddNewDropdown(false)} + /> + )} + ); }; diff --git a/packages/twenty-front/src/modules/object-record/relation-picker/components/SingleEntitySelect.tsx b/packages/twenty-front/src/modules/object-record/relation-picker/components/SingleEntitySelect.tsx index 93b40525a..d51d63f58 100644 --- a/packages/twenty-front/src/modules/object-record/relation-picker/components/SingleEntitySelect.tsx +++ b/packages/twenty-front/src/modules/object-record/relation-picker/components/SingleEntitySelect.tsx @@ -30,7 +30,14 @@ export const SingleEntitySelect = ({ refs: [containerRef], callback: (event) => { event.stopImmediatePropagation(); - onCancel?.(); + + const weAreNotInAnHTMLInput = !( + event.target instanceof HTMLInputElement && + event.target.tagName === 'INPUT' + ); + if (weAreNotInAnHTMLInput && onCancel) { + onCancel(); + } }, }); diff --git a/packages/twenty-front/src/modules/object-record/relation-picker/components/SingleEntitySelectMenuItems.tsx b/packages/twenty-front/src/modules/object-record/relation-picker/components/SingleEntitySelectMenuItems.tsx index 88352c0b6..05356e89d 100644 --- a/packages/twenty-front/src/modules/object-record/relation-picker/components/SingleEntitySelectMenuItems.tsx +++ b/packages/twenty-front/src/modules/object-record/relation-picker/components/SingleEntitySelectMenuItems.tsx @@ -1,7 +1,8 @@ -import { useRef } from 'react'; +import { useContext, useRef } from 'react'; import { isNonEmptyString } from '@sniptt/guards'; import { Key } from 'ts-key-enum'; +import { BoardCardIdContext } from '@/object-record/record-board/contexts/BoardCardIdContext'; import { SelectableMenuItemSelect } from '@/object-record/relation-picker/components/SelectableMenuItemSelect'; import { IconPlus } from '@/ui/display/icon'; import { IconComponent } from '@/ui/display/icon/types/IconComponent'; @@ -14,6 +15,7 @@ import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem'; import { MenuItemSelect } from '@/ui/navigation/menu-item/components/MenuItemSelect'; import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys'; import { assertNotNull } from '~/utils/assert'; +import { isDefined } from '~/utils/isDefined'; import { EntityForSelect } from '../types/EntityForSelect'; import { RelationPickerHotkeyScope } from '../types/RelationPickerHotkeyScope'; @@ -69,6 +71,12 @@ export const SingleEntitySelectMenuItems = ({ const selectableItemIds = entitiesInDropdown.map((entity) => entity.id); + const boardCardId = useContext(BoardCardIdContext); + const weAreInOpportunitiesPageCard = isDefined(boardCardId); + + const hideSearchResults = + weAreInOpportunitiesPageCard && !entitiesInDropdown.length; + return (
- - {loading ? ( - - ) : entitiesInDropdown.length === 0 && !isAllEntitySelectShown ? ( - - ) : ( - <> - {isAllEntitySelectShown && - selectAllLabel && - onAllEntitySelected && ( - onAllEntitySelected()} - LeftIcon={SelectAllIcon} - text={selectAllLabel} - selected={!!isAllEntitySelected} - /> - )} - {emptyLabel && ( - onEntitySelected()} - LeftIcon={EmptyIcon} - text={emptyLabel} - selected={!selectedEntity} - /> + {!hideSearchResults && ( + <> + + {loading ? ( + + ) : entitiesInDropdown.length === 0 && !isAllEntitySelectShown ? ( + + ) : ( + <> + {isAllEntitySelectShown && + selectAllLabel && + onAllEntitySelected && ( + onAllEntitySelected()} + LeftIcon={SelectAllIcon} + text={selectAllLabel} + selected={!!isAllEntitySelected} + /> + )} + {emptyLabel && ( + onEntitySelected()} + LeftIcon={EmptyIcon} + text={emptyLabel} + selected={!selectedEntity} + /> + )} + )} + + {entitiesInDropdown?.map((entity) => ( ))} - - )} - - {showCreateButton && ( - <> - - - )} + {(hideSearchResults || showCreateButton) && !loading && ( + + {entitiesToSelect.length > 0 && } + + + )}
); diff --git a/packages/twenty-front/src/modules/object-record/relation-picker/components/SingleEntitySelectMenuItemsWithSearch.tsx b/packages/twenty-front/src/modules/object-record/relation-picker/components/SingleEntitySelectMenuItemsWithSearch.tsx index ddbb450f0..eab1908ef 100644 --- a/packages/twenty-front/src/modules/object-record/relation-picker/components/SingleEntitySelectMenuItemsWithSearch.tsx +++ b/packages/twenty-front/src/modules/object-record/relation-picker/components/SingleEntitySelectMenuItemsWithSearch.tsx @@ -1,3 +1,6 @@ +import { useContext } from 'react'; + +import { BoardCardIdContext } from '@/object-record/record-board/contexts/BoardCardIdContext'; import { SingleEntitySelectMenuItems, SingleEntitySelectMenuItemsProps, @@ -35,13 +38,20 @@ export const SingleEntitySelectMenuItemsWithSearch = ({ const showCreateButton = isDefined(onCreate) && searchFilter !== ''; + const boardCardId = useContext(BoardCardIdContext); + const weAreInOpportunitiesPageCard = isDefined(boardCardId); + const hideSearchInput = + weAreInOpportunitiesPageCard && !entitiesToSelect.length && !selectedEntity; + return ( <> - + {!hideSearchInput && ( + + )}