Step operand will more or less be the same as view filter operand. This PR: - moves `ViewFilterOperand` to twenty-shared - use it as step operand - check what operand should be available based on the selected field type in filter action - rewrite the function that evaluates filters so it uses ViewFilterOperand instead ViewFilterOperand may be renamed in a future PR.
199 lines
7.3 KiB
TypeScript
199 lines
7.3 KiB
TypeScript
import { isNonEmptyString, isObject } from '@sniptt/guards';
|
|
import qs from 'qs';
|
|
import { useMemo } from 'react';
|
|
import { useParams, useSearchParams } from 'react-router-dom';
|
|
import { useRecoilCallback, useRecoilValue } from 'recoil';
|
|
import z from 'zod';
|
|
|
|
import { useApolloCoreClient } from '@/object-metadata/hooks/useApolloCoreClient';
|
|
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
|
import { useObjectNameSingularFromPlural } from '@/object-metadata/hooks/useObjectNameSingularFromPlural';
|
|
import { objectMetadataItemFamilySelector } from '@/object-metadata/states/objectMetadataItemFamilySelector';
|
|
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
|
|
import { getObjectRecordIdentifier } from '@/object-metadata/utils/getObjectRecordIdentifier';
|
|
import { useObjectPermissions } from '@/object-record/hooks/useObjectPermissions';
|
|
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
|
import { generateFindManyRecordsQuery } from '@/object-record/utils/generateFindManyRecordsQuery';
|
|
import { ViewFilter } from '@/views/types/ViewFilter';
|
|
import { relationFilterValueSchemaObject } from '@/views/view-filter-value/validation-schemas/jsonRelationFilterValueSchema';
|
|
import { ViewFilterOperand } from 'twenty-shared/src/types/ViewFilterOperand';
|
|
import { isDefined } from 'twenty-shared/utils';
|
|
|
|
const filterQueryParamsSchema = z.object({
|
|
viewId: z.string().optional(),
|
|
filter: z
|
|
.record(
|
|
z.record(
|
|
z.nativeEnum(ViewFilterOperand),
|
|
z.string().or(z.array(z.string())).or(relationFilterValueSchemaObject),
|
|
),
|
|
)
|
|
.optional(),
|
|
});
|
|
|
|
export type FilterQueryParams = z.infer<typeof filterQueryParamsSchema>;
|
|
|
|
export const useViewFromQueryParams = () => {
|
|
const apolloCoreClient = useApolloCoreClient();
|
|
const [searchParams] = useSearchParams();
|
|
const { objectNamePlural = '' } = useParams();
|
|
const { objectNameSingular } = useObjectNameSingularFromPlural({
|
|
objectNamePlural,
|
|
});
|
|
const { objectMetadataItem } = useObjectMetadataItem({
|
|
objectNameSingular,
|
|
});
|
|
|
|
const objectMetadataItems = useRecoilValue(objectMetadataItemsState);
|
|
|
|
const { objectPermissionsByObjectMetadataId } = useObjectPermissions();
|
|
|
|
const queryParamsValidation = filterQueryParamsSchema.safeParse(
|
|
qs.parse(searchParams.toString()),
|
|
);
|
|
|
|
const filterQueryParams = useMemo(
|
|
() =>
|
|
queryParamsValidation.success ? queryParamsValidation.data.filter : {},
|
|
[queryParamsValidation],
|
|
);
|
|
const viewIdQueryParam = useMemo(
|
|
() =>
|
|
queryParamsValidation.success
|
|
? queryParamsValidation.data.viewId
|
|
: undefined,
|
|
[queryParamsValidation],
|
|
);
|
|
|
|
const hasFiltersQueryParams =
|
|
isDefined(filterQueryParams) &&
|
|
Object.entries(filterQueryParams).length > 0;
|
|
|
|
const getFiltersFromQueryParams = useRecoilCallback(
|
|
({ snapshot }) =>
|
|
async () => {
|
|
if (!hasFiltersQueryParams) return [];
|
|
|
|
return (
|
|
await Promise.all(
|
|
Object.entries(filterQueryParams).map<Promise<ViewFilter | null>>(
|
|
async ([fieldName, filterFromURL]) => {
|
|
const [filterOperandFromURL, filterValueFromURL] =
|
|
Object.entries(filterFromURL)[0];
|
|
const fieldMetadataItem = objectMetadataItem.fields.find(
|
|
(field) => field.name === fieldName,
|
|
);
|
|
|
|
if (!fieldMetadataItem) return null;
|
|
|
|
const relationObjectMetadataNameSingular =
|
|
fieldMetadataItem.relation?.targetObjectMetadata
|
|
?.nameSingular;
|
|
|
|
const relationObjectMetadataNamePlural =
|
|
fieldMetadataItem.relation?.targetObjectMetadata?.namePlural;
|
|
|
|
const relationObjectMetadataItem =
|
|
relationObjectMetadataNameSingular
|
|
? snapshot
|
|
.getLoadable(
|
|
objectMetadataItemFamilySelector({
|
|
objectName: relationObjectMetadataNameSingular,
|
|
objectNameType: 'singular',
|
|
}),
|
|
)
|
|
.getValue()
|
|
: null;
|
|
|
|
const satisfiesRelationFilterSchema =
|
|
relationFilterValueSchemaObject.safeParse(
|
|
filterValueFromURL,
|
|
)?.success;
|
|
|
|
const relationRecordNames = [];
|
|
|
|
if (
|
|
isNonEmptyString(relationObjectMetadataNamePlural) &&
|
|
isDefined(relationObjectMetadataItem) &&
|
|
(Array.isArray(filterValueFromURL) ||
|
|
satisfiesRelationFilterSchema)
|
|
) {
|
|
const queryResult = await apolloCoreClient.query<
|
|
Record<string, { edges: { node: ObjectRecord }[] }>
|
|
>({
|
|
query: generateFindManyRecordsQuery({
|
|
objectMetadataItem: relationObjectMetadataItem,
|
|
objectMetadataItems,
|
|
objectPermissionsByObjectMetadataId,
|
|
}),
|
|
variables: {
|
|
filter: {
|
|
id: {
|
|
in: satisfiesRelationFilterSchema
|
|
? (
|
|
filterValueFromURL as {
|
|
selectedRecordIds: string[];
|
|
}
|
|
)?.selectedRecordIds
|
|
: filterValueFromURL,
|
|
},
|
|
},
|
|
},
|
|
});
|
|
|
|
const relationRecordNamesFromQuery = queryResult.data?.[
|
|
relationObjectMetadataNamePlural
|
|
]?.edges.map(
|
|
({ node: record }) =>
|
|
getObjectRecordIdentifier({
|
|
objectMetadataItem: relationObjectMetadataItem,
|
|
record,
|
|
}).name,
|
|
);
|
|
|
|
relationRecordNames.push(...relationRecordNamesFromQuery);
|
|
}
|
|
|
|
const filterValueAsString =
|
|
Array.isArray(filterValueFromURL) ||
|
|
isObject(filterValueFromURL)
|
|
? JSON.stringify(filterValueFromURL)
|
|
: filterValueFromURL;
|
|
|
|
return {
|
|
__typename: 'ViewFilter',
|
|
id: `tmp-${[
|
|
fieldName,
|
|
filterOperandFromURL,
|
|
filterValueFromURL,
|
|
].join('-')}`,
|
|
fieldMetadataId: fieldMetadataItem.id,
|
|
operand: filterOperandFromURL as ViewFilterOperand,
|
|
value: filterValueAsString,
|
|
displayValue:
|
|
relationRecordNames?.join(', ') ?? filterValueAsString,
|
|
persistAction: 'NONE',
|
|
};
|
|
},
|
|
),
|
|
)
|
|
).filter(isDefined);
|
|
},
|
|
[
|
|
apolloCoreClient,
|
|
filterQueryParams,
|
|
hasFiltersQueryParams,
|
|
objectMetadataItem.fields,
|
|
objectMetadataItems,
|
|
objectPermissionsByObjectMetadataId,
|
|
],
|
|
);
|
|
|
|
return {
|
|
viewIdQueryParam,
|
|
hasFiltersQueryParams,
|
|
getFiltersFromQueryParams,
|
|
objectMetadataItem,
|
|
};
|
|
};
|