Introduce main identifier to power RelationFieldDisplay (#2577)
* Introduce main identifier to power RelationFieldDisplay, FilterDrodown, TableFirstColumn * Apply to RelationPicker
This commit is contained in:
@ -36,7 +36,7 @@ export const ActivityTargetChips = ({
|
||||
key={company.id}
|
||||
id={company.id}
|
||||
name={company.name}
|
||||
pictureUrl={getLogoUrlFromDomainName(company.domainName)}
|
||||
avatarUrl={getLogoUrlFromDomainName(company.domainName)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@ -46,7 +46,7 @@ export const ActivityTargetChips = ({
|
||||
key={person.id}
|
||||
id={person.id}
|
||||
name={person.name.firstName + ' ' + person.name.lastName}
|
||||
pictureUrl={person.avatarUrl ?? undefined}
|
||||
avatarUrl={person.avatarUrl ?? undefined}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@ -4,10 +4,6 @@ import styled from '@emotion/styled';
|
||||
import { useHandleCheckableActivityTargetChange } from '@/activities/hooks/useHandleCheckableActivityTargetChange';
|
||||
import { Activity } from '@/activities/types/Activity';
|
||||
import { ActivityTarget } from '@/activities/types/ActivityTarget';
|
||||
import { flatMapAndSortEntityForSelectArrayOfArrayByName } from '@/activities/utils/flatMapAndSortEntityForSelectArrayByName';
|
||||
import { useFilteredSearchCompanyQuery } from '@/companies/hooks/useFilteredSearchCompanyQuery';
|
||||
import { useFilteredSearchPeopleQuery } from '@/people/hooks/useFilteredSearchPeopleQuery';
|
||||
import { MultipleEntitySelect } from '@/ui/input/relation-picker/components/MultipleEntitySelect';
|
||||
import { useInlineCell } from '@/ui/object/record-inline-cell/hooks/useInlineCell';
|
||||
import { assertNotNull } from '~/utils/assert';
|
||||
|
||||
@ -60,31 +56,31 @@ export const ActivityRelationEditableFieldEditMode = ({
|
||||
Record<string, boolean>
|
||||
>(initialSelectedEntityIds);
|
||||
|
||||
const personsForMultiSelect = useFilteredSearchPeopleQuery({
|
||||
searchFilter,
|
||||
selectedIds: initialPeopleIds,
|
||||
});
|
||||
// const personsForMultiSelect = useFilteredSearchPeopleQuery({
|
||||
// searchFilter,
|
||||
// selectedIds: initialPeopleIds,
|
||||
// });
|
||||
|
||||
const companiesForMultiSelect = useFilteredSearchCompanyQuery({
|
||||
searchFilter,
|
||||
selectedIds: initialCompanyIds,
|
||||
});
|
||||
// const companiesForMultiSelect = useFilteredSearchCompanyQuery({
|
||||
// searchFilter,
|
||||
// selectedIds: initialCompanyIds,
|
||||
// });
|
||||
|
||||
const selectedEntities = flatMapAndSortEntityForSelectArrayOfArrayByName([
|
||||
personsForMultiSelect.selectedEntities,
|
||||
companiesForMultiSelect.selectedEntities,
|
||||
]);
|
||||
// const selectedEntities = flatMapAndSortEntityForSelectArrayOfArrayByName([
|
||||
// personsForMultiSelect.selectedEntities,
|
||||
// companiesForMultiSelect.selectedEntities,
|
||||
// ]);
|
||||
|
||||
const filteredSelectedEntities =
|
||||
flatMapAndSortEntityForSelectArrayOfArrayByName([
|
||||
personsForMultiSelect.filteredSelectedEntities,
|
||||
companiesForMultiSelect.filteredSelectedEntities,
|
||||
]);
|
||||
// const filteredSelectedEntities =
|
||||
// flatMapAndSortEntityForSelectArrayOfArrayByName([
|
||||
// personsForMultiSelect.filteredSelectedEntities,
|
||||
// companiesForMultiSelect.filteredSelectedEntities,
|
||||
// ]);
|
||||
|
||||
const entitiesToSelect = flatMapAndSortEntityForSelectArrayOfArrayByName([
|
||||
personsForMultiSelect.entitiesToSelect,
|
||||
companiesForMultiSelect.entitiesToSelect,
|
||||
]);
|
||||
// const entitiesToSelect = flatMapAndSortEntityForSelectArrayOfArrayByName([
|
||||
// personsForMultiSelect.entitiesToSelect,
|
||||
// companiesForMultiSelect.entitiesToSelect,
|
||||
// ]);
|
||||
|
||||
const handleCheckItemsChange = useHandleCheckableActivityTargetChange({
|
||||
activity,
|
||||
@ -102,7 +98,7 @@ export const ActivityRelationEditableFieldEditMode = ({
|
||||
|
||||
return (
|
||||
<StyledSelectContainer>
|
||||
<MultipleEntitySelect
|
||||
{/* <MultipleEntitySelect
|
||||
entities={{
|
||||
entitiesToSelect,
|
||||
filteredSelectedEntities,
|
||||
@ -115,7 +111,7 @@ export const ActivityRelationEditableFieldEditMode = ({
|
||||
value={selectedEntityIds}
|
||||
onCancel={handleCancel}
|
||||
onSubmit={handleSubmit}
|
||||
/>
|
||||
/> */}
|
||||
</StyledSelectContainer>
|
||||
);
|
||||
};
|
||||
|
||||
@ -49,7 +49,7 @@ export const TimelineActivityCardFooter = ({
|
||||
' ' +
|
||||
activity.assignee.name.lastName ?? ''
|
||||
}
|
||||
pictureUrl={activity.assignee.avatarUrl ?? ''}
|
||||
avatarUrl={activity.assignee.avatarUrl ?? ''}
|
||||
/>
|
||||
)}
|
||||
|
||||
|
||||
@ -17,7 +17,7 @@ export const SmallName: Story = {
|
||||
args: {
|
||||
id: 'airbnb',
|
||||
name: 'Airbnb',
|
||||
pictureUrl: 'https://api.faviconkit.com/airbnb.com/144',
|
||||
avatarUrl: 'https://api.faviconkit.com/airbnb.com/144',
|
||||
},
|
||||
};
|
||||
|
||||
@ -25,6 +25,6 @@ export const BigName: Story = {
|
||||
args: {
|
||||
id: 'google',
|
||||
name: 'Google with a real big name to overflow the cell',
|
||||
pictureUrl: 'https://api.faviconkit.com/google.com/144',
|
||||
avatarUrl: 'https://api.faviconkit.com/google.com/144',
|
||||
},
|
||||
};
|
||||
|
||||
@ -3,10 +3,6 @@ import styled from '@emotion/styled';
|
||||
import { flip, offset, useFloating } from '@floating-ui/react';
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
import {
|
||||
PeoplePicker,
|
||||
PersonForSelect,
|
||||
} from '@/people/components/PeoplePicker';
|
||||
import { IconPlus } from '@/ui/display/icon';
|
||||
import { LightIconButton } from '@/ui/input/button/components/LightIconButton';
|
||||
import { RelationPickerHotkeyScope } from '@/ui/input/relation-picker/types/RelationPickerHotkeyScope';
|
||||
@ -67,7 +63,7 @@ export const AddPersonToCompany = ({
|
||||
} = usePreviousHotkeyScope();
|
||||
|
||||
const handlePersonSelected =
|
||||
(companyId: string) => async (newPerson: PersonForSelect | null) => {
|
||||
(companyId: string) => async (newPerson: any | null) => {
|
||||
if (newPerson) {
|
||||
// await updatePerson({
|
||||
// variables: {
|
||||
@ -146,13 +142,14 @@ export const AddPersonToCompany = ({
|
||||
/>
|
||||
</StyledInputContainer>
|
||||
) : (
|
||||
<PeoplePicker
|
||||
personId={''}
|
||||
onSubmit={handlePersonSelected(companyId)}
|
||||
onCancel={handleClosePicker}
|
||||
onCreate={() => setIsCreationDropdownOpen(true)}
|
||||
excludePersonIds={peopleIds}
|
||||
/>
|
||||
<>todo</>
|
||||
// <PeoplePicker
|
||||
// personId={''}
|
||||
// onSubmit={handlePersonSelected(companyId)}
|
||||
// onCancel={handleClosePicker}
|
||||
// onCreate={() => setIsCreationDropdownOpen(true)}
|
||||
// excludePersonIds={peopleIds}
|
||||
// />
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
@ -208,7 +208,7 @@ export const CompanyBoardCard = () => {
|
||||
<CompanyChip
|
||||
id={company.id}
|
||||
name={company.name}
|
||||
pictureUrl={getLogoUrlFromDomainName(company.domainName)}
|
||||
avatarUrl={getLogoUrlFromDomainName(company.domainName)}
|
||||
variant={EntityChipVariant.Transparent}
|
||||
/>
|
||||
{showCompactView && (
|
||||
@ -239,14 +239,13 @@ export const CompanyBoardCard = () => {
|
||||
value={{
|
||||
entityId: boardCardId,
|
||||
recoilScopeId: boardCardId + viewField.fieldMetadataId,
|
||||
isMainIdentifier: false,
|
||||
fieldDefinition: {
|
||||
fieldMetadataId: viewField.fieldMetadataId,
|
||||
label: viewField.label,
|
||||
iconName: viewField.iconName,
|
||||
type: viewField.type,
|
||||
metadata: viewField.metadata,
|
||||
entityChipDisplayMapper:
|
||||
viewField.entityChipDisplayMapper,
|
||||
},
|
||||
useUpdateEntityMutation: useUpdateOneObjectMutation,
|
||||
hotkeyScope: InlineCellHotkeyScope.InlineCell,
|
||||
|
||||
@ -6,14 +6,14 @@ import {
|
||||
type CompanyChipProps = {
|
||||
id: string;
|
||||
name: string;
|
||||
pictureUrl?: string;
|
||||
avatarUrl?: string;
|
||||
variant?: EntityChipVariant;
|
||||
};
|
||||
|
||||
export const CompanyChip = ({
|
||||
id,
|
||||
name,
|
||||
pictureUrl,
|
||||
avatarUrl,
|
||||
variant = EntityChipVariant.Regular,
|
||||
}: CompanyChipProps) => (
|
||||
<EntityChip
|
||||
@ -21,7 +21,7 @@ export const CompanyChip = ({
|
||||
linkToEntity={`/objects/companies/${id}`}
|
||||
name={name}
|
||||
avatarType="squared"
|
||||
pictureUrl={pictureUrl}
|
||||
avatarUrl={avatarUrl}
|
||||
variant={variant}
|
||||
/>
|
||||
);
|
||||
|
||||
@ -1,78 +0,0 @@
|
||||
import { useEffect } from 'react';
|
||||
import { useQuery } from '@apollo/client';
|
||||
|
||||
import { useFindOneObjectMetadataItem } from '@/object-metadata/hooks/useFindOneObjectMetadataItem';
|
||||
import { useFilteredSearchEntityQueryV2 } from '@/search/hooks/useFilteredSearchEntityQueryV2';
|
||||
import { SingleEntitySelect } from '@/ui/input/relation-picker/components/SingleEntitySelect';
|
||||
import { relationPickerSearchFilterScopedState } from '@/ui/input/relation-picker/states/relationPickerSearchFilterScopedState';
|
||||
import { EntityForSelect } from '@/ui/input/relation-picker/types/EntityForSelect';
|
||||
import { Entity } from '@/ui/input/relation-picker/types/EntityTypeForSelect';
|
||||
import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
|
||||
import { getLogoUrlFromDomainName } from '~/utils';
|
||||
|
||||
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 { findManyQuery } = useFindOneObjectMetadataItem({
|
||||
objectNamePlural: 'companies',
|
||||
});
|
||||
|
||||
const useFindManyCompanies = (options: any) =>
|
||||
useQuery(findManyQuery, options);
|
||||
|
||||
const companies = useFilteredSearchEntityQueryV2({
|
||||
queryHook: useFindManyCompanies,
|
||||
filters: [
|
||||
{
|
||||
fieldNames: ['name'],
|
||||
filter: relationPickerSearchFilter,
|
||||
},
|
||||
],
|
||||
orderByField: 'name',
|
||||
mappingFunction: (company) => ({
|
||||
entityType: Entity.Company,
|
||||
id: company.id,
|
||||
name: company.name,
|
||||
avatarType: 'squared',
|
||||
avatarUrl: getLogoUrlFromDomainName(company.domainName),
|
||||
originalEntity: company,
|
||||
}),
|
||||
selectedIds: companyId ? [companyId] : [],
|
||||
objectNamePlural: 'companies',
|
||||
});
|
||||
|
||||
const handleEntitySelected = async (
|
||||
selectedCompany: EntityForSelect | null | undefined,
|
||||
) => {
|
||||
onSubmit(selectedCompany ?? null);
|
||||
};
|
||||
|
||||
return (
|
||||
<SingleEntitySelect
|
||||
entitiesToSelect={companies.entitiesToSelect}
|
||||
loading={companies.loading}
|
||||
onCancel={onCancel}
|
||||
onEntitySelected={handleEntitySelected}
|
||||
selectedEntity={companies.selectedEntities[0]}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@ -3,7 +3,6 @@ import { useRecoilState } from 'recoil';
|
||||
|
||||
import { currentPipelineState } from '@/pipeline/states/currentPipelineState';
|
||||
import { IconChevronDown } from '@/ui/display/icon';
|
||||
import { SingleEntitySelectBase } from '@/ui/input/relation-picker/components/SingleEntitySelectBase';
|
||||
import { useEntitySelectSearch } from '@/ui/input/relation-picker/hooks/useEntitySelectSearch';
|
||||
import { EntityForSelect } from '@/ui/input/relation-picker/types/EntityForSelect';
|
||||
import { DropdownMenu } from '@/ui/layout/dropdown/components/DropdownMenu';
|
||||
@ -14,8 +13,6 @@ import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownM
|
||||
import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem';
|
||||
import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope';
|
||||
|
||||
import { useFilteredSearchCompanyQuery } from '../hooks/useFilteredSearchCompanyQuery';
|
||||
|
||||
export type CompanyProgressPickerProps = {
|
||||
companyId: string | null;
|
||||
onSubmit: (
|
||||
@ -34,10 +31,10 @@ export const CompanyProgressPicker = ({
|
||||
|
||||
const { searchFilter, handleSearchFilterChange } = useEntitySelectSearch();
|
||||
|
||||
const companies = useFilteredSearchCompanyQuery({
|
||||
searchFilter,
|
||||
selectedIds: companyId ? [companyId] : [],
|
||||
});
|
||||
// const companies = useFilteredSearchCompanyQuery({
|
||||
// searchFilter,
|
||||
// selectedIds: companyId ? [companyId] : [],
|
||||
// });
|
||||
|
||||
const [isProgressSelectionUnfolded, setIsProgressSelectionUnfolded] =
|
||||
useState(false);
|
||||
@ -113,13 +110,13 @@ export const CompanyProgressPicker = ({
|
||||
/>
|
||||
<DropdownMenuSeparator />
|
||||
<RecoilScope>
|
||||
<SingleEntitySelectBase
|
||||
{/* <SingleEntitySelectBase
|
||||
entitiesToSelect={companies.entitiesToSelect}
|
||||
loading={companies.loading}
|
||||
onCancel={onCancel}
|
||||
onEntitySelected={handleEntitySelected}
|
||||
selectedEntity={companies.selectedEntities[0]}
|
||||
/>
|
||||
/> */}
|
||||
</RecoilScope>
|
||||
</>
|
||||
)}
|
||||
|
||||
@ -1,24 +0,0 @@
|
||||
import { ObjectFilterDropdownEntitySearchSelect } from '@/ui/object/object-filter-dropdown/components/ObjectFilterDropdownEntitySearchSelect';
|
||||
import { useFilter } from '@/ui/object/object-filter-dropdown/hooks/useFilter';
|
||||
|
||||
import { useFilteredSearchCompanyQuery } from '../hooks/useFilteredSearchCompanyQuery';
|
||||
|
||||
export const FilterDropdownCompanySearchSelect = () => {
|
||||
const {
|
||||
objectFilterDropdownSearchInput,
|
||||
objectFilterDropdownSelectedEntityId,
|
||||
} = useFilter();
|
||||
|
||||
const usersForSelect = useFilteredSearchCompanyQuery({
|
||||
searchFilter: objectFilterDropdownSearchInput,
|
||||
selectedIds: objectFilterDropdownSelectedEntityId
|
||||
? [objectFilterDropdownSelectedEntityId]
|
||||
: [],
|
||||
});
|
||||
|
||||
return (
|
||||
<ObjectFilterDropdownEntitySearchSelect
|
||||
entitiesForSelect={usersForSelect}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@ -5,7 +5,6 @@ import { useRecoilState, useRecoilValue } from 'recoil';
|
||||
import { Company } from '@/companies/types/Company';
|
||||
import { useFindManyObjectRecords } from '@/object-record/hooks/useFindManyObjectRecords';
|
||||
import { PaginatedObjectTypeResults } from '@/object-record/types/PaginatedObjectTypeResults';
|
||||
import { pipelineAvailableFieldDefinitions } from '@/pipeline/constants/pipelineAvailableFieldDefinitions';
|
||||
import { Opportunity } from '@/pipeline/types/Opportunity';
|
||||
import { PipelineStep } from '@/pipeline/types/PipelineStep';
|
||||
import { useBoardActionBarEntries } from '@/ui/layout/board/hooks/useBoardActionBarEntries';
|
||||
@ -117,7 +116,7 @@ export const HooksCompanyBoardEffect = () => {
|
||||
useEffect(() => {
|
||||
setAvailableFilterDefinitions(opportunitiesBoardOptions.filterDefinitions);
|
||||
setAvailableSortDefinitions?.(opportunitiesBoardOptions.sortDefinitions);
|
||||
setAvailableFieldDefinitions?.(pipelineAvailableFieldDefinitions);
|
||||
setAvailableFieldDefinitions?.([]);
|
||||
}, [
|
||||
setAvailableFieldDefinitions,
|
||||
setAvailableFilterDefinitions,
|
||||
@ -140,7 +139,7 @@ export const HooksCompanyBoardEffect = () => {
|
||||
if (!loading && opportunities && companies) {
|
||||
setActionBarEntries();
|
||||
setContextMenuEntries();
|
||||
setAvailableBoardCardFields(pipelineAvailableFieldDefinitions);
|
||||
setAvailableBoardCardFields([]);
|
||||
updateCompanyBoard(pipelineSteps, opportunities, companies);
|
||||
setEntityCountInCurrentView(companies.length);
|
||||
}
|
||||
@ -160,10 +159,7 @@ export const HooksCompanyBoardEffect = () => {
|
||||
useEffect(() => {
|
||||
if (currentViewFields) {
|
||||
setBoardCardFields(
|
||||
mapViewFieldsToBoardFieldDefinitions(
|
||||
currentViewFields,
|
||||
pipelineAvailableFieldDefinitions,
|
||||
),
|
||||
mapViewFieldsToBoardFieldDefinitions(currentViewFields, []),
|
||||
);
|
||||
}
|
||||
}, [currentViewFields, setBoardCardFields]);
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
import { useCallback, useContext, useState } from 'react';
|
||||
|
||||
import { useSnackBar } from '@/ui/feedback/snack-bar/hooks/useSnackBar';
|
||||
import { SingleEntitySelect } from '@/ui/input/relation-picker/components/SingleEntitySelect';
|
||||
import { relationPickerSearchFilterScopedState } from '@/ui/input/relation-picker/states/relationPickerSearchFilterScopedState';
|
||||
import { RelationPickerHotkeyScope } from '@/ui/input/relation-picker/types/RelationPickerHotkeyScope';
|
||||
import { NewButton } from '@/ui/layout/board/components/NewButton';
|
||||
@ -9,8 +8,6 @@ import { BoardColumnContext } from '@/ui/layout/board/contexts/BoardColumnContex
|
||||
import { usePreviousHotkeyScope } from '@/ui/utilities/hotkey/hooks/usePreviousHotkeyScope';
|
||||
import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
|
||||
|
||||
import { useFilteredSearchCompanyQuery } from '../hooks/useFilteredSearchCompanyQuery';
|
||||
|
||||
export const NewCompanyProgressButton = () => {
|
||||
const [isCreatingCard, setIsCreatingCard] = useState(false);
|
||||
const column = useContext(BoardColumnContext);
|
||||
@ -55,22 +52,23 @@ export const NewCompanyProgressButton = () => {
|
||||
relationPickerSearchFilterScopedState,
|
||||
);
|
||||
|
||||
const companies = useFilteredSearchCompanyQuery({
|
||||
searchFilter: relationPickerSearchFilter,
|
||||
});
|
||||
// const companies = useFilteredSearchCompanyQuery({
|
||||
// searchFilter: relationPickerSearchFilter,
|
||||
// });
|
||||
|
||||
return (
|
||||
<>
|
||||
{isCreatingCard ? (
|
||||
<SingleEntitySelect
|
||||
disableBackgroundBlur
|
||||
entitiesToSelect={companies.entitiesToSelect}
|
||||
loading={companies.loading}
|
||||
onCancel={handleCancel}
|
||||
onEntitySelected={handleEntitySelect}
|
||||
selectedEntity={companies.selectedEntities[0]}
|
||||
/>
|
||||
<>TODO</>
|
||||
) : (
|
||||
// <SingleEntitySelect
|
||||
// disableBackgroundBlur
|
||||
// entitiesToSelect={companies.entitiesToSelect}
|
||||
// loading={companies.loading}
|
||||
// onCancel={handleCancel}
|
||||
// onEntitySelected={handleEntitySelect}
|
||||
// selectedEntity={companies.selectedEntities[0]}
|
||||
// />
|
||||
<NewButton onClick={handleNewClick} />
|
||||
)}
|
||||
</>
|
||||
|
||||
@ -1,44 +0,0 @@
|
||||
import { useQuery } from '@apollo/client';
|
||||
|
||||
import { useFindOneObjectMetadataItem } from '@/object-metadata/hooks/useFindOneObjectMetadataItem';
|
||||
import { useFilteredSearchEntityQueryV2 } from '@/search/hooks/useFilteredSearchEntityQueryV2';
|
||||
import { Entity } from '@/ui/input/relation-picker/types/EntityTypeForSelect';
|
||||
|
||||
export const useFilteredSearchCompanyQuery = ({
|
||||
searchFilter,
|
||||
selectedIds = [],
|
||||
limit,
|
||||
}: {
|
||||
searchFilter: string;
|
||||
selectedIds?: string[];
|
||||
limit?: number;
|
||||
}) => {
|
||||
const { findManyQuery } = useFindOneObjectMetadataItem({
|
||||
objectNameSingular: 'company',
|
||||
});
|
||||
|
||||
const useFindManyCompanies = (options: any) =>
|
||||
useQuery(findManyQuery, options);
|
||||
|
||||
return useFilteredSearchEntityQueryV2({
|
||||
queryHook: useFindManyCompanies,
|
||||
filters: [
|
||||
{
|
||||
fieldNames: ['name.firstName', 'name.lastName'],
|
||||
filter: searchFilter,
|
||||
},
|
||||
],
|
||||
orderByField: 'createdAt',
|
||||
mappingFunction: (company) => ({
|
||||
entityType: Entity.Company,
|
||||
id: company.id,
|
||||
name: company.name,
|
||||
avatarType: 'squared',
|
||||
avatarUrl: '',
|
||||
originalEntity: company,
|
||||
}),
|
||||
selectedIds: selectedIds,
|
||||
objectNamePlural: 'workspaceMembers',
|
||||
limit,
|
||||
});
|
||||
};
|
||||
@ -26,7 +26,6 @@ export const useComputeDefinitionsFromFieldMetadata = (
|
||||
formatFieldMetadataItemAsColumnDefinition({
|
||||
position: index,
|
||||
field,
|
||||
objectMetadataItem: objectMetadataItem,
|
||||
}),
|
||||
);
|
||||
|
||||
|
||||
@ -8,7 +8,7 @@ export const useMapFieldMetadataToGraphQLQuery = () => {
|
||||
|
||||
const mapFieldMetadataToGraphQLQuery = (
|
||||
field: FieldMetadataItem,
|
||||
maxDepthForRelations: number = 1,
|
||||
maxDepthForRelations: number = 2,
|
||||
): any => {
|
||||
if (maxDepthForRelations <= 0) {
|
||||
return '';
|
||||
|
||||
@ -0,0 +1,56 @@
|
||||
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { MainIdentifierMapper } from '@/ui/object/field/types/MainIdentifierMapper';
|
||||
|
||||
export const useObjectMainIdentifier = (
|
||||
objectMetadataItem?: ObjectMetadataItem,
|
||||
) => {
|
||||
if (!objectMetadataItem) {
|
||||
return {
|
||||
mainIdentifierMapper: undefined,
|
||||
mainIdentifierFieldMetadataId: undefined,
|
||||
basePathToShowPage: undefined,
|
||||
};
|
||||
}
|
||||
|
||||
const mainIdentifierMapper: MainIdentifierMapper = (record: any) => {
|
||||
if (objectMetadataItem.nameSingular === 'company') {
|
||||
return {
|
||||
id: record.id,
|
||||
name: record.name,
|
||||
avatarUrl: record.avatarUrl,
|
||||
avatarType: 'squared',
|
||||
record: record,
|
||||
};
|
||||
}
|
||||
|
||||
if (objectMetadataItem.nameSingular === 'workspaceMember') {
|
||||
return {
|
||||
id: record.id,
|
||||
name: record.name.firstName + ' ' + record.name.lastName,
|
||||
avatarUrl: record.avatarUrl,
|
||||
avatarType: 'rounded',
|
||||
record: record,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
id: record.id,
|
||||
name: record.name,
|
||||
avatarUrl: record.avatarUrl,
|
||||
avatarType: 'rounded',
|
||||
record: record,
|
||||
};
|
||||
};
|
||||
|
||||
const mainIdentifierFieldMetadataId = objectMetadataItem.fields.find(
|
||||
({ name }) => name === 'name',
|
||||
)?.id;
|
||||
|
||||
const basePathToShowPage = `/object/${objectMetadataItem.nameSingular}/`;
|
||||
|
||||
return {
|
||||
mainIdentifierMapper,
|
||||
mainIdentifierFieldMetadataId,
|
||||
basePathToShowPage,
|
||||
};
|
||||
};
|
||||
@ -6,12 +6,18 @@ export type FieldMetadataItem = Omit<
|
||||
> & {
|
||||
fromRelationMetadata?:
|
||||
| (Pick<Relation, 'id' | 'toFieldMetadataId' | 'relationType'> & {
|
||||
toObjectMetadata: Pick<Relation['toObjectMetadata'], 'id'>;
|
||||
toObjectMetadata: Pick<
|
||||
Relation['toObjectMetadata'],
|
||||
'id' | 'nameSingular' | 'namePlural'
|
||||
>;
|
||||
})
|
||||
| null;
|
||||
toRelationMetadata?:
|
||||
| (Pick<Relation, 'id' | 'fromFieldMetadataId' | 'relationType'> & {
|
||||
fromObjectMetadata: Pick<Relation['fromObjectMetadata'], 'id'>;
|
||||
fromObjectMetadata: Pick<
|
||||
Relation['fromObjectMetadata'],
|
||||
'id' | 'nameSingular' | 'namePlural'
|
||||
>;
|
||||
})
|
||||
| null;
|
||||
};
|
||||
|
||||
@ -1,32 +1,68 @@
|
||||
import { parseFieldRelationType } from '@/object-metadata/utils/parseFieldRelationType';
|
||||
import { FieldMetadata } from '@/ui/object/field/types/FieldMetadata';
|
||||
import { MainIdentifierMapper } from '@/ui/object/field/types/MainIdentifierMapper';
|
||||
import { ColumnDefinition } from '@/ui/object/record-table/types/ColumnDefinition';
|
||||
import { getLogoUrlFromDomainName } from '~/utils';
|
||||
|
||||
import { FieldMetadataItem } from '../types/FieldMetadataItem';
|
||||
import { ObjectMetadataItem } from '../types/ObjectMetadataItem';
|
||||
|
||||
import { parseFieldType } from './parseFieldType';
|
||||
|
||||
export const formatFieldMetadataItemAsColumnDefinition = ({
|
||||
position,
|
||||
field,
|
||||
objectMetadataItem,
|
||||
}: {
|
||||
position: number;
|
||||
field: FieldMetadataItem;
|
||||
objectMetadataItem: Omit<ObjectMetadataItem, 'fields'>;
|
||||
}): ColumnDefinition<FieldMetadata> => ({
|
||||
position,
|
||||
fieldMetadataId: field.id,
|
||||
label: field.label,
|
||||
size: 100,
|
||||
type: parseFieldType(field.type),
|
||||
metadata: {
|
||||
fieldName: field.name,
|
||||
placeHolder: field.label,
|
||||
},
|
||||
iconName: field.icon ?? 'Icon123',
|
||||
isVisible: true,
|
||||
basePathToShowPage: `/object/${objectMetadataItem.nameSingular}/`,
|
||||
relationType: parseFieldRelationType(field),
|
||||
});
|
||||
}): ColumnDefinition<FieldMetadata> => {
|
||||
const relationObjectMetadataItem =
|
||||
field.toRelationMetadata?.fromObjectMetadata;
|
||||
|
||||
const mainIdentifierMapper: MainIdentifierMapper = (record: any) => {
|
||||
if (relationObjectMetadataItem?.nameSingular === 'company') {
|
||||
return {
|
||||
id: record.id,
|
||||
name: record.name,
|
||||
avatarUrl: getLogoUrlFromDomainName(record.domainName),
|
||||
avatarType: 'squared',
|
||||
record: record,
|
||||
};
|
||||
}
|
||||
if (relationObjectMetadataItem?.nameSingular === 'workspaceMember') {
|
||||
return {
|
||||
id: record.id,
|
||||
name: record.name.firstName + ' ' + record.name.lastName,
|
||||
avatarUrl: record.avatarUrl,
|
||||
avatarType: 'rounded',
|
||||
record: record,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
id: record.id,
|
||||
name: record.name,
|
||||
avatarUrl: record.avatarUrl,
|
||||
avatarType: 'rounded',
|
||||
record: record,
|
||||
};
|
||||
};
|
||||
|
||||
return {
|
||||
position,
|
||||
fieldMetadataId: field.id,
|
||||
label: field.label,
|
||||
size: 100,
|
||||
type: parseFieldType(field.type),
|
||||
metadata: {
|
||||
fieldName: field.name,
|
||||
placeHolder: field.label,
|
||||
mainIdentifierMapper: mainIdentifierMapper,
|
||||
relationType: parseFieldRelationType(field),
|
||||
searchFields: ['name'],
|
||||
objectMetadataNamePlural: relationObjectMetadataItem?.namePlural,
|
||||
objectMetadataNameSingular: relationObjectMetadataItem?.nameSingular,
|
||||
},
|
||||
iconName: field.icon ?? 'Icon123',
|
||||
isVisible: true,
|
||||
};
|
||||
};
|
||||
|
||||
@ -165,11 +165,11 @@ export const RecordShowPage = () => {
|
||||
value={{
|
||||
entityId: object.id,
|
||||
recoilScopeId: object.id + metadataField.id,
|
||||
isMainIdentifier: false,
|
||||
fieldDefinition:
|
||||
formatFieldMetadataItemAsColumnDefinition({
|
||||
field: metadataField,
|
||||
position: index,
|
||||
objectMetadataItem: foundObjectMetadataItem,
|
||||
}),
|
||||
useUpdateEntityMutation: useUpdateOneObjectMutation,
|
||||
hotkeyScope: InlineCellHotkeyScope.InlineCell,
|
||||
|
||||
@ -2,6 +2,7 @@ import { useEffect } from 'react';
|
||||
|
||||
import { useComputeDefinitionsFromFieldMetadata } from '@/object-metadata/hooks/useComputeDefinitionsFromFieldMetadata';
|
||||
import { useFindOneObjectMetadataItem } from '@/object-metadata/hooks/useFindOneObjectMetadataItem';
|
||||
import { useObjectMainIdentifier } from '@/object-metadata/hooks/useObjectMainIdentifier';
|
||||
import { useRecordTableContextMenuEntries } from '@/object-record/hooks/useRecordTableContextMenuEntries';
|
||||
import { filterAvailableTableColumns } from '@/object-record/utils/filterAvailableTableColumns';
|
||||
import { useRecordTable } from '@/ui/object/record-table/hooks/useRecordTable';
|
||||
@ -13,12 +14,19 @@ export const RecordTableEffect = () => {
|
||||
scopeId: objectNamePlural,
|
||||
setAvailableTableColumns,
|
||||
setOnEntityCountChange,
|
||||
setObjectMetadataConfig,
|
||||
} = useRecordTable();
|
||||
|
||||
const { foundObjectMetadataItem } = useFindOneObjectMetadataItem({
|
||||
objectNamePlural,
|
||||
});
|
||||
|
||||
const {
|
||||
mainIdentifierMapper,
|
||||
basePathToShowPage,
|
||||
mainIdentifierFieldMetadataId,
|
||||
} = useObjectMainIdentifier(foundObjectMetadataItem);
|
||||
|
||||
const { columnDefinitions, filterDefinitions, sortDefinitions } =
|
||||
useComputeDefinitionsFromFieldMetadata(foundObjectMetadataItem);
|
||||
|
||||
@ -31,6 +39,26 @@ export const RecordTableEffect = () => {
|
||||
setEntityCountInCurrentView,
|
||||
} = useView();
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
mainIdentifierMapper &&
|
||||
basePathToShowPage &&
|
||||
mainIdentifierFieldMetadataId
|
||||
) {
|
||||
setObjectMetadataConfig?.({
|
||||
mainIdentifierMapper,
|
||||
basePathToShowPage,
|
||||
mainIdentifierFieldMetadataId,
|
||||
});
|
||||
}
|
||||
}, [
|
||||
basePathToShowPage,
|
||||
foundObjectMetadataItem,
|
||||
mainIdentifierFieldMetadataId,
|
||||
mainIdentifierMapper,
|
||||
setObjectMetadataConfig,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!foundObjectMetadataItem) {
|
||||
return;
|
||||
|
||||
@ -1,12 +1,13 @@
|
||||
import { FieldMetadata } from '@/ui/object/field/types/FieldMetadata';
|
||||
import { isFieldRelation } from '@/ui/object/field/types/guards/isFieldRelation';
|
||||
import { ColumnDefinition } from '@/ui/object/record-table/types/ColumnDefinition';
|
||||
|
||||
export const filterAvailableTableColumns = (
|
||||
columnDefinition: ColumnDefinition<FieldMetadata>,
|
||||
): boolean => {
|
||||
if (
|
||||
columnDefinition.type === 'RELATION' &&
|
||||
columnDefinition.relationType !== 'TO_ONE_OBJECT'
|
||||
isFieldRelation(columnDefinition) &&
|
||||
columnDefinition.metadata?.relationType !== 'TO_ONE_OBJECT'
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -15,6 +15,7 @@ export const mapPaginatedObjectsToObjects = <
|
||||
pagedObjects: ObjectTypeQuery | undefined;
|
||||
objectNamePlural: string;
|
||||
}) => {
|
||||
console.log(objectNamePlural);
|
||||
const formattedObjects: ObjectType[] =
|
||||
pagedObjects?.[objectNamePlural].edges.map((objectEdge: ObjectEdge) => ({
|
||||
...objectEdge.node,
|
||||
|
||||
@ -1,23 +0,0 @@
|
||||
import { useFilteredSearchPeopleQuery } from '@/people/hooks/useFilteredSearchPeopleQuery';
|
||||
import { ObjectFilterDropdownEntitySearchSelect } from '@/ui/object/object-filter-dropdown/components/ObjectFilterDropdownEntitySearchSelect';
|
||||
import { useFilter } from '@/ui/object/object-filter-dropdown/hooks/useFilter';
|
||||
|
||||
export const FilterDropdownPeopleSearchSelect = () => {
|
||||
const {
|
||||
objectFilterDropdownSearchInput,
|
||||
objectFilterDropdownSelectedEntityId,
|
||||
} = useFilter();
|
||||
|
||||
const peopleForSelect = useFilteredSearchPeopleQuery({
|
||||
searchFilter: objectFilterDropdownSearchInput,
|
||||
selectedIds: objectFilterDropdownSelectedEntityId
|
||||
? [objectFilterDropdownSelectedEntityId]
|
||||
: [],
|
||||
});
|
||||
|
||||
return (
|
||||
<ObjectFilterDropdownEntitySearchSelect
|
||||
entitiesForSelect={peopleForSelect}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@ -1,96 +0,0 @@
|
||||
import { useEffect } from 'react';
|
||||
import { useQuery } from '@apollo/client';
|
||||
|
||||
import { useFindOneObjectMetadataItem } from '@/object-metadata/hooks/useFindOneObjectMetadataItem';
|
||||
import { useFilteredSearchEntityQueryV2 } from '@/search/hooks/useFilteredSearchEntityQueryV2';
|
||||
import { SingleEntitySelect } from '@/ui/input/relation-picker/components/SingleEntitySelect';
|
||||
import { relationPickerSearchFilterScopedState } from '@/ui/input/relation-picker/states/relationPickerSearchFilterScopedState';
|
||||
import { EntityForSelect } from '@/ui/input/relation-picker/types/EntityForSelect';
|
||||
import { Entity } from '@/ui/input/relation-picker/types/EntityTypeForSelect';
|
||||
import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
|
||||
|
||||
export type PeoplePickerProps = {
|
||||
personId: string | null;
|
||||
companyId?: string;
|
||||
onSubmit: (newPersonId: PersonForSelect | null) => void;
|
||||
onCancel?: () => void;
|
||||
onCreate?: () => void;
|
||||
excludePersonIds?: string[];
|
||||
initialSearchFilter?: string | null;
|
||||
};
|
||||
|
||||
export type PersonForSelect = EntityForSelect & {
|
||||
entityType: Entity.Person;
|
||||
};
|
||||
|
||||
export const PeoplePicker = ({
|
||||
personId,
|
||||
companyId,
|
||||
onSubmit,
|
||||
onCancel,
|
||||
onCreate,
|
||||
excludePersonIds,
|
||||
initialSearchFilter,
|
||||
}: PeoplePickerProps) => {
|
||||
const [relationPickerSearchFilter, setRelationPickerSearchFilter] =
|
||||
useRecoilScopedState(relationPickerSearchFilterScopedState);
|
||||
|
||||
useEffect(() => {
|
||||
setRelationPickerSearchFilter(initialSearchFilter ?? '');
|
||||
}, [initialSearchFilter, setRelationPickerSearchFilter]);
|
||||
|
||||
const queryFilters = [
|
||||
{
|
||||
fieldNames: ['name.firstName', 'name.lastName'],
|
||||
filter: relationPickerSearchFilter,
|
||||
},
|
||||
];
|
||||
|
||||
if (companyId) {
|
||||
queryFilters.push({
|
||||
fieldNames: ['companyId'],
|
||||
filter: companyId,
|
||||
});
|
||||
}
|
||||
|
||||
const { findManyQuery } = useFindOneObjectMetadataItem({
|
||||
objectNameSingular: 'person',
|
||||
});
|
||||
|
||||
const useFindManyPeople = (options: any) => useQuery(findManyQuery, options);
|
||||
|
||||
const people = useFilteredSearchEntityQueryV2({
|
||||
queryHook: useFindManyPeople,
|
||||
filters: queryFilters,
|
||||
orderByField: 'createdAt',
|
||||
mappingFunction: (workspaceMember) => ({
|
||||
entityType: Entity.WorkspaceMember,
|
||||
id: workspaceMember.id,
|
||||
name:
|
||||
workspaceMember.name.firstName + ' ' + workspaceMember.name.lastName,
|
||||
avatarType: 'rounded',
|
||||
avatarUrl: '',
|
||||
originalEntity: workspaceMember,
|
||||
}),
|
||||
selectedIds: [personId ?? ''],
|
||||
excludeEntityIds: excludePersonIds,
|
||||
objectNamePlural: 'people',
|
||||
});
|
||||
|
||||
const handleEntitySelected = async (
|
||||
selectedPerson: any | null | undefined,
|
||||
) => {
|
||||
onSubmit(selectedPerson ?? null);
|
||||
};
|
||||
|
||||
return (
|
||||
<SingleEntitySelect
|
||||
entitiesToSelect={people.entitiesToSelect}
|
||||
loading={people.loading}
|
||||
onCancel={onCancel}
|
||||
onCreate={onCreate}
|
||||
onEntitySelected={handleEntitySelected}
|
||||
selectedEntity={people.selectedEntities[0]}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@ -6,14 +6,14 @@ import {
|
||||
export type PersonChipProps = {
|
||||
id: string;
|
||||
name: string;
|
||||
pictureUrl?: string;
|
||||
avatarUrl?: string;
|
||||
variant?: EntityChipVariant;
|
||||
};
|
||||
|
||||
export const PersonChip = ({
|
||||
id,
|
||||
name,
|
||||
pictureUrl,
|
||||
avatarUrl,
|
||||
variant,
|
||||
}: PersonChipProps) => (
|
||||
<EntityChip
|
||||
@ -21,7 +21,7 @@ export const PersonChip = ({
|
||||
linkToEntity={`/person/${id}`}
|
||||
name={name}
|
||||
avatarType="rounded"
|
||||
pictureUrl={pictureUrl}
|
||||
avatarUrl={avatarUrl}
|
||||
variant={variant}
|
||||
/>
|
||||
);
|
||||
|
||||
@ -1,43 +0,0 @@
|
||||
import { useQuery } from '@apollo/client';
|
||||
|
||||
import { useFindOneObjectMetadataItem } from '@/object-metadata/hooks/useFindOneObjectMetadataItem';
|
||||
import { useFilteredSearchEntityQueryV2 } from '@/search/hooks/useFilteredSearchEntityQueryV2';
|
||||
import { Entity } from '@/ui/input/relation-picker/types/EntityTypeForSelect';
|
||||
|
||||
export const useFilteredSearchPeopleQuery = ({
|
||||
searchFilter,
|
||||
selectedIds = [],
|
||||
limit,
|
||||
}: {
|
||||
searchFilter: string;
|
||||
selectedIds?: string[];
|
||||
limit?: number;
|
||||
}) => {
|
||||
const { findManyQuery } = useFindOneObjectMetadataItem({
|
||||
objectNameSingular: 'person',
|
||||
});
|
||||
|
||||
const useFindManyPeople = (options: any) => useQuery(findManyQuery, options);
|
||||
|
||||
return useFilteredSearchEntityQueryV2({
|
||||
queryHook: useFindManyPeople,
|
||||
filters: [
|
||||
{
|
||||
fieldNames: ['name.firstName', 'name.lastName'],
|
||||
filter: searchFilter,
|
||||
},
|
||||
],
|
||||
orderByField: 'createdAt',
|
||||
mappingFunction: (person) => ({
|
||||
entityType: Entity.Person,
|
||||
id: person.id,
|
||||
name: person.name.firstName + ' ' + person.name.lastName,
|
||||
avatarType: 'rounded',
|
||||
avatarUrl: '',
|
||||
originalEntity: person,
|
||||
}),
|
||||
selectedIds: selectedIds,
|
||||
objectNamePlural: 'workspaceMembers',
|
||||
limit,
|
||||
});
|
||||
};
|
||||
@ -1,78 +0,0 @@
|
||||
import { Person } from '@/people/types/Person';
|
||||
import { Entity } from '@/ui/input/relation-picker/types/EntityTypeForSelect';
|
||||
import {
|
||||
FieldDateMetadata,
|
||||
FieldMetadata,
|
||||
FieldNumberMetadata,
|
||||
FieldProbabilityMetadata,
|
||||
FieldRelationMetadata,
|
||||
} from '@/ui/object/field/types/FieldMetadata';
|
||||
import { ColumnDefinition } from '@/ui/object/record-table/types/ColumnDefinition';
|
||||
|
||||
export const pipelineAvailableFieldDefinitions: ColumnDefinition<FieldMetadata>[] =
|
||||
[
|
||||
{
|
||||
fieldMetadataId: 'closeDate',
|
||||
label: 'Close Date',
|
||||
iconName: 'IconCalendarEvent',
|
||||
position: 0,
|
||||
type: 'DATE',
|
||||
metadata: {
|
||||
fieldName: 'closeDate',
|
||||
},
|
||||
size: 0,
|
||||
isVisible: true,
|
||||
infoTooltipContent:
|
||||
'Specified date by which an opportunity must be completed.',
|
||||
} satisfies ColumnDefinition<FieldDateMetadata>,
|
||||
{
|
||||
fieldMetadataId: 'amount',
|
||||
label: 'Amount',
|
||||
iconName: 'IconCurrencyDollar',
|
||||
position: 1,
|
||||
type: 'NUMBER',
|
||||
metadata: {
|
||||
fieldName: 'amount',
|
||||
placeHolder: '0',
|
||||
},
|
||||
size: 0,
|
||||
isVisible: true,
|
||||
infoTooltipContent: 'Potential monetary value of a business opportunity.',
|
||||
} satisfies ColumnDefinition<FieldNumberMetadata>,
|
||||
{
|
||||
fieldMetadataId: 'probability',
|
||||
label: 'Probability',
|
||||
iconName: 'IconProgressCheck',
|
||||
position: 2,
|
||||
type: 'PROBABILITY',
|
||||
metadata: {
|
||||
fieldName: 'probability',
|
||||
},
|
||||
size: 0,
|
||||
isVisible: true,
|
||||
infoTooltipContent:
|
||||
"Level of certainty in the lead's potential to convert into a success.",
|
||||
} satisfies ColumnDefinition<FieldProbabilityMetadata>,
|
||||
{
|
||||
fieldMetadataId: 'pointOfContact',
|
||||
label: 'Point of Contact',
|
||||
iconName: 'IconUser',
|
||||
position: 3,
|
||||
type: 'RELATION',
|
||||
metadata: {
|
||||
fieldName: 'pointOfContact',
|
||||
relationType: Entity.Person,
|
||||
useEditButton: true,
|
||||
},
|
||||
size: 0,
|
||||
isVisible: true,
|
||||
infoTooltipContent: 'Primary contact within the company.',
|
||||
entityChipDisplayMapper: (dataObject: Person) => {
|
||||
return {
|
||||
name: dataObject?.name.firstName + ' ' + dataObject?.name.lastName,
|
||||
pictureUrl: dataObject?.avatarUrl ?? undefined,
|
||||
avatarType: 'rounded',
|
||||
};
|
||||
},
|
||||
} satisfies ColumnDefinition<FieldRelationMetadata>,
|
||||
];
|
||||
@ -18,7 +18,7 @@ const DEFAULT_SEARCH_REQUEST_LIMIT = 30;
|
||||
|
||||
// TODO: use this for all search queries, because we need selectedEntities and entitiesToSelect each time we want to search
|
||||
// Filtered entities to select are
|
||||
export const useFilteredSearchEntityQueryV2 = ({
|
||||
export const useFilteredSearchEntityQuery = ({
|
||||
queryHook,
|
||||
orderByField,
|
||||
filters,
|
||||
@ -90,12 +90,11 @@ export const SettingsObjectFieldPreview = ({
|
||||
objectMetadataId,
|
||||
});
|
||||
|
||||
const { defaultValue: relationDefaultValue, entityChipDisplayMapper } =
|
||||
useRelationFieldPreview({
|
||||
relationObjectMetadataId,
|
||||
skipDefaultValue:
|
||||
fieldMetadata.type !== FieldMetadataType.Relation || hasValue,
|
||||
});
|
||||
const { defaultValue: relationDefaultValue } = useRelationFieldPreview({
|
||||
relationObjectMetadataId,
|
||||
skipDefaultValue:
|
||||
fieldMetadata.type !== FieldMetadataType.Relation || hasValue,
|
||||
});
|
||||
|
||||
const defaultValue =
|
||||
fieldMetadata.type === FieldMetadataType.Relation
|
||||
@ -138,16 +137,13 @@ export const SettingsObjectFieldPreview = ({
|
||||
<FieldContext.Provider
|
||||
value={{
|
||||
entityId,
|
||||
isMainIdentifier: false,
|
||||
fieldDefinition: {
|
||||
type: parseFieldType(fieldMetadata.type),
|
||||
iconName: 'FieldIcon',
|
||||
fieldMetadataId: fieldMetadata.id || '',
|
||||
label: fieldMetadata.label,
|
||||
metadata: { fieldName },
|
||||
entityChipDisplayMapper:
|
||||
fieldMetadata.type === FieldMetadataType.Relation
|
||||
? entityChipDisplayMapper
|
||||
: undefined,
|
||||
},
|
||||
hotkeyScope: 'field-preview',
|
||||
}}
|
||||
|
||||
@ -12,7 +12,7 @@ export type EntityChipProps = {
|
||||
linkToEntity?: string;
|
||||
entityId: string;
|
||||
name: string;
|
||||
pictureUrl?: string;
|
||||
avatarUrl?: string;
|
||||
avatarType?: AvatarType;
|
||||
variant?: EntityChipVariant;
|
||||
LeftIcon?: IconComponent;
|
||||
@ -27,7 +27,7 @@ export const EntityChip = ({
|
||||
linkToEntity,
|
||||
entityId,
|
||||
name,
|
||||
pictureUrl,
|
||||
avatarUrl,
|
||||
avatarType = 'rounded',
|
||||
variant = EntityChipVariant.Regular,
|
||||
LeftIcon,
|
||||
@ -59,7 +59,7 @@ export const EntityChip = ({
|
||||
<LeftIcon size={theme.icon.size.md} stroke={theme.icon.stroke.sm} />
|
||||
) : (
|
||||
<Avatar
|
||||
avatarUrl={pictureUrl}
|
||||
avatarUrl={avatarUrl}
|
||||
colorId={entityId}
|
||||
placeholder={name}
|
||||
size="sm"
|
||||
|
||||
@ -11,14 +11,12 @@ import { sleep } from '~/testing/sleep';
|
||||
|
||||
import { relationPickerSearchFilterScopedState } from '../../states/relationPickerSearchFilterScopedState';
|
||||
import { EntityForSelect } from '../../types/EntityForSelect';
|
||||
import { Entity } from '../../types/EntityTypeForSelect';
|
||||
import { SingleEntitySelect } from '../SingleEntitySelect';
|
||||
|
||||
const entities = mockedPeopleData.map<EntityForSelect>((person) => ({
|
||||
id: person.id,
|
||||
entityType: Entity.Person,
|
||||
name: person.name.firstName + ' ' + person.name.lastName,
|
||||
originalEntity: person,
|
||||
record: person,
|
||||
}));
|
||||
|
||||
const meta: Meta<typeof SingleEntitySelect> = {
|
||||
|
||||
@ -1,12 +1,9 @@
|
||||
import { AvatarType } from '@/users/components/Avatar';
|
||||
|
||||
import { EntityTypeForSelect } from './EntityTypeForSelect';
|
||||
|
||||
export type EntityForSelect = {
|
||||
id: string;
|
||||
entityType: EntityTypeForSelect;
|
||||
name: string;
|
||||
avatarUrl?: string;
|
||||
avatarType?: AvatarType;
|
||||
originalEntity: any;
|
||||
record: any;
|
||||
};
|
||||
|
||||
@ -1,10 +0,0 @@
|
||||
import { ActivityTargetableEntityType } from '@/activities/types/ActivityTargetableEntity';
|
||||
|
||||
export enum Entity {
|
||||
Company = 'Company',
|
||||
Person = 'Person',
|
||||
User = 'User',
|
||||
WorkspaceMember = 'WorkspaceMember',
|
||||
}
|
||||
|
||||
export type EntityTypeForSelect = ActivityTargetableEntityType | Entity;
|
||||
@ -2,7 +2,6 @@ import { useCallback, useContext, useRef, useState } from 'react';
|
||||
import styled from '@emotion/styled';
|
||||
import { Key } from 'ts-key-enum';
|
||||
|
||||
import { useFilteredSearchCompanyQuery } from '@/companies/hooks/useFilteredSearchCompanyQuery';
|
||||
import {
|
||||
IconArrowLeft,
|
||||
IconArrowRight,
|
||||
@ -10,7 +9,6 @@ import {
|
||||
IconPlus,
|
||||
} from '@/ui/display/icon';
|
||||
import { useSnackBar } from '@/ui/feedback/snack-bar/hooks/useSnackBar';
|
||||
import { SingleEntitySelect } from '@/ui/input/relation-picker/components/SingleEntitySelect';
|
||||
import { relationPickerSearchFilterScopedState } from '@/ui/input/relation-picker/states/relationPickerSearchFilterScopedState';
|
||||
import { EntityForSelect } from '@/ui/input/relation-picker/types/EntityForSelect';
|
||||
import { RelationPickerHotkeyScope } from '@/ui/input/relation-picker/types/RelationPickerHotkeyScope';
|
||||
@ -98,9 +96,9 @@ export const BoardColumnMenu = ({
|
||||
const [relationPickerSearchFilter] = useRecoilScopedState(
|
||||
relationPickerSearchFilterScopedState,
|
||||
);
|
||||
const companies = useFilteredSearchCompanyQuery({
|
||||
searchFilter: relationPickerSearchFilter,
|
||||
});
|
||||
// const companies = useFilteredSearchCompanyQuery({
|
||||
// searchFilter: relationPickerSearchFilter,
|
||||
// });
|
||||
|
||||
useListenClickOutside({
|
||||
refs: [boardColumnMenuRef],
|
||||
@ -172,14 +170,15 @@ export const BoardColumnMenu = ({
|
||||
/>
|
||||
)}
|
||||
{currentMenu === 'add' && (
|
||||
<SingleEntitySelect
|
||||
disableBackgroundBlur
|
||||
entitiesToSelect={companies.entitiesToSelect}
|
||||
loading={companies.loading}
|
||||
onCancel={closeMenu}
|
||||
onEntitySelected={handleCompanySelected}
|
||||
selectedEntity={companies.selectedEntities[0]}
|
||||
/>
|
||||
<div>add</div>
|
||||
// <SingleEntitySelect
|
||||
// disableBackgroundBlur
|
||||
// entitiesToSelect={companies.entitiesToSelect}
|
||||
// loading={companies.loading}
|
||||
// onCancel={closeMenu}
|
||||
// onEntitySelected={handleCompanySelected}
|
||||
// selectedEntity={companies.selectedEntities[0]}
|
||||
// />
|
||||
)}
|
||||
</DropdownMenu>
|
||||
</StyledMenuContainer>
|
||||
|
||||
@ -10,6 +10,13 @@ export type GenericFieldContextType = {
|
||||
entityId: string;
|
||||
recoilScopeId?: string;
|
||||
hotkeyScope: string;
|
||||
isMainIdentifier: boolean;
|
||||
mainIdentifierMapper?: (record: any) => {
|
||||
name: string;
|
||||
avatarUrl?: string;
|
||||
avatarType: string;
|
||||
};
|
||||
basePathToShowPage?: string;
|
||||
};
|
||||
|
||||
export const FieldContext = createContext<GenericFieldContextType>(
|
||||
|
||||
@ -10,10 +10,8 @@ export const useIsFieldEmpty = () => {
|
||||
const isFieldEmpty = useRecoilValue(
|
||||
isEntityFieldEmptyFamilySelector({
|
||||
fieldDefinition: {
|
||||
fieldMetadataId: fieldDefinition.fieldMetadataId,
|
||||
label: fieldDefinition.label,
|
||||
type: fieldDefinition.type,
|
||||
metadata: fieldDefinition.metadata,
|
||||
metadata: { ...fieldDefinition.metadata, mainIdentifierMapper: null },
|
||||
},
|
||||
entityId,
|
||||
}),
|
||||
|
||||
@ -18,6 +18,7 @@ export const FieldContextProvider = ({
|
||||
<FieldContext.Provider
|
||||
value={{
|
||||
entityId: entityId ?? '1',
|
||||
isMainIdentifier: false,
|
||||
recoilScopeId: '1',
|
||||
hotkeyScope: 'hotkey-scope',
|
||||
fieldDefinition,
|
||||
|
||||
@ -2,14 +2,12 @@ import { useChipField } from '../../hooks/useChipField';
|
||||
import { ChipDisplay } from '../content-display/components/ChipDisplay';
|
||||
|
||||
export const ChipFieldDisplay = () => {
|
||||
const { avatarFieldValue, contentFieldValue, entityType, entityId } =
|
||||
useChipField();
|
||||
const { avatarFieldValue, contentFieldValue, entityId } = useChipField();
|
||||
|
||||
return (
|
||||
<ChipDisplay
|
||||
displayName={contentFieldValue}
|
||||
avatarUrlValue={avatarFieldValue}
|
||||
entityType={entityType}
|
||||
entityId={entityId}
|
||||
/>
|
||||
);
|
||||
|
||||
@ -2,7 +2,7 @@ import { useDoubleTextChipField } from '../../hooks/useDoubleTextChipField';
|
||||
import { ChipDisplay } from '../content-display/components/ChipDisplay';
|
||||
|
||||
export const DoubleTextChipFieldDisplay = () => {
|
||||
const { avatarUrl, firstValue, secondValue, entityType, entityId } =
|
||||
const { avatarUrl, firstValue, secondValue, entityId } =
|
||||
useDoubleTextChipField();
|
||||
|
||||
const content = [firstValue, secondValue].filter(Boolean).join(' ');
|
||||
@ -11,7 +11,6 @@ export const DoubleTextChipFieldDisplay = () => {
|
||||
<ChipDisplay
|
||||
displayName={content}
|
||||
avatarUrlValue={avatarUrl}
|
||||
entityType={entityType}
|
||||
entityId={entityId}
|
||||
/>
|
||||
);
|
||||
|
||||
@ -1,22 +1,23 @@
|
||||
import { EntityChip } from '@/ui/display/chip/components/EntityChip';
|
||||
import { getEntityChipFromFieldMetadata } from '@/ui/object/field/meta-types/display/utils/getEntityChipFromFieldMetadata';
|
||||
|
||||
import { useRelationField } from '../../hooks/useRelationField';
|
||||
|
||||
export const RelationFieldDisplay = () => {
|
||||
const { fieldValue, fieldDefinition } = useRelationField();
|
||||
|
||||
const entityChipProps = getEntityChipFromFieldMetadata(
|
||||
fieldDefinition,
|
||||
fieldValue,
|
||||
);
|
||||
if (!fieldValue || !fieldDefinition) {
|
||||
return <></>;
|
||||
}
|
||||
|
||||
const mainIdentifierMapped =
|
||||
fieldDefinition.metadata.mainIdentifierMapper(fieldValue);
|
||||
|
||||
return (
|
||||
<EntityChip
|
||||
entityId={entityChipProps.entityId}
|
||||
name={entityChipProps.name}
|
||||
pictureUrl={entityChipProps.pictureUrl}
|
||||
avatarType={entityChipProps.avatarType}
|
||||
entityId={fieldValue.id}
|
||||
name={mainIdentifierMapped.name}
|
||||
avatarUrl={mainIdentifierMapped.avatarUrl}
|
||||
avatarType={mainIdentifierMapped.avatarType}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@ -26,6 +26,7 @@ const meta: Meta = {
|
||||
<FieldContext.Provider
|
||||
value={{
|
||||
entityId: '',
|
||||
isMainIdentifier: false,
|
||||
fieldDefinition: {
|
||||
fieldMetadataId: 'date',
|
||||
label: 'Date',
|
||||
|
||||
@ -25,6 +25,7 @@ const meta: Meta = {
|
||||
<FieldContext.Provider
|
||||
value={{
|
||||
entityId: '',
|
||||
isMainIdentifier: false,
|
||||
fieldDefinition: {
|
||||
fieldMetadataId: 'email',
|
||||
label: 'Email',
|
||||
|
||||
@ -25,6 +25,7 @@ const meta: Meta = {
|
||||
<FieldContext.Provider
|
||||
value={{
|
||||
entityId: '',
|
||||
isMainIdentifier: false,
|
||||
fieldDefinition: {
|
||||
fieldMetadataId: 'enum',
|
||||
label: 'Enum',
|
||||
|
||||
@ -24,6 +24,7 @@ const meta: Meta = {
|
||||
<FieldContext.Provider
|
||||
value={{
|
||||
entityId: '',
|
||||
isMainIdentifier: false,
|
||||
fieldDefinition: {
|
||||
fieldMetadataId: 'money',
|
||||
label: 'Money',
|
||||
|
||||
@ -24,6 +24,7 @@ const meta: Meta = {
|
||||
<FieldContext.Provider
|
||||
value={{
|
||||
entityId: '',
|
||||
isMainIdentifier: false,
|
||||
fieldDefinition: {
|
||||
fieldMetadataId: 'number',
|
||||
label: 'Number',
|
||||
|
||||
@ -25,6 +25,7 @@ const meta: Meta = {
|
||||
<FieldContext.Provider
|
||||
value={{
|
||||
entityId: '',
|
||||
isMainIdentifier: false,
|
||||
fieldDefinition: {
|
||||
fieldMetadataId: 'phone',
|
||||
label: 'Phone',
|
||||
|
||||
@ -24,6 +24,7 @@ const meta: Meta = {
|
||||
<FieldContext.Provider
|
||||
value={{
|
||||
entityId: '',
|
||||
isMainIdentifier: false,
|
||||
fieldDefinition: {
|
||||
fieldMetadataId: 'text',
|
||||
label: 'Text',
|
||||
|
||||
@ -25,6 +25,7 @@ const meta: Meta = {
|
||||
<FieldContext.Provider
|
||||
value={{
|
||||
entityId: '',
|
||||
isMainIdentifier: false,
|
||||
fieldDefinition: {
|
||||
fieldMetadataId: 'URL',
|
||||
label: 'URL',
|
||||
|
||||
@ -1,45 +1,34 @@
|
||||
import { CompanyChip } from '@/companies/components/CompanyChip';
|
||||
import { PersonChip } from '@/people/components/PersonChip';
|
||||
import { Entity } from '@/ui/input/relation-picker/types/EntityTypeForSelect';
|
||||
import { getLogoUrlFromDomainName } from '~/utils';
|
||||
import { logError } from '~/utils/logError';
|
||||
|
||||
type ChipDisplayProps = {
|
||||
entityType: Entity;
|
||||
displayName: string;
|
||||
entityId: string | null;
|
||||
avatarUrlValue?: string;
|
||||
};
|
||||
|
||||
export const ChipDisplay = ({
|
||||
entityType,
|
||||
displayName,
|
||||
entityId,
|
||||
avatarUrlValue,
|
||||
}: ChipDisplayProps) => {
|
||||
switch (entityType) {
|
||||
case Entity.Company: {
|
||||
return (
|
||||
<CompanyChip
|
||||
id={entityId ?? ''}
|
||||
name={displayName}
|
||||
pictureUrl={getLogoUrlFromDomainName(avatarUrlValue)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
case Entity.Person: {
|
||||
return (
|
||||
<PersonChip
|
||||
id={entityId ?? ''}
|
||||
name={displayName}
|
||||
pictureUrl={avatarUrlValue}
|
||||
/>
|
||||
);
|
||||
}
|
||||
switch (true) {
|
||||
// case Entity.Company: {
|
||||
// return (
|
||||
// <CompanyChip
|
||||
// id={entityId ?? ''}
|
||||
// name={displayName}
|
||||
// avatarUrl={getLogoUrlFromDomainName(avatarUrlValue)}
|
||||
// />
|
||||
// );
|
||||
// }
|
||||
// case Entity.Person: {
|
||||
// return (
|
||||
// <PersonChip
|
||||
// id={entityId ?? ''}
|
||||
// name={displayName}
|
||||
// avatarUrl={avatarUrlValue}
|
||||
// />
|
||||
// );
|
||||
// }
|
||||
default:
|
||||
logError(
|
||||
`Unknown relation type: "${entityType}" in DoubleTextChipDisplay`,
|
||||
);
|
||||
return <> </>;
|
||||
}
|
||||
};
|
||||
|
||||
@ -1,42 +0,0 @@
|
||||
import { EntityChipProps } from '@/ui/display/chip/components/EntityChip';
|
||||
import { FieldDefinition } from '@/ui/object/field/types/FieldDefinition';
|
||||
import { FieldRelationMetadata } from '@/ui/object/field/types/FieldMetadata';
|
||||
import { getLogoUrlFromDomainName } from '~/utils';
|
||||
|
||||
export const getEntityChipFromFieldMetadata = (
|
||||
fieldDefinition: FieldDefinition<FieldRelationMetadata>,
|
||||
fieldValue: any,
|
||||
) => {
|
||||
const { entityChipDisplayMapper } = fieldDefinition;
|
||||
const { fieldName } = fieldDefinition.metadata;
|
||||
|
||||
const defaultChipValue: Pick<
|
||||
EntityChipProps,
|
||||
'name' | 'pictureUrl' | 'avatarType' | 'entityId'
|
||||
> = {
|
||||
name: '',
|
||||
pictureUrl: '',
|
||||
avatarType: 'rounded',
|
||||
entityId: fieldValue?.id,
|
||||
};
|
||||
|
||||
if (['accountOwner', 'person'].includes(fieldName) && fieldValue) {
|
||||
return {
|
||||
...defaultChipValue,
|
||||
name: `${fieldValue.firstName} ${fieldValue.lastName}`,
|
||||
};
|
||||
}
|
||||
|
||||
if (fieldName === 'company' && fieldValue) {
|
||||
return {
|
||||
...defaultChipValue,
|
||||
name: fieldValue.name,
|
||||
pictureUrl: getLogoUrlFromDomainName(fieldValue.domainName),
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
...defaultChipValue,
|
||||
...entityChipDisplayMapper?.(fieldValue),
|
||||
};
|
||||
};
|
||||
@ -29,8 +29,6 @@ export const useChipField = () => {
|
||||
}),
|
||||
);
|
||||
|
||||
const entityType = fieldDefinition.metadata.relationType;
|
||||
|
||||
const fieldInitialValue = useFieldInitialValue();
|
||||
|
||||
const initialContentValue = fieldInitialValue?.isEmpty
|
||||
@ -51,7 +49,6 @@ export const useChipField = () => {
|
||||
avatarFieldValue,
|
||||
initialAvatarValue,
|
||||
setAvatarFieldValue,
|
||||
entityType,
|
||||
entityId,
|
||||
hotkeyScope,
|
||||
};
|
||||
|
||||
@ -39,8 +39,6 @@ export const useDoubleTextChipField = () => {
|
||||
|
||||
const fullValue = [firstValue, secondValue].filter(Boolean).join(' ');
|
||||
|
||||
const entityType = fieldDefinition.metadata.entityType;
|
||||
|
||||
const fieldInitialValue = useFieldInitialValue();
|
||||
|
||||
const initialFirstValue = fieldInitialValue?.isEmpty
|
||||
@ -68,7 +66,6 @@ export const useDoubleTextChipField = () => {
|
||||
firstValue,
|
||||
setFirstValue,
|
||||
fullValue,
|
||||
entityType,
|
||||
entityId,
|
||||
hotkeyScope,
|
||||
initialAvatarUrl,
|
||||
|
||||
@ -1,10 +1,8 @@
|
||||
import { useEffect } from 'react';
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
import { CompanyPicker } from '@/companies/components/CompanyPicker';
|
||||
import { PeoplePicker } from '@/people/components/PeoplePicker';
|
||||
import { EntityForSelect } from '@/ui/input/relation-picker/types/EntityForSelect';
|
||||
import { WorkspaceMemberPicker } from '@/workspace-member/components/WorkspaceMemberPicker';
|
||||
import { RelationPicker } from '@/ui/object/field/meta-types/input/components/internal/RelationPicker';
|
||||
|
||||
import { usePersistField } from '../../../hooks/usePersistField';
|
||||
import { useRelationField } from '../../hooks/useRelationField';
|
||||
@ -32,14 +30,21 @@ export const RelationFieldInput = ({
|
||||
const persistField = usePersistField();
|
||||
|
||||
const handleSubmit = (newEntity: EntityForSelect | null) => {
|
||||
onSubmit?.(() => persistField(newEntity?.originalEntity ?? null));
|
||||
onSubmit?.(() => persistField(newEntity?.record ?? null));
|
||||
};
|
||||
|
||||
useEffect(() => {}, [initialSearchValue]);
|
||||
|
||||
return (
|
||||
<StyledRelationPickerContainer>
|
||||
{fieldDefinition.metadata.fieldName === 'person' ? (
|
||||
<RelationPicker
|
||||
fieldDefinition={fieldDefinition}
|
||||
recordId={initialValue?.id ?? ''}
|
||||
onSubmit={handleSubmit}
|
||||
onCancel={onCancel}
|
||||
initialSearchFilter={initialSearchValue}
|
||||
/>
|
||||
{/* {fieldDefinition.metadata.fieldName === 'person' ? (
|
||||
<PeoplePicker
|
||||
personId={initialValue?.id ?? ''}
|
||||
companyId={initialValue?.companyId ?? ''}
|
||||
@ -47,13 +52,6 @@ export const RelationFieldInput = ({
|
||||
onCancel={onCancel}
|
||||
initialSearchFilter={initialSearchValue}
|
||||
/>
|
||||
) : fieldDefinition.metadata.fieldName === 'accountOwner' ? (
|
||||
<WorkspaceMemberPicker
|
||||
userId={initialValue?.id ?? ''}
|
||||
onSubmit={handleSubmit}
|
||||
onCancel={onCancel}
|
||||
initialSearchFilter={initialSearchValue}
|
||||
/>
|
||||
) : fieldDefinition.metadata.fieldName === 'company' ? (
|
||||
<CompanyPicker
|
||||
companyId={initialValue?.id ?? ''}
|
||||
@ -61,7 +59,7 @@ export const RelationFieldInput = ({
|
||||
onCancel={onCancel}
|
||||
initialSearchFilter={initialSearchValue}
|
||||
/>
|
||||
) : null}
|
||||
) : null} */}
|
||||
</StyledRelationPickerContainer>
|
||||
);
|
||||
};
|
||||
|
||||
@ -3,7 +3,6 @@ import { expect, jest } from '@storybook/jest';
|
||||
import { Decorator, Meta, StoryObj } from '@storybook/react';
|
||||
import { userEvent, waitFor, within } from '@storybook/testing-library';
|
||||
|
||||
import { Entity } from '@/ui/input/relation-picker/types/EntityTypeForSelect';
|
||||
import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope';
|
||||
|
||||
import { FieldContextProvider } from '../../../__stories__/FieldContextProvider';
|
||||
@ -52,7 +51,6 @@ const ChipFieldInputWithContext = ({
|
||||
contentFieldName: 'name',
|
||||
urlFieldName: 'xURL',
|
||||
placeHolder: 'X URL',
|
||||
relationType: Entity.Person,
|
||||
},
|
||||
}}
|
||||
entityId={entityId}
|
||||
|
||||
@ -3,7 +3,6 @@ import { expect, jest } from '@storybook/jest';
|
||||
import { Decorator, Meta, StoryObj } from '@storybook/react';
|
||||
import { userEvent, waitFor, within } from '@storybook/testing-library';
|
||||
|
||||
import { Entity } from '@/ui/input/relation-picker/types/EntityTypeForSelect';
|
||||
import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope';
|
||||
|
||||
import { FieldContextProvider } from '../../../__stories__/FieldContextProvider';
|
||||
@ -67,7 +66,7 @@ const DoubleTextChipFieldInputWithContext = ({
|
||||
secondValueFieldName: 'Second-text',
|
||||
secondValuePlaceholder: 'Second-text',
|
||||
avatarUrlFieldName: 'avatarUrl',
|
||||
entityType: Entity.Person,
|
||||
fieldName: '',
|
||||
},
|
||||
}}
|
||||
entityId={entityId}
|
||||
|
||||
@ -3,7 +3,6 @@ import { expect, jest } from '@storybook/jest';
|
||||
import { Decorator, Meta, StoryObj } from '@storybook/react';
|
||||
import { userEvent, waitFor, within } from '@storybook/testing-library';
|
||||
|
||||
import { Entity } from '@/ui/input/relation-picker/types/EntityTypeForSelect';
|
||||
import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope';
|
||||
import { ComponentWithRecoilScopeDecorator } from '~/testing/decorators/ComponentWithRecoilScopeDecorator';
|
||||
import { graphqlMocks } from '~/testing/graphqlMocks';
|
||||
@ -52,7 +51,6 @@ const RelationFieldInputWithContext = ({
|
||||
iconName: 'IconLink',
|
||||
metadata: {
|
||||
fieldName: 'Relation',
|
||||
relationType: Entity.Person,
|
||||
},
|
||||
}}
|
||||
entityId={entityId}
|
||||
|
||||
@ -2,29 +2,32 @@ import { useEffect } from 'react';
|
||||
import { useQuery } from '@apollo/client';
|
||||
|
||||
import { useFindOneObjectMetadataItem } from '@/object-metadata/hooks/useFindOneObjectMetadataItem';
|
||||
import { useFilteredSearchEntityQueryV2 } from '@/search/hooks/useFilteredSearchEntityQueryV2';
|
||||
import { useFilteredSearchEntityQuery } from '@/search/hooks/useFilteredSearchEntityQuery';
|
||||
import { IconUserCircle } from '@/ui/display/icon';
|
||||
import { SingleEntitySelect } from '@/ui/input/relation-picker/components/SingleEntitySelect';
|
||||
import { relationPickerSearchFilterScopedState } from '@/ui/input/relation-picker/states/relationPickerSearchFilterScopedState';
|
||||
import { EntityForSelect } from '@/ui/input/relation-picker/types/EntityForSelect';
|
||||
import { Entity } from '@/ui/input/relation-picker/types/EntityTypeForSelect';
|
||||
import { FieldDefinition } from '@/ui/object/field/types/FieldDefinition';
|
||||
import { FieldRelationMetadata } from '@/ui/object/field/types/FieldMetadata';
|
||||
import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
|
||||
|
||||
export type WorkspaceMemberPickerProps = {
|
||||
userId: string;
|
||||
export type RelationPickerProps = {
|
||||
recordId: string;
|
||||
onSubmit: (newUser: EntityForSelect | null) => void;
|
||||
onCancel?: () => void;
|
||||
width?: number;
|
||||
initialSearchFilter?: string | null;
|
||||
fieldDefinition: FieldDefinition<FieldRelationMetadata>;
|
||||
};
|
||||
|
||||
export const WorkspaceMemberPicker = ({
|
||||
userId,
|
||||
export const RelationPicker = ({
|
||||
recordId,
|
||||
onSubmit,
|
||||
onCancel,
|
||||
width,
|
||||
initialSearchFilter,
|
||||
}: WorkspaceMemberPickerProps) => {
|
||||
fieldDefinition,
|
||||
}: RelationPickerProps) => {
|
||||
const [relationPickerSearchFilter, setRelationPickerSearchFilter] =
|
||||
useRecoilScopedState(relationPickerSearchFilterScopedState);
|
||||
|
||||
@ -33,32 +36,23 @@ export const WorkspaceMemberPicker = ({
|
||||
}, [initialSearchFilter, setRelationPickerSearchFilter]);
|
||||
|
||||
const { findManyQuery } = useFindOneObjectMetadataItem({
|
||||
objectNameSingular: 'workspaceMember',
|
||||
objectNameSingular: fieldDefinition.metadata.objectMetadataNameSingular,
|
||||
});
|
||||
|
||||
const useFindManyWorkspaceMembers = (options: any) =>
|
||||
useQuery(findManyQuery, options);
|
||||
const useFindManyQuery = (options: any) => useQuery(findManyQuery, options);
|
||||
|
||||
const workspaceMembers = useFilteredSearchEntityQueryV2({
|
||||
queryHook: useFindManyWorkspaceMembers,
|
||||
const workspaceMembers = useFilteredSearchEntityQuery({
|
||||
queryHook: useFindManyQuery,
|
||||
filters: [
|
||||
{
|
||||
fieldNames: ['name.firstName', 'name.lastName'],
|
||||
fieldNames: fieldDefinition.metadata.searchFields,
|
||||
filter: relationPickerSearchFilter,
|
||||
},
|
||||
],
|
||||
orderByField: 'createdAt',
|
||||
mappingFunction: (workspaceMember) => ({
|
||||
entityType: Entity.WorkspaceMember,
|
||||
id: workspaceMember.id,
|
||||
name:
|
||||
workspaceMember.name.firstName + ' ' + workspaceMember.name.lastName,
|
||||
avatarType: 'rounded',
|
||||
avatarUrl: '',
|
||||
originalEntity: workspaceMember,
|
||||
}),
|
||||
selectedIds: userId ? [userId] : [],
|
||||
objectNamePlural: 'workspaceMembers',
|
||||
mappingFunction: fieldDefinition.metadata.mainIdentifierMapper,
|
||||
selectedIds: recordId ? [recordId] : [],
|
||||
objectNamePlural: fieldDefinition.metadata.objectMetadataNamePlural,
|
||||
});
|
||||
|
||||
const handleEntitySelected = async (selectedUser: any | null | undefined) => {
|
||||
@ -34,10 +34,9 @@ export const isEntityFieldEmptyFamilySelector = selectorFamily({
|
||||
fieldDefinition,
|
||||
entityId,
|
||||
}: {
|
||||
fieldDefinition: Pick<
|
||||
FieldDefinition<FieldMetadata>,
|
||||
'type' | 'metadata' | 'fieldMetadataId' | 'label'
|
||||
>;
|
||||
fieldDefinition: Pick<FieldDefinition<FieldMetadata>, 'type'> & {
|
||||
metadata: Omit<FieldMetadata, 'mainIdentifierMapper'>;
|
||||
};
|
||||
entityId: string;
|
||||
}) => {
|
||||
return ({ get }) => {
|
||||
|
||||
@ -1,5 +1,3 @@
|
||||
import { AvatarType } from '@/users/components/Avatar';
|
||||
|
||||
import { FieldMetadata } from './FieldMetadata';
|
||||
import { FieldType } from './FieldType';
|
||||
|
||||
@ -15,12 +13,5 @@ export type FieldDefinition<T extends FieldMetadata> = {
|
||||
iconName: string;
|
||||
type: FieldType;
|
||||
metadata: T;
|
||||
basePathToShowPage?: string;
|
||||
infoTooltipContent?: string;
|
||||
entityChipDisplayMapper?: (dataObject: any) => {
|
||||
name: string;
|
||||
pictureUrl?: string;
|
||||
avatarType: AvatarType;
|
||||
};
|
||||
relationType?: FieldDefinitionRelationType;
|
||||
};
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { EntityForSelect } from '@/ui/input/relation-picker/types/EntityForSelect';
|
||||
import { Entity } from '@/ui/input/relation-picker/types/EntityTypeForSelect';
|
||||
import { MainIdentifierMapper } from '@/ui/object/field/types/MainIdentifierMapper';
|
||||
import { ThemeColor } from '@/ui/theme/constants/colors';
|
||||
|
||||
export type FieldUuidMetadata = {
|
||||
@ -58,14 +58,23 @@ export type FieldEmailMetadata = {
|
||||
placeHolder: string;
|
||||
};
|
||||
|
||||
export type FieldDefinitionRelationType =
|
||||
| 'FROM_MANY_OBJECTS'
|
||||
| 'FROM_ONE_OBJECT'
|
||||
| 'TO_MANY_OBJECTS'
|
||||
| 'TO_ONE_OBJECT';
|
||||
|
||||
export type FieldRelationMetadata = {
|
||||
relationType: Entity;
|
||||
fieldName: string;
|
||||
useEditButton?: boolean;
|
||||
relationType?: FieldDefinitionRelationType;
|
||||
mainIdentifierMapper: MainIdentifierMapper;
|
||||
searchFields: string[];
|
||||
objectMetadataNameSingular: string;
|
||||
objectMetadataNamePlural: string;
|
||||
};
|
||||
|
||||
export type FieldChipMetadata = {
|
||||
relationType: Entity;
|
||||
contentFieldName: string;
|
||||
urlFieldName: string;
|
||||
placeHolder: string;
|
||||
@ -84,7 +93,6 @@ export type FieldDoubleTextChipMetadata = {
|
||||
secondValueFieldName: string;
|
||||
secondValuePlaceholder: string;
|
||||
avatarUrlFieldName: string;
|
||||
entityType: Entity;
|
||||
};
|
||||
|
||||
export type FieldProbabilityMetadata = {
|
||||
|
||||
@ -0,0 +1,9 @@
|
||||
import { AvatarType } from '@/users/components/Avatar';
|
||||
|
||||
export type MainIdentifierMapper = (record: any) => {
|
||||
id: string;
|
||||
name: string;
|
||||
avatarUrl?: string;
|
||||
avatarType: AvatarType;
|
||||
record: any;
|
||||
};
|
||||
@ -0,0 +1,14 @@
|
||||
import { MainIdentifierMapper } from '@/ui/object/field/types/MainIdentifierMapper';
|
||||
|
||||
export type FieldDefinitionRelationType =
|
||||
| 'FROM_MANY_OBJECTS'
|
||||
| 'FROM_ONE_OBJECT'
|
||||
| 'TO_MANY_OBJECTS'
|
||||
| 'TO_ONE_OBJECT';
|
||||
|
||||
export type RelationFieldConfig = {
|
||||
relationType?: FieldDefinitionRelationType;
|
||||
mainIdentifierMapper: MainIdentifierMapper;
|
||||
searchFields: string[];
|
||||
objectMetadataNameSingular: string;
|
||||
};
|
||||
@ -16,7 +16,7 @@ export const GenericEntityFilterChip = ({
|
||||
entityId={filter.value}
|
||||
name={filter.displayValue}
|
||||
avatarType="rounded"
|
||||
pictureUrl={filter.displayAvatarUrl}
|
||||
avatarUrl={filter.displayAvatarUrl}
|
||||
LeftIcon={Icon}
|
||||
/>
|
||||
);
|
||||
|
||||
@ -1,9 +1,10 @@
|
||||
import { useContext } from 'react';
|
||||
import { useSetRecoilState } from 'recoil';
|
||||
import { useRecoilValue, useSetRecoilState } from 'recoil';
|
||||
|
||||
import { RelationPickerHotkeyScope } from '@/ui/input/relation-picker/types/RelationPickerHotkeyScope';
|
||||
import { contextMenuIsOpenState } from '@/ui/navigation/context-menu/states/contextMenuIsOpenState';
|
||||
import { contextMenuPositionState } from '@/ui/navigation/context-menu/states/contextMenuPositionState';
|
||||
import { useRecordTableScopedStates } from '@/ui/object/record-table/hooks/internal/useRecordTableScopedStates';
|
||||
import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope';
|
||||
|
||||
import { FieldContext } from '../../field/contexts/FieldContext';
|
||||
@ -20,6 +21,9 @@ export const RecordTableCell = ({ cellIndex }: { cellIndex: number }) => {
|
||||
const setContextMenuPosition = useSetRecoilState(contextMenuPositionState);
|
||||
const setContextMenuOpenState = useSetRecoilState(contextMenuIsOpenState);
|
||||
const currentRowId = useContext(RowIdContext);
|
||||
const { objectMetadataConfigState } = useRecordTableScopedStates();
|
||||
|
||||
const objectMetadataConfig = useRecoilValue(objectMetadataConfigState);
|
||||
|
||||
const { setCurrentRowSelected } = useCurrentRowSelected();
|
||||
|
||||
@ -56,6 +60,10 @@ export const RecordTableCell = ({ cellIndex }: { cellIndex: number }) => {
|
||||
fieldDefinition: columnDefinition,
|
||||
useUpdateEntityMutation: () => [updateEntityMutation, {}],
|
||||
hotkeyScope: customHotkeyScope,
|
||||
isMainIdentifier:
|
||||
columnDefinition.fieldMetadataId ===
|
||||
objectMetadataConfig?.mainIdentifierFieldMetadataId,
|
||||
mainIdentifierMapper: objectMetadataConfig?.mainIdentifierMapper,
|
||||
}}
|
||||
>
|
||||
<TableCell customHotkeyScope={{ scope: customHotkeyScope }} />
|
||||
|
||||
@ -18,6 +18,7 @@ export const useRecordTableScopedStates = (args?: {
|
||||
tableFiltersState,
|
||||
tableSortsState,
|
||||
tableColumnsState,
|
||||
objectMetadataConfigState,
|
||||
tableColumnsByKeySelector,
|
||||
hiddenTableColumnsSelector,
|
||||
visibleTableColumnsSelector,
|
||||
@ -33,6 +34,7 @@ export const useRecordTableScopedStates = (args?: {
|
||||
tableFiltersState,
|
||||
tableSortsState,
|
||||
tableColumnsState,
|
||||
objectMetadataConfigState,
|
||||
tableColumnsByKeySelector,
|
||||
hiddenTableColumnsSelector,
|
||||
visibleTableColumnsSelector,
|
||||
|
||||
@ -43,6 +43,7 @@ export const useRecordTable = (props?: useRecordTableProps) => {
|
||||
tableFiltersState,
|
||||
tableSortsState,
|
||||
tableColumnsState,
|
||||
objectMetadataConfigState,
|
||||
onEntityCountChangeState,
|
||||
} = useRecordTableScopedStates({
|
||||
customRecordTableScopeId: scopeId,
|
||||
@ -54,6 +55,7 @@ export const useRecordTable = (props?: useRecordTableProps) => {
|
||||
|
||||
const setOnEntityCountChange = useSetRecoilState(onEntityCountChangeState);
|
||||
const setTableFilters = useSetRecoilState(tableFiltersState);
|
||||
const setObjectMetadataConfig = useSetRecoilState(objectMetadataConfigState);
|
||||
|
||||
const setTableSorts = useSetRecoilState(tableSortsState);
|
||||
|
||||
@ -301,6 +303,7 @@ export const useRecordTable = (props?: useRecordTableProps) => {
|
||||
setAvailableTableColumns,
|
||||
setTableFilters,
|
||||
setTableSorts,
|
||||
setObjectMetadataConfig,
|
||||
setOnEntityCountChange,
|
||||
setRecordTableData,
|
||||
setTableColumns,
|
||||
|
||||
@ -37,7 +37,8 @@ export const useTableCell = () => {
|
||||
|
||||
const isEmpty = useIsFieldEmpty();
|
||||
|
||||
const { entityId, fieldDefinition } = useContext(FieldContext);
|
||||
const { entityId, fieldDefinition, basePathToShowPage } =
|
||||
useContext(FieldContext);
|
||||
|
||||
const [, setFieldInitialValue] = useRecoilState(
|
||||
entityFieldInitialValueFamilyState({
|
||||
@ -47,8 +48,8 @@ export const useTableCell = () => {
|
||||
);
|
||||
|
||||
const openTableCell = (options?: { initialValue?: FieldInitialValue }) => {
|
||||
if (isFirstColumnCell && !isEmpty && fieldDefinition.basePathToShowPage) {
|
||||
navigate(`${fieldDefinition.basePathToShowPage}${entityId}`);
|
||||
if (isFirstColumnCell && !isEmpty && basePathToShowPage) {
|
||||
navigate(`${basePathToShowPage}${entityId}`);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@ -0,0 +1,8 @@
|
||||
import { ObjectMetadataConfig } from '@/ui/object/record-table/types/ObjectMetadataConfig';
|
||||
import { createScopedState } from '@/ui/utilities/recoil-scope/utils/createScopedState';
|
||||
|
||||
export const objectMetadataConfigScopedState =
|
||||
createScopedState<ObjectMetadataConfig | null>({
|
||||
key: 'objectMetadataConfigScopedState',
|
||||
defaultValue: null,
|
||||
});
|
||||
@ -0,0 +1,11 @@
|
||||
import { AvatarType } from '@/users/components/Avatar';
|
||||
|
||||
export type ObjectMetadataConfig = {
|
||||
mainIdentifierFieldMetadataId: string;
|
||||
mainIdentifierMapper: (record: any) => {
|
||||
name: string;
|
||||
avatarUrl?: string;
|
||||
avatarType: AvatarType;
|
||||
};
|
||||
basePathToShowPage: string;
|
||||
};
|
||||
@ -1,3 +1,4 @@
|
||||
import { objectMetadataConfigScopedState } from '@/ui/object/record-table/states/objectMetadataConfigScopedState';
|
||||
import { getScopedState } from '@/ui/utilities/recoil-scope/utils/getScopedState';
|
||||
|
||||
import { availableTableColumnsScopedState } from '../states/availableTableColumnsScopedState';
|
||||
@ -36,6 +37,11 @@ export const getRecordTableScopedStates = ({
|
||||
recordTableScopeId,
|
||||
);
|
||||
|
||||
const objectMetadataConfigState = getScopedState(
|
||||
objectMetadataConfigScopedState,
|
||||
recordTableScopeId,
|
||||
);
|
||||
|
||||
const tableColumnsByKeySelector =
|
||||
tableColumnsByKeyScopedSelector(recordTableScopeId);
|
||||
|
||||
@ -60,6 +66,7 @@ export const getRecordTableScopedStates = ({
|
||||
tableFiltersState,
|
||||
tableSortsState,
|
||||
tableColumnsState,
|
||||
objectMetadataConfigState,
|
||||
tableColumnsByKeySelector,
|
||||
hiddenTableColumnsSelector,
|
||||
visibleTableColumnsSelector,
|
||||
|
||||
@ -1,8 +1,7 @@
|
||||
import { useQuery } from '@apollo/client';
|
||||
|
||||
import { useFindOneObjectMetadataItem } from '@/object-metadata/hooks/useFindOneObjectMetadataItem';
|
||||
import { useFilteredSearchEntityQueryV2 } from '@/search/hooks/useFilteredSearchEntityQueryV2';
|
||||
import { Entity } from '@/ui/input/relation-picker/types/EntityTypeForSelect';
|
||||
import { useFilteredSearchEntityQuery } from '@/search/hooks/useFilteredSearchEntityQuery';
|
||||
import { ObjectFilterDropdownEntitySearchSelect } from '@/ui/object/object-filter-dropdown/components/ObjectFilterDropdownEntitySearchSelect';
|
||||
import { useFilter } from '@/ui/object/object-filter-dropdown/hooks/useFilter';
|
||||
|
||||
@ -19,7 +18,7 @@ export const FilterDropdownUserSearchSelect = () => {
|
||||
const useFindManyWorkspaceMembers = (options: any) =>
|
||||
useQuery(findManyQuery, options);
|
||||
|
||||
const workspaceMembers = useFilteredSearchEntityQueryV2({
|
||||
const workspaceMembers = useFilteredSearchEntityQuery({
|
||||
queryHook: useFindManyWorkspaceMembers,
|
||||
filters: [
|
||||
{
|
||||
@ -29,13 +28,13 @@ export const FilterDropdownUserSearchSelect = () => {
|
||||
],
|
||||
orderByField: 'createdAt',
|
||||
mappingFunction: (workspaceMember) => ({
|
||||
entityType: Entity.WorkspaceMember,
|
||||
entityType: 'WorkspaceMember',
|
||||
id: workspaceMember.id,
|
||||
name:
|
||||
workspaceMember.name.firstName + ' ' + workspaceMember.name.lastName,
|
||||
avatarType: 'rounded',
|
||||
avatarUrl: '',
|
||||
originalEntity: workspaceMember,
|
||||
record: workspaceMember,
|
||||
}),
|
||||
selectedIds: objectFilterDropdownSelectedEntityId
|
||||
? [objectFilterDropdownSelectedEntityId]
|
||||
|
||||
@ -3,14 +3,14 @@ import { EntityChip } from '@/ui/display/chip/components/EntityChip';
|
||||
export type UserChipProps = {
|
||||
id: string;
|
||||
name: string;
|
||||
pictureUrl?: string;
|
||||
avatarUrl?: string;
|
||||
};
|
||||
|
||||
export const UserChip = ({ id, name, pictureUrl }: UserChipProps) => (
|
||||
export const UserChip = ({ id, name, avatarUrl }: UserChipProps) => (
|
||||
<EntityChip
|
||||
entityId={id}
|
||||
name={name}
|
||||
avatarType="rounded"
|
||||
pictureUrl={pictureUrl}
|
||||
avatarUrl={avatarUrl}
|
||||
/>
|
||||
);
|
||||
|
||||
@ -19,10 +19,7 @@ export const mapViewFieldsToBoardFieldDefinitions = (
|
||||
fieldMetadataId: viewField.fieldMetadataId,
|
||||
label: correspondingFieldMetadata.label,
|
||||
metadata: correspondingFieldMetadata.metadata,
|
||||
entityChipDisplayMapper:
|
||||
correspondingFieldMetadata.entityChipDisplayMapper,
|
||||
infoTooltipContent: correspondingFieldMetadata.infoTooltipContent,
|
||||
basePathToShowPage: correspondingFieldMetadata.basePathToShowPage,
|
||||
iconName: correspondingFieldMetadata.iconName,
|
||||
type: correspondingFieldMetadata.type,
|
||||
position: viewField.position,
|
||||
|
||||
@ -19,10 +19,7 @@ export const mapViewFieldsToColumnDefinitions = (
|
||||
fieldMetadataId: viewField.fieldMetadataId,
|
||||
label: correspondingFieldMetadata.label,
|
||||
metadata: correspondingFieldMetadata.metadata,
|
||||
entityChipDisplayMapper:
|
||||
correspondingFieldMetadata.entityChipDisplayMapper,
|
||||
infoTooltipContent: correspondingFieldMetadata.infoTooltipContent,
|
||||
basePathToShowPage: correspondingFieldMetadata.basePathToShowPage,
|
||||
iconName: correspondingFieldMetadata.iconName,
|
||||
type: correspondingFieldMetadata.type,
|
||||
position: viewField.position,
|
||||
|
||||
@ -1,9 +1,6 @@
|
||||
import { FilterDropdownCompanySearchSelect } from '@/companies/components/FilterDropdownCompanySearchSelect';
|
||||
import { Opportunity } from '@/pipeline/types/Opportunity';
|
||||
import { FilterDefinitionByEntity } from '@/ui/object/object-filter-dropdown/types/FilterDefinitionByEntity';
|
||||
|
||||
import { FilterDropdownPeopleSearchSelect } from '../../../modules/people/components/FilterDropdownPeopleSearchSelect';
|
||||
|
||||
export const opportunityBoardFilterDefinitions: FilterDefinitionByEntity<Opportunity>[] =
|
||||
[
|
||||
{
|
||||
@ -23,13 +20,13 @@ export const opportunityBoardFilterDefinitions: FilterDefinitionByEntity<Opportu
|
||||
label: 'Company',
|
||||
iconName: 'IconBuildingSkyscraper',
|
||||
type: 'ENTITY',
|
||||
entitySelectComponent: <FilterDropdownCompanySearchSelect />,
|
||||
// entitySelectComponent: <FilterDropdownCompanySearchSelect />,
|
||||
},
|
||||
{
|
||||
fieldMetadataId: 'pointOfContactId',
|
||||
label: 'Point of contact',
|
||||
iconName: 'IconUser',
|
||||
type: 'ENTITY',
|
||||
entitySelectComponent: <FilterDropdownPeopleSearchSelect />,
|
||||
//entitySelectComponent: <FilterDropdownPeopleSearchSelect />,
|
||||
},
|
||||
];
|
||||
|
||||
Reference in New Issue
Block a user