Compute Filter DisplayValue at runtime on FE (#14148)
<img width="1913" height="979" alt="image" src="https://github.com/user-attachments/assets/33936329-47b1-487f-94fa-54c45e7843fc" /> <img width="1913" height="979" alt="image" src="https://github.com/user-attachments/assets/6ea992a5-1ac6-49f6-8da1-661ca35144aa" />
This commit is contained in:
+14
-5
@@ -8,6 +8,7 @@ import { useRemoveRecordFilter } from '@/object-record/record-filter/hooks/useRe
|
||||
import { isRecordFilterConsideredEmpty } from '@/object-record/record-filter/utils/isRecordFilterConsideredEmpty';
|
||||
import { useCloseDropdown } from '@/ui/layout/dropdown/hooks/useCloseDropdown';
|
||||
import { EditableFilterChipDropdownContent } from '@/views/components/EditableFilterChipDropdownContent';
|
||||
import { EditableRelationFilterChip } from '@/views/components/EditableRelationFilterChip';
|
||||
import { useClearVectorSearchInput } from '@/views/hooks/useClearVectorSearchInput';
|
||||
import { useSetEditableFilterChipDropdownStates } from '@/views/hooks/useSetEditableFilterChipDropdownStates';
|
||||
|
||||
@@ -54,11 +55,19 @@ export const EditableFilterDropdownButton = ({
|
||||
<Dropdown
|
||||
dropdownId={recordFilter.id}
|
||||
clickableComponent={
|
||||
<EditableFilterChip
|
||||
recordFilter={recordFilter}
|
||||
onRemove={handleRemove}
|
||||
onClick={handleFilterChipClick}
|
||||
/>
|
||||
recordFilter.type === 'RELATION' ? (
|
||||
<EditableRelationFilterChip
|
||||
recordFilter={recordFilter}
|
||||
onRemove={handleRemove}
|
||||
onClick={handleFilterChipClick}
|
||||
/>
|
||||
) : (
|
||||
<EditableFilterChip
|
||||
recordFilter={recordFilter}
|
||||
onRemove={handleRemove}
|
||||
onClick={handleFilterChipClick}
|
||||
/>
|
||||
)
|
||||
}
|
||||
dropdownComponents={
|
||||
<EditableFilterChipDropdownContent recordFilterId={recordFilter.id} />
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
import { useFieldMetadataItemByIdOrThrow } from '@/object-metadata/hooks/useFieldMetadataItemByIdOrThrow';
|
||||
import { type RecordFilter } from '@/object-record/record-filter/types/RecordFilter';
|
||||
import { SortOrFilterChip } from '@/views/components/SortOrFilterChip';
|
||||
import { useComputeRecordRelationFilterLabelValue } from '@/views/hooks/useComputeRecordRelationFilterLabelValue';
|
||||
import { useIcons } from 'twenty-ui/display';
|
||||
|
||||
type EditableRelationFilterChipProps = {
|
||||
recordFilter: RecordFilter;
|
||||
onRemove: () => void;
|
||||
onClick?: () => void;
|
||||
};
|
||||
|
||||
export const EditableRelationFilterChip = ({
|
||||
recordFilter,
|
||||
onRemove,
|
||||
onClick,
|
||||
}: EditableRelationFilterChipProps) => {
|
||||
const { getIcon } = useIcons();
|
||||
|
||||
const { fieldMetadataItem } = useFieldMetadataItemByIdOrThrow(
|
||||
recordFilter.fieldMetadataId,
|
||||
);
|
||||
|
||||
const FieldMetadataItemIcon = getIcon(fieldMetadataItem.icon);
|
||||
|
||||
const { labelValue } = useComputeRecordRelationFilterLabelValue({
|
||||
recordFilter,
|
||||
});
|
||||
|
||||
return (
|
||||
<SortOrFilterChip
|
||||
key={recordFilter.id}
|
||||
testId={recordFilter.id}
|
||||
labelKey={fieldMetadataItem.label}
|
||||
labelValue={labelValue}
|
||||
Icon={FieldMetadataItemIcon}
|
||||
onRemove={onRemove}
|
||||
onClick={onClick}
|
||||
type="filter"
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -95,6 +95,10 @@ const StyledSortValue = styled.span`
|
||||
font-weight: ${({ theme }) => theme.font.weight.medium};
|
||||
`;
|
||||
|
||||
const StyledKeyLabelContainer = styled.div`
|
||||
display: flex;
|
||||
`;
|
||||
|
||||
export type SortOrFilterChipVariant = 'default' | 'danger';
|
||||
|
||||
export type SortOrFilterChipType = 'sort' | 'filter';
|
||||
@@ -134,12 +138,14 @@ export const SortOrFilterChip = ({
|
||||
<Icon size={theme.icon.size.sm} />
|
||||
</StyledIcon>
|
||||
)}
|
||||
{labelKey && <StyledLabelKey>{labelKey}</StyledLabelKey>}
|
||||
{type === 'sort' ? (
|
||||
<StyledSortValue>{labelValue}</StyledSortValue>
|
||||
) : (
|
||||
<StyledFilterValue>{labelValue}</StyledFilterValue>
|
||||
)}
|
||||
<StyledKeyLabelContainer>
|
||||
{labelKey && <StyledLabelKey>{labelKey}</StyledLabelKey>}
|
||||
{type === 'sort' ? (
|
||||
<StyledSortValue>{labelValue}</StyledSortValue>
|
||||
) : (
|
||||
<StyledFilterValue>{labelValue}</StyledFilterValue>
|
||||
)}
|
||||
</StyledKeyLabelContainer>
|
||||
<StyledDelete
|
||||
variant={variant}
|
||||
onClick={handleDeleteClick}
|
||||
|
||||
+1
-1
@@ -151,7 +151,7 @@ export const usePersistViewFieldRecords = () => {
|
||||
mutation: CREATE_CORE_VIEW_FIELD,
|
||||
variables: {
|
||||
input: {
|
||||
id: viewField.id,
|
||||
id: v4(),
|
||||
fieldMetadataId: viewField.fieldMetadataId,
|
||||
viewId: view.id,
|
||||
isVisible: viewField.isVisible,
|
||||
|
||||
+91
@@ -0,0 +1,91 @@
|
||||
import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems';
|
||||
import { getRelationObjectMetadataNameSingular } from '@/object-metadata/utils/formatFieldMetadataItemsAsFilterDefinitions';
|
||||
import { getFieldMetadataItemByIdOrThrow } from '@/object-metadata/utils/getFieldMetadataItemByIdOrThrow';
|
||||
import { MAX_RECORDS_TO_DISPLAY } from '@/object-record/object-filter-dropdown/components/ObjectFilterDropdownRecordSelect';
|
||||
import { type RecordFilter } from '@/object-record/record-filter/types/RecordFilter';
|
||||
import { useRecordsForSelect } from '@/object-record/select/hooks/useRecordsForSelect';
|
||||
import { getRecordFilterLabelValue } from '@/views/utils/getRecordFilterLabelValue';
|
||||
import { arrayOfUuidOrVariableSchema } from '@/views/view-filter-value/validation-schemas/arrayOfUuidsOrVariablesSchema';
|
||||
import { jsonRelationFilterValueSchema } from '@/views/view-filter-value/validation-schemas/jsonRelationFilterValueSchema';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
type ObjectFilterDropdownRecordSelectProps = {
|
||||
recordFilter: RecordFilter;
|
||||
};
|
||||
|
||||
export const useComputeRecordRelationFilterLabelValue = ({
|
||||
recordFilter,
|
||||
}: ObjectFilterDropdownRecordSelectProps) => {
|
||||
const { objectMetadataItems } = useObjectMetadataItems();
|
||||
|
||||
if (!isDefined(recordFilter.fieldMetadataId)) {
|
||||
throw new Error('fieldMetadataItemUsedInFilterDropdown is not defined');
|
||||
}
|
||||
|
||||
const { fieldMetadataItem } = getFieldMetadataItemByIdOrThrow({
|
||||
fieldMetadataId: recordFilter.fieldMetadataId,
|
||||
objectMetadataItems,
|
||||
});
|
||||
|
||||
const relationObjectMetadataNameSingular =
|
||||
getRelationObjectMetadataNameSingular({
|
||||
field: fieldMetadataItem,
|
||||
});
|
||||
|
||||
if (!isDefined(relationObjectMetadataNameSingular)) {
|
||||
throw new Error('relationObjectMetadataNameSingular is not defined');
|
||||
}
|
||||
|
||||
const relationObjectMetadataItem = objectMetadataItems.find(
|
||||
(objectMetadataItem) =>
|
||||
objectMetadataItem.nameSingular === relationObjectMetadataNameSingular,
|
||||
);
|
||||
|
||||
if (!isDefined(relationObjectMetadataItem)) {
|
||||
throw new Error('relationObjectMetadataItem is not defined');
|
||||
}
|
||||
|
||||
const relationObjectLabelPlural = relationObjectMetadataItem.labelPlural;
|
||||
|
||||
const { isCurrentWorkspaceMemberSelected, selectedRecordIds } =
|
||||
jsonRelationFilterValueSchema
|
||||
.catch({
|
||||
isCurrentWorkspaceMemberSelected: false,
|
||||
selectedRecordIds: arrayOfUuidOrVariableSchema.parse(
|
||||
recordFilter.value,
|
||||
),
|
||||
})
|
||||
.parse(recordFilter.value);
|
||||
|
||||
const { selectedRecords, loading } = useRecordsForSelect({
|
||||
searchFilterText: '',
|
||||
selectedIds: selectedRecordIds,
|
||||
objectNameSingular: relationObjectMetadataNameSingular,
|
||||
limit: 10,
|
||||
});
|
||||
|
||||
if (loading) {
|
||||
return { labelValue: t`Loading...` };
|
||||
}
|
||||
|
||||
const labelValueItems = [
|
||||
...(isCurrentWorkspaceMemberSelected ? [t`Me`] : []),
|
||||
...selectedRecords.map((record) => record.name),
|
||||
];
|
||||
|
||||
const filterDisplayValue =
|
||||
labelValueItems.length > MAX_RECORDS_TO_DISPLAY
|
||||
? `${labelValueItems.length} ${relationObjectLabelPlural.toLowerCase()}`
|
||||
: labelValueItems.join(', ');
|
||||
|
||||
return {
|
||||
labelValue:
|
||||
labelValueItems.length > 0
|
||||
? getRecordFilterLabelValue({
|
||||
...recordFilter,
|
||||
displayValue: filterDisplayValue,
|
||||
})
|
||||
: getRecordFilterLabelValue(recordFilter),
|
||||
};
|
||||
};
|
||||
@@ -46,7 +46,6 @@
|
||||
},
|
||||
"files": [
|
||||
"dist",
|
||||
"assets",
|
||||
"accessibility",
|
||||
"assets",
|
||||
"components",
|
||||
|
||||
@@ -243,7 +243,7 @@ const computePackageJsonFilesAndExportsConfig = (
|
||||
return {
|
||||
exports,
|
||||
typesVersions: { '*': typesVersionsEntries },
|
||||
files: ['dist', 'assets', ...entrypoints],
|
||||
files: ['dist', ...entrypoints],
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user