New branch based on feedback in PR #8950 and issue #8016 --------- Co-authored-by: ad-elias <elias@autodiligence.com> Co-authored-by: Lucas Bordeau <bordeau.lucas@gmail.com>
This commit is contained in:
@ -15,6 +15,7 @@ import { DELETE_MAX_COUNT } from '@/object-record/constants/DeleteMaxCount';
|
||||
import { useDeleteManyRecords } from '@/object-record/hooks/useDeleteManyRecords';
|
||||
import { useLazyFetchAllRecords } from '@/object-record/hooks/useLazyFetchAllRecords';
|
||||
import { FilterOperand } from '@/object-record/object-filter-dropdown/types/FilterOperand';
|
||||
import { useFilterValueDependencies } from '@/object-record/record-filter/hooks/useFilterValueDependencies';
|
||||
import { useRecordTable } from '@/object-record/record-table/hooks/useRecordTable';
|
||||
import { ConfirmationModal } from '@/ui/layout/modal/components/ConfirmationModal';
|
||||
import { useRightDrawer } from '@/ui/layout/right-drawer/hooks/useRightDrawer';
|
||||
@ -52,10 +53,13 @@ export const useDeleteMultipleRecordsAction = ({
|
||||
contextStoreFiltersComponentState,
|
||||
);
|
||||
|
||||
const { filterValueDependencies } = useFilterValueDependencies();
|
||||
|
||||
const graphqlFilter = computeContextStoreFilters(
|
||||
contextStoreTargetedRecordsRule,
|
||||
contextStoreFilters,
|
||||
objectMetadataItem,
|
||||
filterValueDependencies,
|
||||
);
|
||||
|
||||
const deletedAtFieldMetadata = objectMetadataItem.fields.find(
|
||||
|
||||
@ -4,6 +4,7 @@ import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/s
|
||||
import { computeContextStoreFilters } from '@/context-store/utils/computeContextStoreFilters';
|
||||
import { useObjectMetadataItemById } from '@/object-metadata/hooks/useObjectMetadataItemById';
|
||||
import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords';
|
||||
import { useFilterValueDependencies } from '@/object-record/record-filter/hooks/useFilterValueDependencies';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||
|
||||
export const useFindManyRecordsSelectedInContextStore = ({
|
||||
@ -30,10 +31,13 @@ export const useFindManyRecordsSelectedInContextStore = ({
|
||||
instanceId,
|
||||
);
|
||||
|
||||
const { filterValueDependencies } = useFilterValueDependencies();
|
||||
|
||||
const queryFilter = computeContextStoreFilters(
|
||||
contextStoreTargetedRecordsRule,
|
||||
contextStoreFilters,
|
||||
objectMetadataItem,
|
||||
filterValueDependencies,
|
||||
);
|
||||
|
||||
const { records, loading, totalCount } = useFindManyRecords({
|
||||
|
||||
@ -1,14 +1,20 @@
|
||||
import { ContextStoreTargetedRecordsRule } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
|
||||
import { computeContextStoreFilters } from '@/context-store/utils/computeContextStoreFilters';
|
||||
import { Filter } from '@/object-record/object-filter-dropdown/types/Filter';
|
||||
import { FilterValueDependencies } from '@/object-record/record-filter/types/FilterValueDependencies';
|
||||
import { ViewFilterOperand } from '@/views/types/ViewFilterOperand';
|
||||
import { expect } from '@storybook/test';
|
||||
import { generatedMockObjectMetadataItems } from '~/testing/mock-data/generatedMockObjectMetadataItems';
|
||||
|
||||
describe('computeContextStoreFilters', () => {
|
||||
const personObjectMetadataItem = generatedMockObjectMetadataItems.find(
|
||||
(item) => item.nameSingular === 'person',
|
||||
)!;
|
||||
|
||||
const mockFilterValueDependencies: FilterValueDependencies = {
|
||||
currentWorkspaceMemberId: '32219445-f587-4c40-b2b1-6d3205ed96da',
|
||||
};
|
||||
|
||||
it('should work for selection mode', () => {
|
||||
const contextStoreTargetedRecordsRule: ContextStoreTargetedRecordsRule = {
|
||||
mode: 'selection',
|
||||
@ -19,6 +25,7 @@ describe('computeContextStoreFilters', () => {
|
||||
contextStoreTargetedRecordsRule,
|
||||
[],
|
||||
personObjectMetadataItem,
|
||||
mockFilterValueDependencies,
|
||||
);
|
||||
|
||||
expect(filters).toEqual({
|
||||
@ -61,6 +68,7 @@ describe('computeContextStoreFilters', () => {
|
||||
contextStoreTargetedRecordsRule,
|
||||
contextStoreFilters,
|
||||
personObjectMetadataItem,
|
||||
mockFilterValueDependencies,
|
||||
);
|
||||
|
||||
expect(filters).toEqual({
|
||||
|
||||
@ -2,6 +2,7 @@ import { ContextStoreTargetedRecordsRule } from '@/context-store/states/contextS
|
||||
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { RecordGqlOperationFilter } from '@/object-record/graphql/types/RecordGqlOperationFilter';
|
||||
import { Filter } from '@/object-record/object-filter-dropdown/types/Filter';
|
||||
import { FilterValueDependencies } from '@/object-record/record-filter/types/FilterValueDependencies';
|
||||
import { computeViewRecordGqlOperationFilter } from '@/object-record/record-filter/utils/computeViewRecordGqlOperationFilter';
|
||||
import { makeAndFilterVariables } from '@/object-record/utils/makeAndFilterVariables';
|
||||
|
||||
@ -9,12 +10,14 @@ export const computeContextStoreFilters = (
|
||||
contextStoreTargetedRecordsRule: ContextStoreTargetedRecordsRule,
|
||||
contextStoreFilters: Filter[],
|
||||
objectMetadataItem: ObjectMetadataItem,
|
||||
filterValueDependencies: FilterValueDependencies,
|
||||
) => {
|
||||
let queryFilter: RecordGqlOperationFilter | undefined;
|
||||
|
||||
if (contextStoreTargetedRecordsRule.mode === 'exclusion') {
|
||||
queryFilter = makeAndFilterVariables([
|
||||
computeViewRecordGqlOperationFilter(
|
||||
filterValueDependencies,
|
||||
contextStoreFilters,
|
||||
objectMetadataItem?.fields ?? [],
|
||||
[],
|
||||
@ -39,6 +42,7 @@ export const computeContextStoreFilters = (
|
||||
},
|
||||
}
|
||||
: computeViewRecordGqlOperationFilter(
|
||||
filterValueDependencies,
|
||||
contextStoreFilters,
|
||||
objectMetadataItem?.fields ?? [],
|
||||
[],
|
||||
|
||||
@ -8,10 +8,10 @@ import { InternalDatePicker } from '@/ui/input/components/internal/date/componen
|
||||
import { ViewFilterOperand } from '@/views/types/ViewFilterOperand';
|
||||
import { computeVariableDateViewFilterValue } from '@/views/view-filter-value/utils/computeVariableDateViewFilterValue';
|
||||
import {
|
||||
resolveDateViewFilterValue,
|
||||
VariableDateViewFilterValueDirection,
|
||||
VariableDateViewFilterValueUnit,
|
||||
} from '@/views/view-filter-value/utils/resolveDateViewFilterValue';
|
||||
import { resolveFilterValue } from '@/views/view-filter-value/utils/resolveFilterValue';
|
||||
import { useState } from 'react';
|
||||
import { isDefined } from 'twenty-ui';
|
||||
import { FieldMetadataType } from '~/generated-metadata/graphql';
|
||||
@ -37,7 +37,7 @@ export const ObjectFilterDropdownDateInput = () => {
|
||||
| undefined;
|
||||
|
||||
const initialFilterValue = selectedFilter
|
||||
? resolveFilterValue(selectedFilter)
|
||||
? resolveDateViewFilterValue(selectedFilter)
|
||||
: null;
|
||||
const [internalDate, setInternalDate] = useState<Date | null>(
|
||||
initialFilterValue instanceof Date ? initialFilterValue : null,
|
||||
@ -98,7 +98,7 @@ export const ObjectFilterDropdownDateInput = () => {
|
||||
selectedOperandInDropdown === ViewFilterOperand.IsRelative;
|
||||
|
||||
const resolvedValue = selectedFilter
|
||||
? resolveFilterValue(selectedFilter)
|
||||
? resolveDateViewFilterValue(selectedFilter)
|
||||
: null;
|
||||
|
||||
const relativeDate =
|
||||
|
||||
@ -0,0 +1,45 @@
|
||||
import { StyledMultipleSelectDropdownAvatarChip } from '@/object-record/select/components/StyledMultipleSelectDropdownAvatarChip';
|
||||
import { SelectableItem } from '@/object-record/select/types/SelectableItem';
|
||||
import styled from '@emotion/styled';
|
||||
import { MenuItemMultiSelectAvatar } from 'twenty-ui';
|
||||
|
||||
const StyledPinnedItemsContainer = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: ${({ theme }) => theme.spacing(1)};
|
||||
`;
|
||||
|
||||
export const ObjectFilterDropdownRecordPinnedItems = (props: {
|
||||
selectableItems: SelectableItem[];
|
||||
onChange: (
|
||||
selectableItem: SelectableItem,
|
||||
isNewCheckedValue: boolean,
|
||||
) => void;
|
||||
}) => {
|
||||
return (
|
||||
<StyledPinnedItemsContainer>
|
||||
{props.selectableItems.map((selectableItem) => {
|
||||
return (
|
||||
<MenuItemMultiSelectAvatar
|
||||
key={selectableItem.id}
|
||||
selected={selectableItem.isSelected}
|
||||
onSelectChange={(newCheckedValue) => {
|
||||
props.onChange(selectableItem, newCheckedValue);
|
||||
}}
|
||||
avatar={
|
||||
<StyledMultipleSelectDropdownAvatarChip
|
||||
className="avatar-icon-container"
|
||||
name={selectableItem.name}
|
||||
avatarUrl={selectableItem.avatarUrl}
|
||||
LeftIcon={selectableItem.AvatarIcon}
|
||||
avatarType={selectableItem.avatarType}
|
||||
isIconInverted={selectableItem.isIconInverted}
|
||||
placeholderColorSeed={selectableItem.id}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</StyledPinnedItemsContainer>
|
||||
);
|
||||
};
|
||||
@ -2,16 +2,26 @@ import { useState } from 'react';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||
import { ObjectFilterDropdownRecordPinnedItems } from '@/object-record/object-filter-dropdown/components/ObjectFilterDropdownRecordPinnedItems';
|
||||
import { CURRENT_WORKSPACE_MEMBER_SELECTABLE_ITEM_ID } from '@/object-record/object-filter-dropdown/constants/CurrentWorkspaceMemberSelectableItemId';
|
||||
import { useFilterDropdown } from '@/object-record/object-filter-dropdown/hooks/useFilterDropdown';
|
||||
import { RelationPickerHotkeyScope } from '@/object-record/relation-picker/types/RelationPickerHotkeyScope';
|
||||
import { MultipleSelectDropdown } from '@/object-record/select/components/MultipleSelectDropdown';
|
||||
import { useRecordsForSelect } from '@/object-record/select/hooks/useRecordsForSelect';
|
||||
import { SelectableItem } from '@/object-record/select/types/SelectableItem';
|
||||
import { useDeleteCombinedViewFilters } from '@/views/hooks/useDeleteCombinedViewFilters';
|
||||
import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator';
|
||||
import { useGetCurrentView } from '@/views/hooks/useGetCurrentView';
|
||||
import { RelationFilterValue } from '@/views/view-filter-value/types/RelationFilterValue';
|
||||
import { relationFilterValueSchema } from '@/views/view-filter-value/validation-schemas/relationFilterValueSchema';
|
||||
import { IconUserCircle } from 'twenty-ui';
|
||||
import { isDefined } from '~/utils/isDefined';
|
||||
|
||||
export const EMPTY_FILTER_VALUE = '[]';
|
||||
export const EMPTY_FILTER_VALUE: string = JSON.stringify({
|
||||
isCurrentWorkspaceMemberSelected: false,
|
||||
selectedRecordIds: [],
|
||||
} satisfies RelationFilterValue);
|
||||
|
||||
export const MAX_RECORDS_TO_DISPLAY = 3;
|
||||
|
||||
type ObjectFilterDropdownRecordSelectProps = {
|
||||
@ -26,15 +36,10 @@ export const ObjectFilterDropdownRecordSelect = ({
|
||||
objectFilterDropdownSearchInputState,
|
||||
selectedOperandInDropdownState,
|
||||
selectedFilterState,
|
||||
setObjectFilterDropdownSelectedRecordIds,
|
||||
objectFilterDropdownSelectedRecordIdsState,
|
||||
selectFilter,
|
||||
emptyFilterButKeepDefinition,
|
||||
} = useFilterDropdown();
|
||||
|
||||
const { deleteCombinedViewFilter } =
|
||||
useDeleteCombinedViewFilters(viewComponentId);
|
||||
|
||||
const { currentViewWithCombinedFiltersAndSorts } =
|
||||
useGetCurrentView(viewComponentId);
|
||||
|
||||
@ -54,9 +59,26 @@ export const ObjectFilterDropdownRecordSelect = ({
|
||||
|
||||
const selectedFilter = useRecoilValue(selectedFilterState);
|
||||
|
||||
const { isCurrentWorkspaceMemberSelected } = relationFilterValueSchema
|
||||
.catch({
|
||||
isCurrentWorkspaceMemberSelected: false,
|
||||
selectedRecordIds: [],
|
||||
})
|
||||
.parse(selectedFilter?.value);
|
||||
|
||||
const objectNameSingular =
|
||||
filterDefinitionUsedInDropdown?.relationObjectMetadataNameSingular;
|
||||
|
||||
if (!isDefined(objectNameSingular)) {
|
||||
throw new Error('relationObjectMetadataNameSingular is not defined');
|
||||
}
|
||||
|
||||
const { objectMetadataItem } = useObjectMetadataItem({
|
||||
objectNameSingular: objectNameSingular,
|
||||
});
|
||||
|
||||
const objectLabelPlural = objectMetadataItem?.labelPlural;
|
||||
|
||||
if (!isDefined(objectNameSingular)) {
|
||||
throw new Error('objectNameSingular is not defined');
|
||||
}
|
||||
@ -69,27 +91,53 @@ export const ObjectFilterDropdownRecordSelect = ({
|
||||
limit: 10,
|
||||
});
|
||||
|
||||
const currentWorkspaceMemberSelectableItem: SelectableItem = {
|
||||
id: CURRENT_WORKSPACE_MEMBER_SELECTABLE_ITEM_ID,
|
||||
name: 'Me',
|
||||
isSelected: isCurrentWorkspaceMemberSelected,
|
||||
AvatarIcon: IconUserCircle,
|
||||
};
|
||||
|
||||
const pinnedSelectableItems: SelectableItem[] =
|
||||
objectNameSingular === 'workspaceMember'
|
||||
? [currentWorkspaceMemberSelectableItem]
|
||||
: [];
|
||||
|
||||
const filteredPinnedSelectableItems = pinnedSelectableItems.filter((item) =>
|
||||
item.name
|
||||
.toLowerCase()
|
||||
.includes(objectFilterDropdownSearchInput.toLowerCase()),
|
||||
);
|
||||
|
||||
const handleMultipleRecordSelectChange = (
|
||||
recordToSelect: SelectableItem,
|
||||
newSelectedValue: boolean,
|
||||
itemToSelect: SelectableItem,
|
||||
isNewSelectedValue: boolean,
|
||||
) => {
|
||||
if (loading) {
|
||||
return;
|
||||
}
|
||||
|
||||
const newSelectedRecordIds = newSelectedValue
|
||||
? [...objectFilterDropdownSelectedRecordIds, recordToSelect.id]
|
||||
: objectFilterDropdownSelectedRecordIds.filter(
|
||||
(id) => id !== recordToSelect.id,
|
||||
);
|
||||
const isItemCurrentWorkspaceMember =
|
||||
itemToSelect.id === CURRENT_WORKSPACE_MEMBER_SELECTABLE_ITEM_ID;
|
||||
|
||||
if (newSelectedRecordIds.length === 0) {
|
||||
emptyFilterButKeepDefinition();
|
||||
deleteCombinedViewFilter(fieldId);
|
||||
return;
|
||||
}
|
||||
const selectedRecordIdsWithAddedRecord = [
|
||||
...objectFilterDropdownSelectedRecordIds,
|
||||
itemToSelect.id,
|
||||
];
|
||||
const selectedRecordIdsWithRemovedRecord =
|
||||
objectFilterDropdownSelectedRecordIds.filter(
|
||||
(id) => id !== itemToSelect.id,
|
||||
);
|
||||
|
||||
setObjectFilterDropdownSelectedRecordIds(newSelectedRecordIds);
|
||||
const newSelectedRecordIds = isItemCurrentWorkspaceMember
|
||||
? objectFilterDropdownSelectedRecordIds
|
||||
: isNewSelectedValue
|
||||
? selectedRecordIdsWithAddedRecord
|
||||
: selectedRecordIdsWithRemovedRecord;
|
||||
|
||||
const newIsCurrentWorkspaceMemberSelected = isItemCurrentWorkspaceMember
|
||||
? isNewSelectedValue
|
||||
: isCurrentWorkspaceMemberSelected;
|
||||
|
||||
const selectedRecordNames = [
|
||||
...recordsToSelect,
|
||||
@ -103,19 +151,32 @@ export const ObjectFilterDropdownRecordSelect = ({
|
||||
.filter((record) => newSelectedRecordIds.includes(record.id))
|
||||
.map((record) => record.name);
|
||||
|
||||
const selectedPinnedItemNames = newIsCurrentWorkspaceMemberSelected
|
||||
? [currentWorkspaceMemberSelectableItem.name]
|
||||
: [];
|
||||
|
||||
const selectedItemNames = [
|
||||
...selectedPinnedItemNames,
|
||||
...selectedRecordNames,
|
||||
];
|
||||
|
||||
const filterDisplayValue =
|
||||
selectedRecordNames.length > MAX_RECORDS_TO_DISPLAY
|
||||
? `${selectedRecordNames.length} companies`
|
||||
: selectedRecordNames.join(', ');
|
||||
selectedItemNames.length > MAX_RECORDS_TO_DISPLAY
|
||||
? `${selectedItemNames.length} ${objectLabelPlural.toLowerCase()}`
|
||||
: selectedItemNames.join(', ');
|
||||
|
||||
if (
|
||||
isDefined(filterDefinitionUsedInDropdown) &&
|
||||
isDefined(selectedOperandInDropdown)
|
||||
) {
|
||||
const newFilterValue =
|
||||
newSelectedRecordIds.length > 0
|
||||
? JSON.stringify(newSelectedRecordIds)
|
||||
: EMPTY_FILTER_VALUE;
|
||||
newSelectedRecordIds.length > 0 || newIsCurrentWorkspaceMemberSelected
|
||||
? JSON.stringify({
|
||||
isCurrentWorkspaceMemberSelected:
|
||||
newIsCurrentWorkspaceMemberSelected,
|
||||
selectedRecordIds: newSelectedRecordIds,
|
||||
} satisfies RelationFilterValue)
|
||||
: '';
|
||||
|
||||
const viewFilter =
|
||||
currentViewWithCombinedFiltersAndSorts?.viewFilters.find(
|
||||
@ -139,15 +200,26 @@ export const ObjectFilterDropdownRecordSelect = ({
|
||||
};
|
||||
|
||||
return (
|
||||
<MultipleSelectDropdown
|
||||
selectableListId="object-filter-record-select-id"
|
||||
hotkeyScope={RelationPickerHotkeyScope.RelationPicker}
|
||||
itemsToSelect={recordsToSelect}
|
||||
filteredSelectedItems={filteredSelectedRecords}
|
||||
selectedItems={selectedRecords}
|
||||
onChange={handleMultipleRecordSelectChange}
|
||||
searchFilter={objectFilterDropdownSearchInput}
|
||||
loadingItems={loading}
|
||||
/>
|
||||
<>
|
||||
{filteredPinnedSelectableItems.length > 0 && (
|
||||
<>
|
||||
<ObjectFilterDropdownRecordPinnedItems
|
||||
selectableItems={filteredPinnedSelectableItems}
|
||||
onChange={handleMultipleRecordSelectChange}
|
||||
/>
|
||||
<DropdownMenuSeparator />
|
||||
</>
|
||||
)}
|
||||
<MultipleSelectDropdown
|
||||
selectableListId="object-filter-record-select-id"
|
||||
hotkeyScope={RelationPickerHotkeyScope.RelationPicker}
|
||||
itemsToSelect={recordsToSelect}
|
||||
filteredSelectedItems={filteredSelectedRecords}
|
||||
selectedItems={selectedRecords}
|
||||
onChange={handleMultipleRecordSelectChange}
|
||||
searchFilter={objectFilterDropdownSearchInput}
|
||||
loadingItems={loading}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@ -0,0 +1,2 @@
|
||||
export const CURRENT_WORKSPACE_MEMBER_SELECTABLE_ITEM_ID =
|
||||
'CURRENT_WORKSPACE_MEMBER';
|
||||
@ -3,6 +3,7 @@ import { RecordBoardContext } from '@/object-record/record-board/contexts/Record
|
||||
import { RecordBoardColumnContext } from '@/object-record/record-board/record-board-column/contexts/RecordBoardColumnContext';
|
||||
import { buildRecordGqlFieldsAggregateForRecordBoard } from '@/object-record/record-board/record-board-column/utils/buildRecordGqlFieldsAggregateForRecordBoard';
|
||||
import { computeAggregateValueAndLabel } from '@/object-record/record-board/record-board-column/utils/computeAggregateValueAndLabel';
|
||||
import { useFilterValueDependencies } from '@/object-record/record-filter/hooks/useFilterValueDependencies';
|
||||
import { computeViewRecordGqlOperationFilter } from '@/object-record/record-filter/utils/computeViewRecordGqlOperationFilter';
|
||||
import { recordIndexFiltersState } from '@/object-record/record-index/states/recordIndexFiltersState';
|
||||
import { recordIndexKanbanAggregateOperationState } from '@/object-record/record-index/states/recordIndexKanbanAggregateOperationState';
|
||||
@ -53,7 +54,11 @@ export const useAggregateRecordsForRecordBoardColumn = () => {
|
||||
);
|
||||
|
||||
const recordIndexFilters = useRecoilValue(recordIndexFiltersState);
|
||||
|
||||
const { filterValueDependencies } = useFilterValueDependencies();
|
||||
|
||||
const requestFilters = computeViewRecordGqlOperationFilter(
|
||||
filterValueDependencies,
|
||||
recordIndexFilters,
|
||||
objectMetadataItem.fields,
|
||||
recordIndexViewFilterGroups,
|
||||
|
||||
@ -0,0 +1,16 @@
|
||||
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
|
||||
import { FilterValueDependencies } from '@/object-record/record-filter/types/FilterValueDependencies';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
export const useFilterValueDependencies = (): {
|
||||
filterValueDependencies: FilterValueDependencies;
|
||||
} => {
|
||||
const { id: currentWorkspaceMemberId } =
|
||||
useRecoilValue(currentWorkspaceMemberState) ?? {};
|
||||
|
||||
return {
|
||||
filterValueDependencies: {
|
||||
currentWorkspaceMemberId,
|
||||
},
|
||||
};
|
||||
};
|
||||
@ -0,0 +1,3 @@
|
||||
export interface FilterValueDependencies {
|
||||
currentWorkspaceMemberId?: string;
|
||||
}
|
||||
@ -1,4 +1,5 @@
|
||||
import { Filter } from '@/object-record/object-filter-dropdown/types/Filter';
|
||||
import { FilterValueDependencies } from '@/object-record/record-filter/types/FilterValueDependencies';
|
||||
import { computeViewRecordGqlOperationFilter } from '@/object-record/record-filter/utils/computeViewRecordGqlOperationFilter';
|
||||
import { ViewFilterOperand } from '@/views/types/ViewFilterOperand';
|
||||
import { getCompaniesMock } from '~/testing/mock-data/companies';
|
||||
@ -14,6 +15,10 @@ const personMockObjectMetadataItem = generatedMockObjectMetadataItems.find(
|
||||
(item) => item.nameSingular === 'person',
|
||||
)!;
|
||||
|
||||
const mockFilterValueDependencies: FilterValueDependencies = {
|
||||
currentWorkspaceMemberId: '32219445-f587-4c40-b2b1-6d3205ed96da',
|
||||
};
|
||||
|
||||
jest.useFakeTimers().setSystemTime(new Date('2020-01-01'));
|
||||
|
||||
describe('computeViewRecordGqlOperationFilter', () => {
|
||||
@ -38,6 +43,7 @@ describe('computeViewRecordGqlOperationFilter', () => {
|
||||
};
|
||||
|
||||
const result = computeViewRecordGqlOperationFilter(
|
||||
mockFilterValueDependencies,
|
||||
[nameFilter],
|
||||
companyMockObjectMetadataItem.fields,
|
||||
[],
|
||||
@ -90,6 +96,7 @@ describe('computeViewRecordGqlOperationFilter', () => {
|
||||
};
|
||||
|
||||
const result = computeViewRecordGqlOperationFilter(
|
||||
mockFilterValueDependencies,
|
||||
[nameFilter, employeesFilter],
|
||||
companyMockObjectMetadataItem.fields,
|
||||
[],
|
||||
@ -176,6 +183,7 @@ describe('should work as expected for the different field types', () => {
|
||||
};
|
||||
|
||||
const result = computeViewRecordGqlOperationFilter(
|
||||
mockFilterValueDependencies,
|
||||
[
|
||||
addressFilterContains,
|
||||
addressFilterDoesNotContain,
|
||||
@ -558,6 +566,7 @@ describe('should work as expected for the different field types', () => {
|
||||
};
|
||||
|
||||
const result = computeViewRecordGqlOperationFilter(
|
||||
mockFilterValueDependencies,
|
||||
[
|
||||
phonesFilterContains,
|
||||
phonesFilterDoesNotContain,
|
||||
@ -759,6 +768,7 @@ describe('should work as expected for the different field types', () => {
|
||||
};
|
||||
|
||||
const result = computeViewRecordGqlOperationFilter(
|
||||
mockFilterValueDependencies,
|
||||
[
|
||||
emailsFilterContains,
|
||||
emailsFilterDoesNotContain,
|
||||
@ -914,6 +924,7 @@ describe('should work as expected for the different field types', () => {
|
||||
};
|
||||
|
||||
const result = computeViewRecordGqlOperationFilter(
|
||||
mockFilterValueDependencies,
|
||||
[
|
||||
dateFilterIsAfter,
|
||||
dateFilterIsBefore,
|
||||
@ -1030,6 +1041,7 @@ describe('should work as expected for the different field types', () => {
|
||||
};
|
||||
|
||||
const result = computeViewRecordGqlOperationFilter(
|
||||
mockFilterValueDependencies,
|
||||
[
|
||||
employeesFilterIsGreaterThan,
|
||||
employeesFilterIsLessThan,
|
||||
|
||||
@ -28,14 +28,18 @@ import {
|
||||
convertRatingToRatingValue,
|
||||
} from '@/object-record/object-filter-dropdown/components/ObjectFilterDropdownRatingInput';
|
||||
import { Filter } from '@/object-record/object-filter-dropdown/types/Filter';
|
||||
import { FilterValueDependencies } from '@/object-record/record-filter/types/FilterValueDependencies';
|
||||
import { getEmptyRecordGqlOperationFilter } from '@/object-record/record-filter/utils/getEmptyRecordGqlOperationFilter';
|
||||
import { ViewFilterGroup } from '@/views/types/ViewFilterGroup';
|
||||
import { ViewFilterGroupLogicalOperator } from '@/views/types/ViewFilterGroupLogicalOperator';
|
||||
import { resolveFilterValue } from '@/views/view-filter-value/utils/resolveFilterValue';
|
||||
import { resolveDateViewFilterValue } from '@/views/view-filter-value/utils/resolveDateViewFilterValue';
|
||||
import { resolveSelectViewFilterValue } from '@/views/view-filter-value/utils/resolveSelectViewFilterValue';
|
||||
import { relationFilterValueSchema } from '@/views/view-filter-value/validation-schemas/relationFilterValueSchema';
|
||||
import { endOfDay, roundToNearestMinutes, startOfDay } from 'date-fns';
|
||||
import { z } from 'zod';
|
||||
|
||||
const computeFilterRecordGqlOperationFilter = (
|
||||
filterValueDependencies: FilterValueDependencies,
|
||||
filter: Filter,
|
||||
fields: Pick<Field, 'id' | 'name'>[],
|
||||
): RecordGqlOperationFilter | undefined => {
|
||||
@ -124,7 +128,7 @@ const computeFilterRecordGqlOperationFilter = (
|
||||
}
|
||||
case 'DATE':
|
||||
case 'DATE_TIME': {
|
||||
const resolvedFilterValue = resolveFilterValue(filter);
|
||||
const resolvedFilterValue = resolveDateViewFilterValue(filter);
|
||||
const now = roundToNearestMinutes(new Date());
|
||||
const date =
|
||||
resolvedFilterValue instanceof Date ? resolvedFilterValue : now;
|
||||
@ -157,11 +161,8 @@ const computeFilterRecordGqlOperationFilter = (
|
||||
.object({ start: z.date(), end: z.date() })
|
||||
.safeParse(resolvedFilterValue).data;
|
||||
|
||||
const defaultDateRange = resolveFilterValue({
|
||||
const defaultDateRange = resolveDateViewFilterValue({
|
||||
value: 'PAST_1_DAY',
|
||||
definition: {
|
||||
type: 'DATE',
|
||||
},
|
||||
operand: ViewFilterOperand.IsRelative,
|
||||
});
|
||||
|
||||
@ -303,32 +304,41 @@ const computeFilterRecordGqlOperationFilter = (
|
||||
}
|
||||
case 'RELATION': {
|
||||
if (!isEmptyOperand) {
|
||||
try {
|
||||
JSON.parse(filter.value);
|
||||
} catch (e) {
|
||||
throw new Error(
|
||||
`Cannot parse filter value for RELATION filter : "${filter.value}"`,
|
||||
);
|
||||
}
|
||||
const { isCurrentWorkspaceMemberSelected, selectedRecordIds } =
|
||||
relationFilterValueSchema.parse(filter.value);
|
||||
|
||||
const parsedRecordIds = JSON.parse(filter.value) as string[];
|
||||
const recordIds = isCurrentWorkspaceMemberSelected
|
||||
? [
|
||||
...selectedRecordIds,
|
||||
filterValueDependencies.currentWorkspaceMemberId,
|
||||
]
|
||||
: selectedRecordIds;
|
||||
|
||||
if (parsedRecordIds.length === 0) return;
|
||||
if (recordIds.length === 0) return;
|
||||
switch (filter.operand) {
|
||||
case ViewFilterOperand.Is:
|
||||
return {
|
||||
[correspondingField.name + 'Id']: {
|
||||
in: parsedRecordIds,
|
||||
in: recordIds,
|
||||
} as RelationFilter,
|
||||
};
|
||||
case ViewFilterOperand.IsNot: {
|
||||
if (parsedRecordIds.length === 0) return;
|
||||
if (recordIds.length === 0) return;
|
||||
return {
|
||||
not: {
|
||||
[correspondingField.name + 'Id']: {
|
||||
in: parsedRecordIds,
|
||||
} as RelationFilter,
|
||||
},
|
||||
or: [
|
||||
{
|
||||
not: {
|
||||
[correspondingField.name + 'Id']: {
|
||||
in: recordIds,
|
||||
} as RelationFilter,
|
||||
},
|
||||
},
|
||||
{
|
||||
[correspondingField.name + 'Id']: {
|
||||
is: 'NULL',
|
||||
} as RelationFilter,
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
default:
|
||||
@ -611,9 +621,7 @@ const computeFilterRecordGqlOperationFilter = (
|
||||
);
|
||||
}
|
||||
|
||||
const options = resolveFilterValue(
|
||||
filter as Filter & { definition: { type: 'MULTI_SELECT' } },
|
||||
);
|
||||
const options = resolveSelectViewFilterValue(filter);
|
||||
|
||||
if (options.length === 0) return;
|
||||
|
||||
@ -660,9 +668,7 @@ const computeFilterRecordGqlOperationFilter = (
|
||||
filter.definition,
|
||||
);
|
||||
}
|
||||
const options = resolveFilterValue(
|
||||
filter as Filter & { definition: { type: 'SELECT' } },
|
||||
);
|
||||
const options = resolveSelectViewFilterValue(filter);
|
||||
|
||||
if (options.length === 0) return;
|
||||
|
||||
@ -869,6 +875,7 @@ const computeFilterRecordGqlOperationFilter = (
|
||||
};
|
||||
|
||||
const computeViewFilterGroupRecordGqlOperationFilter = (
|
||||
filterValueDependencies: FilterValueDependencies,
|
||||
filters: Filter[],
|
||||
fields: Pick<Field, 'id' | 'name'>[],
|
||||
viewFilterGroups: ViewFilterGroup[],
|
||||
@ -887,7 +894,13 @@ const computeViewFilterGroupRecordGqlOperationFilter = (
|
||||
);
|
||||
|
||||
const groupRecordGqlOperationFilters = groupFilters
|
||||
.map((filter) => computeFilterRecordGqlOperationFilter(filter, fields))
|
||||
.map((filter) =>
|
||||
computeFilterRecordGqlOperationFilter(
|
||||
filterValueDependencies,
|
||||
filter,
|
||||
fields,
|
||||
),
|
||||
)
|
||||
.filter(isDefined);
|
||||
|
||||
const subGroupRecordGqlOperationFilters = viewFilterGroups
|
||||
@ -897,6 +910,7 @@ const computeViewFilterGroupRecordGqlOperationFilter = (
|
||||
)
|
||||
.map((subViewFilterGroup) =>
|
||||
computeViewFilterGroupRecordGqlOperationFilter(
|
||||
filterValueDependencies,
|
||||
filters,
|
||||
fields,
|
||||
viewFilterGroups,
|
||||
@ -932,6 +946,7 @@ const computeViewFilterGroupRecordGqlOperationFilter = (
|
||||
};
|
||||
|
||||
export const computeViewRecordGqlOperationFilter = (
|
||||
filterValueDependencies: FilterValueDependencies,
|
||||
filters: Filter[],
|
||||
fields: Pick<Field, 'id' | 'name'>[],
|
||||
viewFilterGroups: ViewFilterGroup[],
|
||||
@ -939,7 +954,11 @@ export const computeViewRecordGqlOperationFilter = (
|
||||
const regularRecordGqlOperationFilter: RecordGqlOperationFilter[] = filters
|
||||
.filter((filter) => !filter.viewFilterGroupId)
|
||||
.map((regularFilter) =>
|
||||
computeFilterRecordGqlOperationFilter(regularFilter, fields),
|
||||
computeFilterRecordGqlOperationFilter(
|
||||
filterValueDependencies,
|
||||
regularFilter,
|
||||
fields,
|
||||
),
|
||||
)
|
||||
.filter(isDefined);
|
||||
|
||||
@ -949,6 +968,7 @@ export const computeViewRecordGqlOperationFilter = (
|
||||
|
||||
const advancedRecordGqlOperationFilter =
|
||||
computeViewFilterGroupRecordGqlOperationFilter(
|
||||
filterValueDependencies,
|
||||
filters,
|
||||
fields,
|
||||
viewFilterGroups,
|
||||
|
||||
@ -5,6 +5,7 @@ import { computeContextStoreFilters } from '@/context-store/utils/computeContext
|
||||
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||
import { useObjectNameSingularFromPlural } from '@/object-metadata/hooks/useObjectNameSingularFromPlural';
|
||||
import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords';
|
||||
import { useFilterValueDependencies } from '@/object-record/record-filter/hooks/useFilterValueDependencies';
|
||||
import { useRecordIndexContextOrThrow } from '@/object-record/record-index/contexts/RecordIndexContext';
|
||||
import { useFindManyRecordIndexTableParams } from '@/object-record/record-index/hooks/useFindManyRecordIndexTableParams';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||
@ -40,6 +41,8 @@ export const RecordIndexContainerContextStoreNumberOfSelectedRecordsEffect =
|
||||
contextStoreFiltersComponentState,
|
||||
);
|
||||
|
||||
const { filterValueDependencies } = useFilterValueDependencies();
|
||||
|
||||
const { totalCount } = useFindManyRecords({
|
||||
...findManyRecordsParams,
|
||||
recordGqlFields: {
|
||||
@ -49,6 +52,7 @@ export const RecordIndexContainerContextStoreNumberOfSelectedRecordsEffect =
|
||||
contextStoreTargetedRecordsRule,
|
||||
contextStoreFilters,
|
||||
objectMetadataItem,
|
||||
filterValueDependencies,
|
||||
),
|
||||
limit: 1,
|
||||
skip: contextStoreTargetedRecordsRule.mode === 'selection',
|
||||
|
||||
@ -8,6 +8,7 @@ import { computeContextStoreFilters } from '@/context-store/utils/computeContext
|
||||
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { EXPORT_TABLE_DATA_DEFAULT_PAGE_SIZE } from '@/object-record/object-options-dropdown/constants/ExportTableDataDefaultPageSize';
|
||||
import { useObjectOptionsForBoard } from '@/object-record/object-options-dropdown/hooks/useObjectOptionsForBoard';
|
||||
import { useFilterValueDependencies } from '@/object-record/record-filter/hooks/useFilterValueDependencies';
|
||||
import { recordGroupFieldMetadataComponentState } from '@/object-record/record-group/states/recordGroupFieldMetadataComponentState';
|
||||
import { useFindManyRecordIndexTableParams } from '@/object-record/record-index/hooks/useFindManyRecordIndexTableParams';
|
||||
import { visibleTableColumnsComponentSelector } from '@/object-record/record-table/states/selectors/visibleTableColumnsComponentSelector';
|
||||
@ -71,10 +72,13 @@ export const useExportFetchRecords = ({
|
||||
contextStoreFiltersComponentState,
|
||||
);
|
||||
|
||||
const { filterValueDependencies } = useFilterValueDependencies();
|
||||
|
||||
const queryFilter = computeContextStoreFilters(
|
||||
contextStoreTargetedRecordsRule,
|
||||
contextStoreFilters,
|
||||
objectMetadataItem,
|
||||
filterValueDependencies,
|
||||
);
|
||||
|
||||
const findManyRecordsParams = useFindManyRecordIndexTableParams(
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||
import { turnSortsIntoOrderBy } from '@/object-record/object-sort-dropdown/utils/turnSortsIntoOrderBy';
|
||||
import { useFilterValueDependencies } from '@/object-record/record-filter/hooks/useFilterValueDependencies';
|
||||
import { computeViewRecordGqlOperationFilter } from '@/object-record/record-filter/utils/computeViewRecordGqlOperationFilter';
|
||||
import { useCurrentRecordGroupDefinition } from '@/object-record/record-group/hooks/useCurrentRecordGroupDefinition';
|
||||
import { useRecordGroupFilter } from '@/object-record/record-group/hooks/useRecordGroupFilter';
|
||||
@ -35,7 +36,10 @@ export const useFindManyRecordIndexTableParams = (
|
||||
recordTableId,
|
||||
);
|
||||
|
||||
const { filterValueDependencies } = useFilterValueDependencies();
|
||||
|
||||
const stateFilter = computeViewRecordGqlOperationFilter(
|
||||
filterValueDependencies,
|
||||
tableFilters,
|
||||
objectMetadataItem?.fields ?? [],
|
||||
tableViewFilterGroups,
|
||||
|
||||
@ -7,6 +7,7 @@ import { turnSortsIntoOrderBy } from '@/object-record/object-sort-dropdown/utils
|
||||
import { useSetRecordBoardRecordIds } from '@/object-record/record-board/hooks/useSetRecordBoardRecordIds';
|
||||
import { isRecordBoardCompactModeActiveComponentState } from '@/object-record/record-board/states/isRecordBoardCompactModeActiveComponentState';
|
||||
import { recordBoardFieldDefinitionsComponentState } from '@/object-record/record-board/states/recordBoardFieldDefinitionsComponentState';
|
||||
import { useFilterValueDependencies } from '@/object-record/record-filter/hooks/useFilterValueDependencies';
|
||||
import { computeViewRecordGqlOperationFilter } from '@/object-record/record-filter/utils/computeViewRecordGqlOperationFilter';
|
||||
import { useRecordBoardRecordGqlFields } from '@/object-record/record-index/hooks/useRecordBoardRecordGqlFields';
|
||||
import { recordIndexFieldDefinitionsState } from '@/object-record/record-index/states/recordIndexFieldDefinitionsState';
|
||||
@ -56,7 +57,11 @@ export const useLoadRecordIndexBoard = ({
|
||||
|
||||
const recordIndexFilters = useRecoilValue(recordIndexFiltersState);
|
||||
const recordIndexSorts = useRecoilValue(recordIndexSortsState);
|
||||
|
||||
const { filterValueDependencies } = useFilterValueDependencies();
|
||||
|
||||
const requestFilters = computeViewRecordGqlOperationFilter(
|
||||
filterValueDependencies,
|
||||
recordIndexFilters,
|
||||
objectMetadataItem?.fields ?? [],
|
||||
recordIndexViewFilterGroups,
|
||||
|
||||
@ -5,6 +5,7 @@ import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadata
|
||||
import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords';
|
||||
import { turnSortsIntoOrderBy } from '@/object-record/object-sort-dropdown/utils/turnSortsIntoOrderBy';
|
||||
import { useSetRecordIdsForColumn } from '@/object-record/record-board/hooks/useSetRecordIdsForColumn';
|
||||
import { useFilterValueDependencies } from '@/object-record/record-filter/hooks/useFilterValueDependencies';
|
||||
import { computeViewRecordGqlOperationFilter } from '@/object-record/record-filter/utils/computeViewRecordGqlOperationFilter';
|
||||
import { recordGroupDefinitionFamilyState } from '@/object-record/record-group/states/recordGroupDefinitionFamilyState';
|
||||
import { useRecordBoardRecordGqlFields } from '@/object-record/record-index/hooks/useRecordBoardRecordGqlFields';
|
||||
@ -43,7 +44,10 @@ export const useLoadRecordIndexBoardColumn = ({
|
||||
const recordIndexFilters = useRecoilValue(recordIndexFiltersState);
|
||||
const recordIndexSorts = useRecoilValue(recordIndexSortsState);
|
||||
|
||||
const { filterValueDependencies } = useFilterValueDependencies();
|
||||
|
||||
const requestFilters = computeViewRecordGqlOperationFilter(
|
||||
filterValueDependencies,
|
||||
recordIndexFilters,
|
||||
objectMetadataItem?.fields ?? [],
|
||||
recordIndexViewFilterGroups,
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { useAggregateRecords } from '@/object-record/hooks/useAggregateRecords';
|
||||
import { computeAggregateValueAndLabel } from '@/object-record/record-board/record-board-column/utils/computeAggregateValueAndLabel';
|
||||
import { useFilterValueDependencies } from '@/object-record/record-filter/hooks/useFilterValueDependencies';
|
||||
import { computeViewRecordGqlOperationFilter } from '@/object-record/record-filter/utils/computeViewRecordGqlOperationFilter';
|
||||
import { useRecordGroupFilter } from '@/object-record/record-group/hooks/useRecordGroupFilter';
|
||||
import { recordIndexFiltersState } from '@/object-record/record-index/states/recordIndexFiltersState';
|
||||
@ -26,7 +27,11 @@ export const useAggregateRecordsForRecordTableColumnFooter = (
|
||||
);
|
||||
|
||||
const recordIndexFilters = useRecoilValue(recordIndexFiltersState);
|
||||
|
||||
const { filterValueDependencies } = useFilterValueDependencies();
|
||||
|
||||
const requestFilters = computeViewRecordGqlOperationFilter(
|
||||
filterValueDependencies,
|
||||
recordIndexFilters,
|
||||
objectMetadataItem.fields,
|
||||
recordIndexViewFilterGroups,
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
import styled from '@emotion/styled';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { Key } from 'ts-key-enum';
|
||||
import { AvatarChip, MenuItem, MenuItemMultiSelectAvatar } from 'twenty-ui';
|
||||
import { MenuItem, MenuItemMultiSelectAvatar } from 'twenty-ui';
|
||||
|
||||
import { StyledMultipleSelectDropdownAvatarChip } from '@/object-record/select/components/StyledMultipleSelectDropdownAvatarChip';
|
||||
import { SelectableItem } from '@/object-record/select/types/SelectableItem';
|
||||
import { DropdownMenuSkeletonItem } from '@/ui/input/relation-picker/components/skeletons/DropdownMenuSkeletonItem';
|
||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||
@ -13,16 +13,6 @@ import { useSelectableListStates } from '@/ui/layout/selectable-list/hooks/inter
|
||||
import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectableList';
|
||||
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
|
||||
|
||||
const StyledAvatarChip = styled(AvatarChip)`
|
||||
&.avatar-icon-container {
|
||||
color: ${({ theme }) => theme.font.color.secondary};
|
||||
gap: ${({ theme }) => theme.spacing(2)};
|
||||
padding-left: 0px;
|
||||
padding-right: 0px;
|
||||
font-size: ${({ theme }) => theme.font.size.md};
|
||||
}
|
||||
`;
|
||||
|
||||
export const MultipleSelectDropdown = ({
|
||||
selectableListId,
|
||||
hotkeyScope,
|
||||
@ -129,7 +119,7 @@ export const MultipleSelectDropdown = ({
|
||||
handleItemSelectChange(item, newCheckedValue);
|
||||
}}
|
||||
avatar={
|
||||
<StyledAvatarChip
|
||||
<StyledMultipleSelectDropdownAvatarChip
|
||||
className="avatar-icon-container"
|
||||
name={item.name}
|
||||
avatarUrl={item.avatarUrl}
|
||||
|
||||
@ -0,0 +1,12 @@
|
||||
import styled from '@emotion/styled';
|
||||
import { AvatarChip } from 'twenty-ui';
|
||||
|
||||
export const StyledMultipleSelectDropdownAvatarChip = styled(AvatarChip)`
|
||||
&.avatar-icon-container {
|
||||
color: ${({ theme }) => theme.font.color.secondary};
|
||||
gap: ${({ theme }) => theme.spacing(2)};
|
||||
padding-left: 0px;
|
||||
padding-right: 0px;
|
||||
font-size: ${({ theme }) => theme.font.size.md};
|
||||
}
|
||||
`;
|
||||
@ -10,6 +10,7 @@ import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-sta
|
||||
import { useGetCurrentView } from '@/views/hooks/useGetCurrentView';
|
||||
import { useUpsertCombinedViewFilters } from '@/views/hooks/useUpsertCombinedViewFilters';
|
||||
import { availableFilterDefinitionsComponentState } from '@/views/states/availableFilterDefinitionsComponentState';
|
||||
import { relationFilterValueSchema } from '@/views/view-filter-value/validation-schemas/relationFilterValueSchema';
|
||||
import { isDefined } from '~/utils/isDefined';
|
||||
|
||||
type ViewBarFilterEffectProps = {
|
||||
@ -69,12 +70,14 @@ export const ViewBarFilterEffect = ({
|
||||
filterDefinitionUsedInDropdown?.fieldMetadataId,
|
||||
);
|
||||
|
||||
const viewFilterSelectedRecords = isNonEmptyString(
|
||||
viewFilterUsedInDropdown?.value,
|
||||
)
|
||||
? JSON.parse(viewFilterUsedInDropdown.value)
|
||||
: [];
|
||||
setObjectFilterDropdownSelectedRecordIds(viewFilterSelectedRecords);
|
||||
const { selectedRecordIds } = relationFilterValueSchema
|
||||
.catch({
|
||||
isCurrentWorkspaceMemberSelected: false,
|
||||
selectedRecordIds: [],
|
||||
})
|
||||
.parse(viewFilterUsedInDropdown?.value);
|
||||
|
||||
setObjectFilterDropdownSelectedRecordIds(selectedRecordIds);
|
||||
} else if (
|
||||
isDefined(filterDefinitionUsedInDropdown) &&
|
||||
['SELECT', 'MULTI_SELECT'].includes(filterDefinitionUsedInDropdown.type)
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { useActiveFieldMetadataItems } from '@/object-metadata/hooks/useActiveFieldMetadataItems';
|
||||
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { useFilterValueDependencies } from '@/object-record/record-filter/hooks/useFilterValueDependencies';
|
||||
import { useViewOrDefaultViewFromPrefetchedViews } from '@/views/hooks/useViewOrDefaultViewFromPrefetchedViews';
|
||||
import { getQueryVariablesFromView } from '@/views/utils/getQueryVariablesFromView';
|
||||
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
|
||||
@ -22,11 +23,14 @@ export const useQueryVariablesFromActiveFieldsOfViewOrDefaultView = ({
|
||||
|
||||
const isJsonFilterEnabled = useIsFeatureEnabled('IS_JSON_FILTER_ENABLED');
|
||||
|
||||
const { filterValueDependencies } = useFilterValueDependencies();
|
||||
|
||||
const { filter, orderBy } = getQueryVariablesFromView({
|
||||
fieldMetadataItems: activeFieldMetadataItems,
|
||||
objectMetadataItem,
|
||||
view,
|
||||
isJsonFilterEnabled,
|
||||
filterValueDependencies,
|
||||
});
|
||||
|
||||
return {
|
||||
|
||||
@ -3,6 +3,7 @@ import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { formatFieldMetadataItemsAsFilterDefinitions } from '@/object-metadata/utils/formatFieldMetadataItemsAsFilterDefinitions';
|
||||
import { formatFieldMetadataItemsAsSortDefinitions } from '@/object-metadata/utils/formatFieldMetadataItemsAsSortDefinitions';
|
||||
import { turnSortsIntoOrderBy } from '@/object-record/object-sort-dropdown/utils/turnSortsIntoOrderBy';
|
||||
import { FilterValueDependencies } from '@/object-record/record-filter/types/FilterValueDependencies';
|
||||
import { computeViewRecordGqlOperationFilter } from '@/object-record/record-filter/utils/computeViewRecordGqlOperationFilter';
|
||||
import { View } from '@/views/types/View';
|
||||
import { mapViewFiltersToFilters } from '@/views/utils/mapViewFiltersToFilters';
|
||||
@ -14,11 +15,13 @@ export const getQueryVariablesFromView = ({
|
||||
fieldMetadataItems,
|
||||
objectMetadataItem,
|
||||
isJsonFilterEnabled,
|
||||
filterValueDependencies,
|
||||
}: {
|
||||
view: View | null | undefined;
|
||||
fieldMetadataItems: FieldMetadataItem[];
|
||||
objectMetadataItem: ObjectMetadataItem;
|
||||
isJsonFilterEnabled: boolean;
|
||||
filterValueDependencies: FilterValueDependencies;
|
||||
}) => {
|
||||
if (!isDefined(view)) {
|
||||
return {
|
||||
@ -39,6 +42,7 @@ export const getQueryVariablesFromView = ({
|
||||
});
|
||||
|
||||
const filter = computeViewRecordGqlOperationFilter(
|
||||
filterValueDependencies,
|
||||
mapViewFiltersToFilters(viewFilters, filterDefinitions),
|
||||
objectMetadataItem?.fields ?? [],
|
||||
viewFilterGroups ?? [],
|
||||
|
||||
@ -0,0 +1,4 @@
|
||||
import { relationFilterValueSchema } from '@/views/view-filter-value/validation-schemas/relationFilterValueSchema';
|
||||
import { z } from 'zod';
|
||||
|
||||
export type RelationFilterValue = z.infer<typeof relationFilterValueSchema>;
|
||||
@ -1,7 +0,0 @@
|
||||
import { ViewFilter } from '@/views/types/ViewFilter';
|
||||
|
||||
export const resolveBooleanViewFilterValue = (
|
||||
viewFilter: Pick<ViewFilter, 'value'>,
|
||||
) => {
|
||||
return viewFilter.value === 'true';
|
||||
};
|
||||
@ -1,53 +0,0 @@
|
||||
import { Filter } from '@/object-record/object-filter-dropdown/types/Filter';
|
||||
import { FilterableFieldType } from '@/object-record/object-filter-dropdown/types/FilterableFieldType';
|
||||
import { ViewFilterOperand } from '@/views/types/ViewFilterOperand';
|
||||
import { resolveNumberViewFilterValue } from '@/views/view-filter-value/utils/resolveNumberViewFilterValue';
|
||||
import { resolveSelectViewFilterValue } from '@/views/view-filter-value/utils/resolveSelectViewFilterValue';
|
||||
import {
|
||||
resolveDateViewFilterValue,
|
||||
ResolvedDateViewFilterValue,
|
||||
} from './resolveDateViewFilterValue';
|
||||
import { resolveBooleanViewFilterValue } from '@/views/view-filter-value/utils/resolveBooleanViewFilterValue';
|
||||
|
||||
type ResolvedFilterValue<
|
||||
T extends FilterableFieldType,
|
||||
O extends ViewFilterOperand,
|
||||
> = T extends 'DATE' | 'DATE_TIME'
|
||||
? ResolvedDateViewFilterValue<O>
|
||||
: T extends 'NUMBER'
|
||||
? ReturnType<typeof resolveNumberViewFilterValue>
|
||||
: T extends 'SELECT' | 'MULTI_SELECT'
|
||||
? string[]
|
||||
: T extends 'BOOLEAN'
|
||||
? boolean
|
||||
: string;
|
||||
|
||||
type PartialFilter<
|
||||
T extends FilterableFieldType,
|
||||
O extends ViewFilterOperand,
|
||||
> = Pick<Filter, 'value'> & {
|
||||
definition: { type: T };
|
||||
operand: O;
|
||||
};
|
||||
|
||||
export const resolveFilterValue = <
|
||||
T extends FilterableFieldType,
|
||||
O extends ViewFilterOperand,
|
||||
>(
|
||||
filter: PartialFilter<T, O>,
|
||||
) => {
|
||||
switch (filter.definition.type) {
|
||||
case 'DATE':
|
||||
case 'DATE_TIME':
|
||||
return resolveDateViewFilterValue(filter) as ResolvedFilterValue<T, O>;
|
||||
case 'NUMBER':
|
||||
return resolveNumberViewFilterValue(filter) as ResolvedFilterValue<T, O>;
|
||||
case 'SELECT':
|
||||
case 'MULTI_SELECT':
|
||||
return resolveSelectViewFilterValue(filter) as ResolvedFilterValue<T, O>;
|
||||
case 'BOOLEAN':
|
||||
return resolveBooleanViewFilterValue(filter) as ResolvedFilterValue<T, O>;
|
||||
default:
|
||||
return filter.value as ResolvedFilterValue<T, O>;
|
||||
}
|
||||
};
|
||||
@ -1,7 +0,0 @@
|
||||
import { ViewFilter } from '@/views/types/ViewFilter';
|
||||
|
||||
export const resolveNumberViewFilterValue = (
|
||||
viewFilter: Pick<ViewFilter, 'value'>,
|
||||
) => {
|
||||
return viewFilter.value === '' ? null : +viewFilter.value;
|
||||
};
|
||||
@ -0,0 +1,21 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
export const relationFilterValueSchema = z
|
||||
.string()
|
||||
.transform((value, ctx) => {
|
||||
try {
|
||||
return JSON.parse(value);
|
||||
} catch (error) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: (error as Error).message,
|
||||
});
|
||||
return z.NEVER;
|
||||
}
|
||||
})
|
||||
.pipe(
|
||||
z.object({
|
||||
isCurrentWorkspaceMemberSelected: z.boolean(),
|
||||
selectedRecordIds: z.array(z.string()),
|
||||
}),
|
||||
);
|
||||
Reference in New Issue
Block a user