Fix/company picker v2 (#2535)

Fixed company picker V2
Fixed picker search hook filters / where clause
Fixed OrderBy / SortOrder type
Fixed set relation to null
This commit is contained in:
Lucas Bordeau
2023-11-16 12:34:23 +01:00
committed by GitHub
parent e026b2b6e9
commit 4acb7f41e1
9 changed files with 98 additions and 60 deletions

View File

@ -1,11 +1,14 @@
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 { useFilteredSearchCompanyQuery } from '../hooks/useFilteredSearchCompanyQuery';
import { getLogoUrlFromDomainName } from '~/utils';
export type CompanyPickerProps = {
companyId: string | null;
@ -29,9 +32,32 @@ export const CompanyPicker = ({
}
}, [initialSearchFilter, setRelationPickerSearchFilter]);
const companies = useFilteredSearchCompanyQuery({
searchFilter: relationPickerSearchFilter,
const { findManyQuery } = useFindOneObjectMetadataItem({
objectNamePlural: 'companiesV2',
});
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: 'companiesV2',
});
const handleEntitySelected = async (

View File

@ -42,7 +42,7 @@ export const useMapFieldMetadataToGraphQLQuery = () => {
return `${field.name}
{
id
${relationMetadataItem?.fields
${(relationMetadataItem?.fields ?? [])
.filter((field) => field.type !== 'RELATION')
.map((field) => field.name)
.join('\n')}
@ -51,11 +51,21 @@ export const useMapFieldMetadataToGraphQLQuery = () => {
fieldType === 'RELATION' &&
field.fromRelationMetadata?.relationType === 'ONE_TO_MANY'
) {
const relationMetadataItem = objectMetadataItems.find(
(objectMetadataItem) =>
objectMetadataItem.id ===
(field.fromRelationMetadata as any)?.toObjectMetadata?.id,
);
return `${field.name}
{
edges {
node {
id
id
${(relationMetadataItem?.fields ?? [])
.filter((field) => field.type !== 'RELATION')
.map((field) => field.name)
.join('\n')}
}
}
}`;

View File

@ -23,10 +23,10 @@ export const useGenerateFindManyCustomObjectsQuery = ({
objectMetadataItem.nameSingular,
)}FilterInput, $orderBy: ${capitalize(
objectMetadataItem.nameSingular,
)}OrderByInput, $lastCursor: String) {
)}OrderByInput, $lastCursor: String, $limit: Float = 30) {
${
objectMetadataItem.namePlural
}(filter: $filter, orderBy: $orderBy, first: 30, after: $lastCursor){
}(filter: $filter, orderBy: $orderBy, first: $limit, after: $lastCursor){
edges {
node {
id

View File

@ -1,9 +1,10 @@
import { QueryHookOptions, QueryResult } from '@apollo/client';
import { isNonEmptyString } from '@sniptt/guards';
import { mapPaginatedObjectsToObjects } from '@/object-record/utils/mapPaginatedObjectsToObjects';
import { EntitiesForMultipleEntitySelect } from '@/ui/input/relation-picker/components/MultipleEntitySelect';
import { EntityForSelect } from '@/ui/input/relation-picker/types/EntityForSelect';
import { QueryMode, SortOrder } from '~/generated/graphql';
import { isDefined } from '~/utils/isDefined';
type SelectStringKeys<T> = NonNullable<
{
@ -23,7 +24,13 @@ type ExtractEntityTypeFromQueryResponse<T> = T extends {
type SearchFilter = { fieldNames: string[]; filter: string | number };
const DEFAULT_SEARCH_REQUEST_LIMIT = 10;
export type OrderBy =
| 'AscNullsLast'
| 'DescNullsLast'
| 'AscNullsFirst'
| 'DescNullsFirst';
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
@ -31,7 +38,7 @@ export const useFilteredSearchEntityQueryV2 = ({
queryHook,
orderByField,
filters,
sortOrder = SortOrder.Asc,
sortOrder = 'AscNullsLast',
selectedIds,
mappingFunction,
limit,
@ -43,7 +50,7 @@ export const useFilteredSearchEntityQueryV2 = ({
) => QueryResult<any, any>;
orderByField: string;
filters: SearchFilter[];
sortOrder?: SortOrder;
sortOrder?: OrderBy;
selectedIds: string[];
mappingFunction: (entity: any) => EntityForSelect;
limit?: number;
@ -53,7 +60,7 @@ export const useFilteredSearchEntityQueryV2 = ({
const { loading: selectedEntitiesLoading, data: selectedEntitiesData } =
queryHook({
variables: {
where: {
filter: {
id: {
in: selectedIds,
},
@ -64,30 +71,39 @@ export const useFilteredSearchEntityQueryV2 = ({
} as any,
});
const searchFilter = filters.map(({ fieldNames, filter }) => {
return {
OR: fieldNames.map((fieldName) => ({
[fieldName]: {
startsWith: `%${filter}%`,
mode: QueryMode.Insensitive,
},
})),
};
});
const searchFilter = filters
.map(({ fieldNames, filter }) => {
if (!isNonEmptyString(filter)) {
return undefined;
}
return {
or: fieldNames.map((fieldName) => ({
[fieldName]: {
like: `%${filter}%`,
// TODO: fix mode
// mode: QueryMode.Insensitive,
},
})),
};
})
.filter(isDefined);
const {
loading: filteredSelectedEntitiesLoading,
data: filteredSelectedEntitiesData,
} = queryHook({
variables: {
where: {
AND: [
filter: {
and: [
{
AND: searchFilter,
and: searchFilter,
},
{
id: {
in: selectedIds,
not: {
id: {
in: selectedIds,
},
},
},
],
@ -101,14 +117,16 @@ export const useFilteredSearchEntityQueryV2 = ({
const { loading: entitiesToSelectLoading, data: entitiesToSelectData } =
queryHook({
variables: {
where: {
AND: [
filter: {
and: [
{
AND: searchFilter,
and: searchFilter,
},
{
id: {
notIn: [...selectedIds, ...excludeEntityIds],
not: {
id: {
in: [...selectedIds, ...excludeEntityIds],
},
},
},
],
@ -120,14 +138,6 @@ export const useFilteredSearchEntityQueryV2 = ({
} as any,
});
console.log({
selectedEntitiesData,
test: mapPaginatedObjectsToObjects({
objectNamePlural: objectNamePlural,
pagedObjects: selectedEntitiesData,
}),
});
return {
selectedEntities: mapPaginatedObjectsToObjects({
objectNamePlural: objectNamePlural,

View File

@ -65,10 +65,6 @@ export const SingleEntitySelectBase = <
assertNotNull(entity) && isNonEmptyString(entity.name),
);
console.log({
entitiesInDropdown,
});
const { preselectedOptionId, resetScroll } = useEntitySelectScroll({
selectableOptionIds: [
EmptyButtonId,

View File

@ -105,16 +105,11 @@ export const usePersistField = () => {
valueToPersist,
);
console.log({
fieldName,
valueToPersist,
});
updateEntity?.({
variables: {
where: { id: entityId },
data: {
[fieldName]: valueToPersist?.id,
[fieldName]: valueToPersist?.id ?? null,
},
},
});

View File

@ -1,6 +1,7 @@
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>,
@ -21,6 +22,11 @@ export const getEntityChipFromFieldMetadata = (
// TODO: use every
if (fieldName === 'accountOwner' && fieldValue) {
chipValue.name = fieldValue.firstName + ' ' + fieldValue.lastName;
} else if (fieldName === 'company' && fieldValue) {
chipValue.name = fieldValue.name;
chipValue.pictureUrl = getLogoUrlFromDomainName(fieldValue.domainName);
} else if (fieldName === 'person' && fieldValue) {
chipValue.name = fieldValue.firstName + ' ' + fieldValue.lastName;
}
return chipValue;

View File

@ -4,7 +4,6 @@ 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 { Entity } from '@/ui/input/relation-picker/types/EntityTypeForSelect';
import { UserPicker } from '@/users/components/UserPicker';
import { usePersistField } from '../../../hooks/usePersistField';
@ -55,7 +54,7 @@ export const RelationFieldInput = ({
onCancel={onCancel}
initialSearchFilter={initialSearchValue}
/>
) : fieldDefinition.metadata.relationType === Entity.Company ? (
) : fieldDefinition.metadata.fieldName === 'company' ? (
<CompanyPicker
companyId={initialValue?.id ?? ''}
onSubmit={handleSubmit}

View File

@ -40,9 +40,9 @@ export const UserPicker = ({
objectNamePlural: 'workspaceMembersV2',
});
const useFindManyWorkspaceMembers = () => useQuery(findManyQuery, {});
const useFindManyWorkspaceMembers = (options: any) =>
useQuery(findManyQuery, options);
// TODO: put workspace member
const users = useFilteredSearchEntityQueryV2({
queryHook: useFindManyWorkspaceMembers,
filters: [
@ -51,7 +51,7 @@ export const UserPicker = ({
filter: relationPickerSearchFilter,
},
],
orderByField: '',
orderByField: 'firstName',
mappingFunction: (workspaceMember) => ({
entityType: Entity.WorkspaceMember,
id: workspaceMember.id,
@ -64,10 +64,6 @@ export const UserPicker = ({
objectNamePlural: 'workspaceMembersV2',
});
console.log({
users,
});
const handleEntitySelected = async (selectedUser: any | null | undefined) => {
onSubmit(selectedUser ?? null);
};